基于 TCC 模式实现普通下单场景的分布式一致性方案
方案背景与适用场景
在分布式系统中,跨服务操作(如 “下单 + 扣库存”)的一致性保障是核心难题。TCC(Try-Confirm-Cancel)作为柔性事务的典型实现,通过 “预留资源 - 确认提交 - 取消回滚” 三阶段操作,能在不依赖强一致性中间件的前提下,实现分布式场景下的最终一致性。
本文分享的 TCC 方案,专门针对非秒杀、非热点商品的普通下单场景—— 该场景下流量相对平稳,无需应对高并发下的极端性能挑战,更侧重事务的可靠性与易用性,同时通过事务日志记录、幂等处理、异常补偿等设计,解决了 TCC 模式中常见的空回滚、悬挂、重复执行等问题。
核心设计与代码结构
方案整体基于 Java 语言开发,依赖 MyBatis-Plus 实现数据持久化、Dubbo 实现跨服务调用,核心分为 “通用 TCC 事务框架” 与 “下单业务实现” 两部分,结构清晰且可复用。
1.通用 TCC 事务框架
负责处理 TCC 三阶段的事务状态管理、幂等控制,是所有 TCC 业务的基础支撑,核心组件如下:
| 组件类型 | 核心类 / 接口 | 功能说明 |
|---|---|---|
| 配置类 | TccConfiguration |
扫描 TCC 相关 Mapper,注入 TransactionLogService(事务日志服务) |
| 实体类 | TransactionLog |
事务日志核心实体,记录事务 ID、业务场景、业务模块、事务状态、回滚类型,是状态判断的核心依据 |
| 枚举类 | TransActionLogState |
定义 TCC 三阶段状态:TRY(资源预留)、CONFIRM(确认提交)、CANCEL(取消回滚) |
| 枚举类 | TransTry/Cancel/ConfirmSuccessType |
定义各阶段的执行结果(如 TRY_SUCCESS首次成功、DUPLICATED_TRY幂等成功),解决重复调用问题 |
| 服务类 | TransactionLogService |
实现 TCC 三阶段核心逻辑:-tryTransaction:记录 TRY 状态日志,实现资源预留的幂等- confirmTransaction:将状态更新为 CONFIRM,确认提交- cancelTransaction:处理回滚(含空回滚、幂等回滚场景) |
2. 普通下单业务实现
基于通用 TCC 框架,实现 “创建订单 + 扣减库存” 的跨服务事务,核心流程与代码如下:
(1)下单核心流程
遵循 TCC 三阶段逻辑,同时增加异常补偿机制,确保事务最终一致:
- Try 阶段:调用库存服务冻结库存、订单服务创建待确认订单,通过
TransactionLog记录 TRY 状态; - Confirm 阶段:若 Try 全部成功,重试调用确认接口(最多 2 次),将库存从 “冻结” 转为 “扣减”、订单状态改为 “已确认”;
- Cancel 阶段:若 Try/Confirm 失败,发送补偿消息,触发库存解冻、订单作废,通过
TransactionLog避免重复回滚。
(2)关键业务代码
- 下单入口(TradeApplicationService):串联 TCC 三阶段,处理异常与补偿消息发送;
- 库存服务实现(GoodsTransactionFacadeServiceImpl):基于
TransactionLogService,实现库存的 “冻结(Try)、扣减(Confirm)、解冻(Cancel)”,并通过分布式锁避免并发问题; - 订单服务实现:类似库存服务,实现订单的 “创建待确认(Try)、确认(Confirm)、作废(Cancel)”。
方案核心亮点
- 解决 TCC 典型问题:
- 幂等性:通过
TransactionLog判断历史执行记录,避免重复 Try/Confirm/Cancel; - 空回滚:若 Cancel 先于 Try 执行,直接记录
EMPTY_CANCEL状态,防止无资源可回滚; - 悬挂问题:通过事务日志状态校验,避免 Try 在 Cancel 后执行导致的资源不一致。
- 幂等性:通过
- 高可靠性:
- Confirm 阶段增加重试机制(最多 2 次),应对服务临时不可用;
- 失败场景发送延迟补偿消息,通过监听机制触发二次校验,避免 “疑似失败” 场景。
- 易用性与可复用:
- 通用 TCC 框架与业务解耦,新增 TCC 事务时只需实现对应业务逻辑(如后续新增 “支付” 环节,无需修改框架);
- 通过
businessScene(业务场景)、businessModule(业务模块)字段,支持多场景复用。
config
/**
* @author mifuRD
*/
@Configuration
@MapperScan("cn.mifu.nft.turbo.tcc.mapper")
public class TccConfiguration {
@Bean
public TransactionLogService transactionLogService() {
return new TransactionLogService();
}
}
entity
/**
* @author mifuRD
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class TransactionLog extends BaseEntity {
/**
* 事务ID
*/
private String transactionId;
/**
* 业务场景
*/
private String businessScene;
/**
* 业务模块
*/
private String businessModule;
/**
* 状态
*/
private TransActionLogState state;
/**
* Cancel的类型
*/
private TransCancelSuccessType cancelType;
public TransactionLog(TccRequest tccRequest, TransActionLogState state) {
this.state = state;
this.transactionId = tccRequest.getTransactionId();
this.businessScene = tccRequest.getBusinessScene();
this.businessModule = tccRequest.getBusinessModule();
}
public TransactionLog(TccRequest tccRequest, TransActionLogState state, TransCancelSuccessType cancelType) {
this.state = state;
this.transactionId = tccRequest.getTransactionId();
this.businessScene = tccRequest.getBusinessScene();
this.businessModule = tccRequest.getBusinessModule();
this.cancelType = cancelType;
}
}
/**
* @author mifuRD
*/
public enum TransActionLogState {
/**
* Try
*/
TRY,
/**
* Confirm
*/
CONFIRM,
/**
* Cancel
*/
CANCEL
}
/**
* @author mifuRD
*/
public enum TransCancelSuccessType {
/**
* 回滚成功-TRY-CANCEL
*/
CANCEL_AFTER_TRY_SUCCESS,
/**
* 回滚成功-TRY-CONFIRM-CANCEL
*/
CANCEL_AFTER_CONFIRM_SUCCESS,
/**
* 空回滚
*/
EMPTY_CANCEL,
/**
* 幂等成功
*/
DUPLICATED_CANCEL;
}
/**
* @author mifuRD
*/
public enum TransConfirmSuccessType {
/**
* Confirm成功
*/
CONFIRM_SUCCESS,
/**
* 幂等成功
*/
DUPLICATED_CONFIRM;
}
/**
* @author mifuRD
*/
public enum TransTrySuccessType {
/**
* Try成功
*/
TRY_SUCCESS,
/**
* 幂等成功
*/
DUPLICATED_TRY;
}
mapper
/**
* @author mifuRD
* 事务日志
*/
@Mapper
public interface TransactionLogMapper extends BaseMapper<TransactionLog> {
}
request
/**
* @author mifuRD
*/
@AllArgsConstructor
@NoArgsConstructor
public class TccRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 事务ID
*/
private String transactionId;
/**
* 业务场景
*/
private String businessScene;
/**
* 业务模块
*/
private String businessModule;
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getBusinessScene() {
return businessScene;
}
public void setBusinessScene(String businessScene) {
this.businessScene = businessScene;
}
public String getBusinessModule() {
return businessModule;
}
public void setBusinessModule(String businessModule) {
this.businessModule = businessModule;
}
}
response
/**
* @author mifuRD
*/
@AllArgsConstructor
@NoArgsConstructor
public class TccRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 事务ID
*/
private String transactionId;
/**
* 业务场景
*/
private String businessScene;
/**
* 业务模块
*/
private String businessModule;
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
public String getBusinessScene() {
return businessScene;
}
public void setBusinessScene(String businessScene) {
this.businessScene = businessScene;
}
public String getBusinessModule() {
return businessModule;
}
public void setBusinessModule(String businessModule) {
this.businessModule = businessModule;
}
}
/**
* @author mifuRD
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class TransactionConfirmResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String errorCode;
private String errorMsg;
private TransConfirmSuccessType transConfirmSuccessType;
public TransactionConfirmResponse(Boolean success, TransConfirmSuccessType transConfirmSuccessType) {
this.success = success;
this.transConfirmSuccessType = transConfirmSuccessType;
}
public TransactionConfirmResponse(Boolean success, String errorCode, String errorMsg) {
this.success = success;
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
}
/**
* @author mifuRD
*/
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class TransactionTryResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean success;
private String errorCode;
private String errorMsg;
private TransTrySuccessType transTrySuccessType;
public TransactionTryResponse(Boolean success, TransTrySuccessType transTrySuccessType) {
this.success = success;
this.transTrySuccessType = transTrySuccessType;
}
public TransactionTryResponse(Boolean success, String errorCode, String errorMsg) {
this.success = success;
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
}
自动装配
在META-INF下创建文件,SpringBoot3版本是org.springframework.boot.autoconfigure.AutoConfiguration.imports,2.x版本我记得是spring.factory,至于为什么要改成名字这么长的文件,我记得好像和云原生相关
cn.mifu.nft.turbo.tcc.config.TccConfiguration
service
/**
* @author mifuRD
*/
public class TransactionLogService extends ServiceImpl<TransactionLogMapper, TransactionLog> {
/**
* TCC事务的Try
*
* @param tccRequest
* @return
*/
public TransactionTryResponse tryTransaction(TccRequest tccRequest) {
TransactionLog existTransactionLog = getExistTransLog(tccRequest);
if (existTransactionLog == null) {
TransactionLog transactionLog = new TransactionLog(tccRequest, TransActionLogState.TRY);
if (this.save(transactionLog)) {
return new TransactionTryResponse(true, TransTrySuccessType.TRY_SUCCESS);
}
return new TransactionTryResponse(false, "TRY_FAILED", "TRY_FAILED");
}
//幂等
return new TransactionTryResponse(true, TransTrySuccessType.DUPLICATED_TRY);
}
/**
* TCC事务的Confirm
*
* @param tccRequest
* @return
*/
public TransactionConfirmResponse confirmTransaction(TccRequest tccRequest) {
TransactionLog existTransactionLog = getExistTransLog(tccRequest);
if (existTransactionLog == null) {
throw new UnsupportedOperationException("transacton can not confirm");
}
if (existTransactionLog.getState() == TransActionLogState.TRY) {
existTransactionLog.setState(TransActionLogState.CONFIRM);
if (this.updateById(existTransactionLog)) {
return new TransactionConfirmResponse(true, TransConfirmSuccessType.CONFIRM_SUCCESS);
}
return new TransactionConfirmResponse(false, "CONFIRM_FAILED", "CONFIRM_FAILED");
}
//幂等
if (existTransactionLog.getState() == TransActionLogState.CONFIRM) {
return new TransactionConfirmResponse(true, TransConfirmSuccessType.DUPLICATED_CONFIRM);
}
throw new UnsupportedOperationException("transacton can not confirm :" + existTransactionLog.getState());
}
/**
* TCC事务的Cancel
*
* @param tccRequest
* @return
*/
public TransactionCancelResponse cancelTransaction(TccRequest tccRequest) {
TransactionLog existTransactionLog = getExistTransLog(tccRequest);
//如果还没有Try,则直接记录一条状态为Cancel的数据,避免发生空回滚,并解决悬挂问题
if (existTransactionLog == null) {
TransactionLog transactionLog = new TransactionLog(tccRequest, TransActionLogState.CANCEL, TransCancelSuccessType.EMPTY_CANCEL);
if (this.save(transactionLog)) {
return new TransactionCancelResponse(true, TransCancelSuccessType.EMPTY_CANCEL);
}
return new TransactionCancelResponse(false, "EMPTY_CANCEL_FAILED", "EMPTY_CANCEL_FAILED");
}
if (existTransactionLog.getState() == TransActionLogState.TRY) {
existTransactionLog.setState(TransActionLogState.CANCEL);
existTransactionLog.setCancelType(TransCancelSuccessType.CANCEL_AFTER_TRY_SUCCESS);
if (this.updateById(existTransactionLog)) {
return new TransactionCancelResponse(true, TransCancelSuccessType.CANCEL_AFTER_TRY_SUCCESS);
}
return new TransactionCancelResponse(false, "CANCEL_FAILED", "CANCEL_FAILED");
}
if (existTransactionLog.getState() == TransActionLogState.CONFIRM) {
existTransactionLog.setState(TransActionLogState.CANCEL);
existTransactionLog.setCancelType(TransCancelSuccessType.CANCEL_AFTER_CONFIRM_SUCCESS);
if (this.updateById(existTransactionLog)) {
return new TransactionCancelResponse(true, TransCancelSuccessType.CANCEL_AFTER_CONFIRM_SUCCESS);
}
return new TransactionCancelResponse(false, "CANCEL_FAILED", "CANCEL_FAILED");
}
//幂等
if (existTransactionLog.getState() == TransActionLogState.CANCEL) {
return new TransactionCancelResponse(true, TransCancelSuccessType.DUPLICATED_CANCEL);
}
return new TransactionCancelResponse(false, "CANCEL_FAILED", "CANCEL_FAILED");
}
private TransactionLog getExistTransLog(TccRequest request) {
QueryWrapper<TransactionLog> queryWrapper = new QueryWrapper<TransactionLog>();
queryWrapper.eq("transaction_id", request.getTransactionId());
queryWrapper.eq("business_scene", request.getBusinessScene());
queryWrapper.eq("business_module", request.getBusinessModule());
return this.getOne(queryWrapper);
}
}
场景
controller
/**
* 普通下单,非热点商品,非秒杀场景
*
* @param
* @return 订单号
*/
@PostMapping("/normalBuy")
public Result<String> normalBuy(@Valid @RequestBody BuyParam buyParam) {
OrderCreateAndConfirmRequest orderCreateAndConfirmRequest = getOrderCreateAndConfirmRequest(buyParam);
OrderResponse orderResponse = RemoteCallWrapper.call(req -> tradeApplicationService.normalBuy(req), orderCreateAndConfirmRequest, "createOrder");
if (orderResponse.getSuccess()) {
//同步写redis,如果失败,不阻塞流程,靠binlog同步保障
try {
InventoryRequest inventoryRequest = new InventoryRequest(orderCreateAndConfirmRequest);
inventoryFacadeService.decrease(inventoryRequest);
} catch (Exception e) {
log.error("decrease inventory from redis failed", e);
}
return Result.success(orderCreateAndConfirmRequest.getOrderId());
}
throw new TradeException(TradeErrorCode.ORDER_CREATE_FAILED);
}
库存tcc
/**
* @author mifuRD
*/
@Service
@Slf4j
public class TradeApplicationService {
private static final int MAX_RETRY_TIMES = 2;
@Autowired
private OrderTransactionFacadeService orderTransactionFacadeService;
@Autowired
private GoodsTransactionFacadeService goodsTransactionFacadeService;
@Autowired
private StreamProducer streamProducer;
/**
* 普通交易,基于TCC实现分布式一致性
* <p>
* Try -> Confirm :Try成功,执行Confirm
* Try --> Cancel : Try失败,执行Cancel
* Try -> Confirm --> Cancel :Try成功,Confirm失败,执行Cancel
*
* @param orderCreateRequest
* @returnc
*/
public OrderResponse normalBuy(OrderCreateAndConfirmRequest orderCreateRequest) {
boolean isTrySuccess = true;
//Try
try {
GoodsSaleRequest goodsSaleRequest = new GoodsSaleRequest(orderCreateRequest);
boolean result = goodsTransactionFacadeService.tryDecreaseInventory(goodsSaleRequest).getSuccess();
Assert.isTrue(result, "decrease inventory failed");
result = orderTransactionFacadeService.tryOrder(orderCreateRequest).getSuccess();
Assert.isTrue(result, "order create failed");
} catch (Exception e) {
isTrySuccess = false;
log.error("normalBuy try failed, ", e);
}
//Try失败,发【废单消息】,异步进行逆向补偿
if (!isTrySuccess) {
//消息监听: NormalBuyMsgListener
streamProducer.send("normalBuyCancel-out-0", orderCreateRequest.getGoodsType().name(), JSON.toJSONString(orderCreateRequest));
return new OrderResponse.OrderResponseBuilder().buildFail(NORMAL_BUY_TCC_CANCEL_FAILED.getCode(), NORMAL_BUY_TCC_CANCEL_FAILED.getMessage());
}
//Confirm
boolean isConfirmSuccess = false;
int retryConfirmCount = 0;
//最大努力执行,失败最多尝试2次.(Dubbo也会有重试机制,在服务突然不可用、超时等情况下会重试2次)
while (!isConfirmSuccess && retryConfirmCount < MAX_RETRY_TIMES) {
try {
GoodsSaleRequest goodsSaleRequest = new GoodsSaleRequest(orderCreateRequest);
isConfirmSuccess = goodsTransactionFacadeService.confirmDecreaseInventory(goodsSaleRequest).getSuccess();
Assert.isTrue(isConfirmSuccess, "confirmDecreaseInventory failed");
OrderConfirmRequest orderConfirmRequest = new OrderConfirmRequest();
BeanUtils.copyProperties(orderCreateRequest, orderConfirmRequest);
isConfirmSuccess = orderTransactionFacadeService.confirmOrder(orderConfirmRequest).getSuccess();
Assert.isTrue(isConfirmSuccess, "confirmOrder failed");
} catch (Exception e) {
retryConfirmCount++;
isConfirmSuccess = false;
log.error("normalBuy confirm failed, ", e);
}
}
//Confirm失败,发【疑似废单消息】进行延迟检查
if (!isConfirmSuccess) {
//消息监听: NormalBuyMsgListener
streamProducer.send("normalBuyPreCancel-out-0", orderCreateRequest.getGoodsType().name(), JSON.toJSONString(orderCreateRequest), DELAY_LEVEL_1_M);
return new OrderResponse.OrderResponseBuilder().buildFail(NORMAL_BUY_TCC_CONFIRM_FAILED.getCode(), NORMAL_BUY_TCC_CONFIRM_FAILED.getMessage());
}
return new OrderResponse.OrderResponseBuilder().orderId(orderCreateRequest.getOrderId()).buildSuccess();
}
}
库存扣减这块涉及一些核心功能,暂不公开展示(还有一套TCC的校验,不过TCC这个方案目前被废弃了,可以先看看TCC这套)
/**
* @author mifuRD
*/
public interface GoodsTransactionFacadeService {
/**
* 锁定库存
* @param request
* @return
*/
public GoodsSaleResponse tryDecreaseInventory(GoodsSaleRequest request);
/**
* 解锁并扣减库存
* @param request
* @return
*/
public GoodsSaleResponse confirmDecreaseInventory(GoodsSaleRequest request);
/**
* 解锁库存
* @param request
* @return
*/
public GoodsSaleResponse cancelDecreaseInventory(GoodsSaleRequest request);
}
/**
* @author mifuRD
*/
@DubboService(version = "1.0.0")
public class GoodsTransactionFacadeServiceImpl implements GoodsTransactionFacadeService {
private static final String ERROR_CODE_UNSUPPORTED_GOODS_TYPE = "UNSUPPORTED_GOODS_TYPE";
@Autowired
private CollectionService collectionService;
@Autowired
private BlindBoxService blindBoxService;
@Autowired
private TransactionLogService transactionLogService;
@Override
@Facade
@Transactional(rollbackFor = Exception.class)
@DistributeLock(keyExpression = "#request.bizNo",scene = "NORMAL_BUY_GOODS")
public GoodsSaleResponse tryDecreaseInventory(GoodsSaleRequest request) {
GoodsFreezeInventoryRequest goodsTrySaleRequest = new GoodsFreezeInventoryRequest(request.getBizNo(), request.getGoodsId(), request.getQuantity());
GoodsType goodsType = GoodsType.valueOf(request.getGoodsType());
TransactionTryResponse transactionTryResponse = transactionLogService.tryTransaction(new TccRequest(request.getBizNo(), "normalBuy", goodsType.name()));
Assert.isTrue(transactionTryResponse.getSuccess(), "transaction try failed");
if (transactionTryResponse.getTransTrySuccessType() == TransTrySuccessType.TRY_SUCCESS) {
Boolean freezeResult = switch (goodsType) {
case BLIND_BOX -> blindBoxService.freezeInventory(goodsTrySaleRequest);
case COLLECTION -> collectionService.freezeInventory(goodsTrySaleRequest);
default -> throw new UnsupportedOperationException(ERROR_CODE_UNSUPPORTED_GOODS_TYPE);
};
Assert.isTrue(freezeResult, "freeze inventory failed");
GoodsSaleResponse response = new GoodsSaleResponse();
response.setSuccess(true);
return response;
}
return new GoodsSaleResponse.GoodsResponseBuilder().buildSuccess();
}
@Override
@Facade
@Transactional(rollbackFor = Exception.class)
@DistributeLock(keyExpression = "#request.bizNo",scene = "NORMAL_BUY_GOODS")
public GoodsSaleResponse confirmDecreaseInventory(GoodsSaleRequest request) {
GoodsUnfreezeAndSaleRequest unfreezeAndSaleRequest = new GoodsUnfreezeAndSaleRequest(request.getBizNo(), request.getGoodsId(), request.getQuantity());
GoodsType goodsType = GoodsType.valueOf(request.getGoodsType());
TransactionConfirmResponse transactionConfirmResponse = transactionLogService.confirmTransaction(new TccRequest(request.getBizNo(), "normalBuy", goodsType.name()));
Assert.isTrue(transactionConfirmResponse.getSuccess(), "transaction confirm failed");
if (transactionConfirmResponse.getTransConfirmSuccessType() == TransConfirmSuccessType.CONFIRM_SUCCESS) {
Boolean unfreezeResult = switch (goodsType) {
case BLIND_BOX -> blindBoxService.unfreezeAndSale(unfreezeAndSaleRequest);
case COLLECTION -> collectionService.unfreezeAndSale(unfreezeAndSaleRequest);
default -> throw new UnsupportedOperationException(ERROR_CODE_UNSUPPORTED_GOODS_TYPE);
};
Assert.isTrue(unfreezeResult, "unfreeze inventory failed");
GoodsSaleResponse response = new GoodsSaleResponse();
response.setSuccess(true);
return response;
}
return new GoodsSaleResponse.GoodsResponseBuilder().buildSuccess();
}
@Override
@Facade
@Transactional(rollbackFor = Exception.class)
@DistributeLock(keyExpression = "#request.bizNo",scene = "NORMAL_BUY_GOODS")
public GoodsSaleResponse cancelDecreaseInventory(GoodsSaleRequest request) {
GoodsType goodsType = GoodsType.valueOf(request.getGoodsType());
TransactionCancelResponse transactionCancelResponse = transactionLogService.cancelTransaction(new TccRequest(request.getBizNo(), "normalBuy", goodsType.name()));
Assert.isTrue(transactionCancelResponse.getSuccess(), "transaction cancel failed");
//如果发生空回滚,或者回滚幂等,则不进行解冻库存操作
//Try成功后的Cancel,直接解冻库存
if (transactionCancelResponse.getTransCancelSuccessType() == TransCancelSuccessType.CANCEL_AFTER_TRY_SUCCESS) {
GoodsUnfreezeInventoryRequest unfreezeInventoryRequest = new GoodsUnfreezeInventoryRequest(request.getBizNo(), request.getGoodsId(), request.getQuantity());
Boolean unfreezeResult = switch (goodsType) {
case BLIND_BOX -> blindBoxService.unfreezeInventory(unfreezeInventoryRequest);
case COLLECTION -> collectionService.unfreezeInventory(unfreezeInventoryRequest);
default -> throw new UnsupportedOperationException(ERROR_CODE_UNSUPPORTED_GOODS_TYPE);
};
Assert.isTrue(unfreezeResult, "unfreeze inventory failed");
}
//如果发生空回滚,或者回滚幂等,则不进行解冻库存操作
//Confirm成功后的Cancel,直接回滚库存
if (transactionCancelResponse.getTransCancelSuccessType() == TransCancelSuccessType.CANCEL_AFTER_CONFIRM_SUCCESS) {
GoodsCancelSaleRequest goodsCancelSaleRequest = new GoodsCancelSaleRequest(request.getBizNo(), request.getGoodsId(), request.getQuantity());
Boolean cancelResult = switch (goodsType) {
case BLIND_BOX -> blindBoxService.cancel(goodsCancelSaleRequest);
case COLLECTION -> collectionService.cancel(goodsCancelSaleRequest);
default -> throw new UnsupportedOperationException(ERROR_CODE_UNSUPPORTED_GOODS_TYPE);
};
Assert.isTrue(cancelResult, "cancel inventory failed");
}
GoodsSaleResponse response = new GoodsSaleResponse();
response.setSuccess(true);
return response;
}
}
订单tcc
/**
* 订单事务门面服务
*
* @author mifuRD
*/
public interface OrderTransactionFacadeService {
/**
* 创建订单
*
* @param orderCreateRequest
* @return
*/
public OrderResponse tryOrder(OrderCreateRequest orderCreateRequest);
/**
* 确认订单
*
* @param orderConfirmRequest
* @return
*/
public OrderResponse confirmOrder(OrderConfirmRequest orderConfirmRequest);
/**
* 撤销订单
*
* @param orderDiscardRequest
* @return
*/
public OrderResponse cancelOrder(OrderDiscardRequest orderDiscardRequest);
}
/**
* @author mifuRD
*/
@DubboService(version = "1.0.0")
public class OrderTransactionFacadeServiceImpl implements OrderTransactionFacadeService {
@Autowired
private OrderManageService orderManageService;
@Autowired
private TransactionLogService transactionLogService;
@Override
@Transactional(rollbackFor = Exception.class)
@Facade
@DistributeLock(keyExpression = "#orderCreateRequest.orderId",scene = "NORMAL_BUY_ORDER")
public OrderResponse tryOrder(OrderCreateRequest orderCreateRequest) {
TransactionTryResponse transactionTryResponse = transactionLogService.tryTransaction(new TccRequest(orderCreateRequest.getOrderId(), "normalBuy", "ORDER"));
Assert.isTrue(transactionTryResponse.getSuccess(), "transaction try failed");
if (transactionTryResponse.getTransTrySuccessType() == TransTrySuccessType.TRY_SUCCESS) {
OrderResponse orderResponse = orderManageService.create(orderCreateRequest);
Assert.isTrue(orderResponse.getSuccess(), () -> new BizException(OrderErrorCode.CREATE_ORDER_FAILED));
return orderResponse;
}
return new OrderResponse.OrderResponseBuilder().buildSuccess();
}
@Override
@Transactional(rollbackFor = Exception.class)
@Facade
@DistributeLock(keyExpression = "#orderConfirmRequest.orderId",scene = "NORMAL_BUY_ORDER")
public OrderResponse confirmOrder(OrderConfirmRequest orderConfirmRequest) {
TransactionConfirmResponse transactionConfirmResponse = transactionLogService.confirmTransaction(new TccRequest(orderConfirmRequest.getOrderId(), "normalBuy", "ORDER"));
Assert.isTrue(transactionConfirmResponse.getSuccess(), "transaction confirm failed");
if(transactionConfirmResponse.getTransConfirmSuccessType() == TransConfirmSuccessType.CONFIRM_SUCCESS){
OrderResponse orderResponse = orderManageService.confirm(orderConfirmRequest);
Assert.isTrue(orderResponse.getSuccess(), () -> new BizException(OrderErrorCode.CREATE_ORDER_FAILED));
return orderResponse;
}
return new OrderResponse.OrderResponseBuilder().buildSuccess();
}
@Override
@Transactional(rollbackFor = Exception.class)
@Facade
@DistributeLock(keyExpression = "#orderDiscardRequest.orderId",scene = "NORMAL_BUY_ORDER")
public OrderResponse cancelOrder(OrderDiscardRequest orderDiscardRequest) {
TransactionCancelResponse transactionCancelResponse = transactionLogService.cancelTransaction(new TccRequest(orderDiscardRequest.getOrderId(), "normalBuy", "ORDER"));
Assert.isTrue(transactionCancelResponse.getSuccess(), "transaction cancel failed");
//如果发生空回滚,或者回滚幂等,则不进行废弃订单操作
if (transactionCancelResponse.getTransCancelSuccessType() == TransCancelSuccessType.CANCEL_AFTER_TRY_SUCCESS
|| transactionCancelResponse.getTransCancelSuccessType() == TransCancelSuccessType.CANCEL_AFTER_CONFIRM_SUCCESS) {
OrderResponse orderResponse = orderManageService.discard(orderDiscardRequest);
Assert.isTrue(orderResponse.getSuccess(), () -> new BizException(OrderErrorCode.UPDATE_ORDER_FAILED));
return orderResponse;
}
return new OrderResponse.OrderResponseBuilder().buildSuccess();
}
}
总结
本方案通过 TCC 模式,在非高并发的普通下单场景下,以较低的实现成本实现了跨服务事务的最终一致性。核心优势在于 “轻量级”(无需依赖 Seata 等中间件)、“高可靠”(解决 TCC 典型问题 + 异常补偿)、“可复用”(框架与业务解耦)。不过这套方案的不少逻辑都还需完善,最终没有上到生产系统,生产采用的是秒杀场景的那套,不过对于学习TCC来说本质是一样的。