Seat分布式事务 Tcc模式demo

本文只是测试用例,模拟订单测试场景,没有考虑高并发情况。

1.订单控制器
import cn.itcast.order.entity.Order;
import cn.itcast.order.service.OrderService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("order")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping
    public ResponseEntity<Long> createOrder(Order order){
        Long orderId = orderService.create(order);
        return ResponseEntity.status(HttpStatus.CREATED).body(orderId);
    }
}

2.订单服务类

主事物开启,先开单在用户支付,发货扣减库存

@Override
    @GlobalTransactional
    public Long create(Order order) {
        // 创建订单
        orderMapper.insert(order);
        try {
            // 扣用户余额
            accountTCCService.deduct(order.getUserId(), order.getMoney());
            // 扣库存
            storageClient.deduct(order.getCommodityCode(), order.getCount());

        } catch (FeignException e) {
            log.error("下单失败,原因:{}", e.contentUTF8(), e);
            throw new RuntimeException(e.contentUTF8(), e);
        }
        return order.getId();
    }

余额子事务

AccountTCCService 类

/**
 * Tcc业务处理类
 */
@LocalTCC
public interface AccountTCCService {
    /**
     * 从用户账户中扣款
     * Try接口
     *   name: 指定try接口
     *   commitMethod: confirm接口
     *   rollbackMethod:cancel接口
     */
    @TwoPhaseBusinessAction(name = "deduct",commitMethod = "confirm",rollbackMethod = "cancel")
    void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,@BusinessActionContextParameter(paramName = "money") int money);

    /**
     * Confirm接口
     */
    public boolean confirm(BusinessActionContext cxt);

    /**
     * Cancel接口
     */
    public boolean cancel(BusinessActionContext cxt);


业务具体实现

1.避免业务悬挂的问题,查询冻结金额表,是否存在记录,存在记录,且状态CANCEL,则证明执行过cancel,不执行try,没有问题记录冻结金额表以及事务状态

2.cancel 判断该事务是否已经执行过try方法 ,没有执行过try,执行空回滚,新增冻结金额数据,金额为0,状态为CANCEL

@Service
public class AccountTCCServiceImpl implements AccountTCCService {
    @Autowired
    private AccountMapper accountMapper;
    @Autowired
    private AccountFreezeMapper freezeMapper;

    @Override
    @Transactional
    public void deduct(String userId, int money) {
        //获取seata内部产生的全局事务ID
        String xid = RootContext.getXID();

        //避免业务悬挂的问题
        //查询冻结金额表,是否存在记录,存在记录,且状态CANCEL,则证明执行过cancel,不执行try
        AccountFreeze oldFreeze = freezeMapper.selectById(xid);
        if(oldFreeze!=null && oldFreeze.getState().equals(AccountFreeze.State.CANCEL)){
            return;
        }

        //扣减account表可用余额
        accountMapper.deduct(userId,money);

        //记录冻结金额表以及事务状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setXid(xid);
        freeze.setUserId(userId);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freezeMapper.insert(freeze);
    }

    @Override
    public boolean confirm(BusinessActionContext cxt) {
        int count = freezeMapper.deleteById(cxt.getXid());
        return count==1;
    }

    @Override
    public boolean cancel(BusinessActionContext cxt) {
        //先查询account_freeze表记录
        AccountFreeze freeze = freezeMapper.selectById(cxt.getXid());

        //从BusinessActionContext取出之前存入的参数
        String userId = (String)cxt.getActionContext("userId");
        Integer money = (Integer)cxt.getActionContext("money");

        //判断该事务是否已经执行过try方法
        if(freeze==null){
            //没有执行过try,执行空回滚
            //新增冻结金额数据,金额为0,状态为CANCEL
            freeze = new AccountFreeze();
            freeze.setXid(cxt.getXid());
            freeze.setUserId(userId);
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.CANCEL);
            freezeMapper.insert(freeze);
            return true;
        }

        //修改account表,恢复可用金额
        accountMapper.refund(userId,money);

        //修改account_freeze表,冻结金额设置为0,stata为CANCEL
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count==1;
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值