seata.data-source-proxy-mode=tcc
package com.i9i.seataaccount.service;
import com.i9i.commons.req.AccountReq;
import com.i9i.commons.resp.AccountResp;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
@LocalTCC
public interface AccountTCCService {
@TwoPhaseBusinessAction(name = "reduce", commitMethod = "commit", rollbackMethod = "rollBack")
AccountResp reduce(@BusinessActionContextParameter("req") AccountReq req);
boolean commit(BusinessActionContext cxt);
boolean rollBack(BusinessActionContext cxt);
}
package com.i9i.seataaccount.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.i9i.commons.req.AccountReq;
import com.i9i.commons.resp.AccountResp;
import com.i9i.seataaccount.dao.AccountDao;
import com.i9i.seataaccount.dao.AccountTccRecordDao;
import com.i9i.seataaccount.enums.TccState;
import com.i9i.seataaccount.model.AccountTccRecord;
import com.i9i.seataaccount.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class AccountTCCServiceImpl implements AccountTCCService{
@Autowired
private AccountDao accountDao;
@Autowired
private AccountTccRecordDao accountTccRecordDao;
@Transactional
@Override
public AccountResp reduce(AccountReq req) {
//0 获取全局事务id xid
String xid=RootContext.getXID();
// 1\ 检查是否已经执行过,如果执行过直接返回,保持幂等性,如果没有执行执行23进行资源锁定
AccountTccRecord records=accountTccRecordDao.selectById(xid);
if(records!=null) {//业务悬挂判断
log.info(xid+" has executed");
return new AccountResp();
}
// 2\ 进行资源锁定隔离
accountDao.reduce(req);
// 3\增加一条锁定记录,状态为try
records=new AccountTccRecord();
records.setXid(xid);
records.setUserId(req.getUserId());
records.setMoney(req.getTotalMoney());
records.setState(TccState.TRY.ordinal());
accountTccRecordDao.insert(records);
return null;
}
@Override
public boolean commit(BusinessActionContext cxt) {
String xid=cxt.getXid();
//执行删除
int updates=accountTccRecordDao.deleteById(xid);
return updates==1;
}
@Override
public boolean rollBack(BusinessActionContext cxt) {
//1\ 检查是否有记录,判断是否执行了try,如果没有执行增加一条锁定记录状态为rollback
String xid=cxt.getXid();
AccountTccRecord records=accountTccRecordDao.selectById(xid);
// 2 如果try阶段没有执行,则有可能不存在 判断是否存在,如果不存在需要记录一条记录状态为rollback
if(records==null) {//空回滚
AccountReq req=cxt.getActionContext("req", AccountReq.class);
records=new AccountTccRecord();
records.setXid(xid);
records.setUserId(req.getUserId());
records.setMoney(0d);
records.setState(TccState.ROLLBACK.ordinal());
accountTccRecordDao.insert(records);
return true;
}
//幂等判断
if(records.getState()==TccState.ROLLBACK.ordinal()) {
return true;
}
// 3\恢复账户余额
AccountReq req=cxt.getActionContext("req", AccountReq.class);
accountDao.refund(req);
// 4\设置 冻结 金额为 rollback
records.setMoney(0d);
records.setState(TccState.ROLLBACK.ordinal());
int updates=accountTccRecordDao.updateById(records);
return updates==1;
}
}