erp-java/seata/example/FinanceService.java

178 lines
6.3 KiB
Java
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.erp.example.seata;
import com.erp.example.seata.entity.Account;
import com.erp.example.seata.entity.TransactionLog;
import com.erp.example.seata.mapper.AccountMapper;
import com.erp.example.seata.mapper.TransactionLogMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
/**
* 财务服务 - AT模式实现
*/
@Service
public class FinanceService {
private static final Logger log = LoggerFactory.getLogger(FinanceService.class);
@Autowired
private AccountMapper accountMapper;
@Autowired
private TransactionLogMapper transactionLogMapper;
/**
* 冻结资金
* AT模式自动记录前后镜像异常时自动回滚
*/
@Transactional
public void freezeAmount(Long customerId, Long amount, String orderNo) {
log.info("冻结资金: 客户ID={}, 金额={}, 订单={}", customerId, amount, orderNo);
Account account = accountMapper.findByCustomerId(customerId);
if (account == null) {
throw new RuntimeException("账户不存在: " + customerId);
}
BigDecimal availableAmount = account.getBalance().subtract(account.getFrozenAmount());
BigDecimal freezeAmount = BigDecimal.valueOf(amount);
if (availableAmount.compareTo(freezeAmount) < 0) {
throw new RuntimeException("余额不足: 可用=" + availableAmount + ", 需要=" + freezeAmount);
}
// 冻结资金 = 增加冻结金额
account.setFrozenAmount(account.getFrozenAmount().add(freezeAmount));
accountMapper.update(account);
// 记录交易流水
TransactionLog txLog = new TransactionLog();
txLog.setTxNo("TX" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8));
txLog.setOrderNo(orderNo);
txLog.setCustomerId(customerId);
txLog.setAmount(freezeAmount);
txLog.setType("FREEZE");
txLog.setStatus("SUCCESS");
txLog.setCreateTime(LocalDateTime.now());
transactionLogMapper.insert(txLog);
log.info("资金冻结成功: {}", account);
}
/**
* 解冻资金(取消冻结)
* 用于订单取消时回滚
*/
@Transactional
public void unfreezeAmount(Long customerId, Long amount, String orderNo) {
log.info("解冻资金: 客户ID={}, 金额={}, 订单={}", customerId, amount, orderNo);
Account account = accountMapper.findByCustomerId(customerId);
if (account == null) {
log.warn("账户不存在: {}", customerId);
return;
}
BigDecimal unfreezeAmount = BigDecimal.valueOf(amount);
BigDecimal actualUnfreeze = unfreezeAmount.min(account.getFrozenAmount());
// 解冻资金 = 减少冻结金额
account.setFrozenAmount(account.getFrozenAmount().subtract(actualUnfreeze));
accountMapper.update(account);
// 记录交易流水
TransactionLog txLog = new TransactionLog();
txLog.setTxNo("TX" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8));
txLog.setOrderNo(orderNo);
txLog.setCustomerId(customerId);
txLog.setAmount(actualUnfreeze);
txLog.setType("UNFREEZE");
txLog.setStatus("SUCCESS");
txLog.setCreateTime(LocalDateTime.now());
transactionLogMapper.insert(txLog);
log.info("资金解冻成功: {}", account);
}
/**
* 消费资金(正式扣减)
* 订单支付成功后调用
*/
@Transactional
public void consumeAmount(Long customerId, Long amount, String orderNo) {
log.info("消费资金: 客户ID={}, 金额={}, 订单={}", customerId, amount, orderNo);
Account account = accountMapper.findByCustomerId(customerId);
if (account == null) {
throw new RuntimeException("账户不存在: " + customerId);
}
BigDecimal consumeAmount = BigDecimal.valueOf(amount);
// 消费资金 = 减少余额和冻结金额
account.setBalance(account.getBalance().subtract(consumeAmount));
account.setFrozenAmount(account.getFrozenAmount().subtract(consumeAmount));
accountMapper.update(account);
// 记录交易流水
TransactionLog txLog = new TransactionLog();
txLog.setTxNo("TX" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8));
txLog.setOrderNo(orderNo);
txLog.setCustomerId(customerId);
txLog.setAmount(consumeAmount);
txLog.setType("CONSUME");
txLog.setStatus("SUCCESS");
txLog.setCreateTime(LocalDateTime.now());
transactionLogMapper.insert(txLog);
log.info("资金消费成功: {}", account);
}
/**
* 退款
* 订单取消或退款时调用
*/
@Transactional
public void refundAmount(Long customerId, Long amount, String orderNo) {
log.info("退款: 客户ID={}, 金额={}, 订单={}", customerId, amount, orderNo);
Account account = accountMapper.findByCustomerId(customerId);
if (account == null) {
throw new RuntimeException("账户不存在: " + customerId);
}
BigDecimal refundAmount = BigDecimal.valueOf(amount);
// 退款 = 增加余额
account.setBalance(account.getBalance().add(refundAmount));
accountMapper.update(account);
// 记录交易流水
TransactionLog txLog = new TransactionLog();
txLog.setTxNo("TX" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8));
txLog.setOrderNo(orderNo);
txLog.setCustomerId(customerId);
txLog.setAmount(refundAmount);
txLog.setType("REFUND");
txLog.setStatus("SUCCESS");
txLog.setCreateTime(LocalDateTime.now());
transactionLogMapper.insert(txLog);
log.info("退款成功: {}", account);
}
/**
* 查询账户
*/
public Account findByCustomerId(Long customerId) {
return accountMapper.findByCustomerId(customerId);
}
}