erp-java/seata/example/InventoryService.java

137 lines
4.8 KiB
Java
Raw 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.Inventory;
import com.erp.example.seata.entity.InventoryLog;
import com.erp.example.seata.mapper.InventoryLogMapper;
import com.erp.example.seata.mapper.InventoryMapper;
import io.seata.rm.tcc.api.BusinessActionContext;
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.time.LocalDateTime;
/**
* 库存服务 - AT模式实现
*/
@Service
public class InventoryService {
private static final Logger log = LoggerFactory.getLogger(InventoryService.class);
@Autowired
private InventoryMapper inventoryMapper;
@Autowired
private InventoryLogMapper inventoryLogMapper;
/**
* 预留库存(冻结库存)
* AT模式自动记录前后镜像异常时自动回滚
*/
@Transactional
public void reserveStock(Long productId, Integer quantity, String orderNo) {
log.info("预留库存: 商品ID={}, 数量={}, 订单={}", productId, quantity, orderNo);
Inventory inventory = inventoryMapper.findByProductId(productId);
if (inventory == null) {
throw new RuntimeException("商品不存在: " + productId);
}
Integer availableStock = inventory.getStock() - inventory.getReservedStock();
if (availableStock < quantity) {
throw new RuntimeException("库存不足: 可用=" + availableStock + ", 需要=" + quantity);
}
// 冻结库存 = 增加预留数量
inventory.setReservedStock(inventory.getReservedStock() + quantity);
inventoryMapper.update(inventory);
// 记录库存变动日志
InventoryLog log = new InventoryLog();
log.setProductId(productId);
log.setOrderNo(orderNo);
log.setChangeType("RESERVE");
log.setQuantity(quantity);
log.setBeforeStock(inventory.getStock());
log.setAfterStock(inventory.getStock());
log.setCreateTime(LocalDateTime.now());
inventoryLogMapper.insert(log);
log.info("库存预留成功: {}", inventory);
}
/**
* 释放库存(取消预留)
* 用于订单取消时回滚
*/
@Transactional
public void releaseStock(Long productId, Integer quantity, String orderNo) {
log.info("释放库存: 商品ID={}, 数量={}, 订单={}", productId, quantity, orderNo);
Inventory inventory = inventoryMapper.findByProductId(productId);
if (inventory == null) {
log.warn("商品不存在: {}", productId);
return;
}
// 释放库存 = 减少预留数量
inventory.setReservedStock(Math.max(0, inventory.getReservedStock() - quantity));
inventoryMapper.update(inventory);
// 记录库存变动日志
InventoryLog log = new InventoryLog();
log.setProductId(productId);
log.setOrderNo(orderNo);
log.setChangeType("RELEASE");
log.setQuantity(quantity);
log.setBeforeStock(inventory.getStock());
log.setAfterStock(inventory.getStock());
log.setCreateTime(LocalDateTime.now());
inventoryLogMapper.insert(log);
log.info("库存释放成功: {}", inventory);
}
/**
* 扣减库存(正式扣减,释放预留)
* 订单支付成功后调用
*/
@Transactional
public void consumeStock(Long productId, Integer quantity, String orderNo) {
log.info("扣减库存: 商品ID={}, 数量={}, 订单={}", productId, quantity, orderNo);
Inventory inventory = inventoryMapper.findByProductId(productId);
if (inventory == null) {
throw new RuntimeException("商品不存在: " + productId);
}
// 扣减库存 = 减少总库存和预留库存
inventory.setStock(inventory.getStock() - quantity);
inventory.setReservedStock(inventory.getReservedStock() - quantity);
inventoryMapper.update(inventory);
// 记录库存变动日志
InventoryLog inventoryLog = new InventoryLog();
inventoryLog.setProductId(productId);
inventoryLog.setOrderNo(orderNo);
inventoryLog.setChangeType("CONSUME");
inventoryLog.setQuantity(quantity);
inventoryLog.setBeforeStock(inventory.getStock() + quantity);
inventoryLog.setAfterStock(inventory.getStock());
inventoryLog.setCreateTime(LocalDateTime.now());
inventoryLogMapper.insert(inventoryLog);
log.info("库存扣减成功: {}", inventory);
}
/**
* 查询商品库存
*/
public Inventory findByProductId(Long productId) {
return inventoryMapper.findByProductId(productId);
}
}