package cn.iocoder.yudao.module.wms.service.inventory; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.wms.api.inventory.dto.*; import cn.iocoder.yudao.module.wms.controller.admin.inventory.vo.WmsInventoryReservePageReqVO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryReserveDO; import cn.iocoder.yudao.module.wms.dal.dataobject.inventory.WmsInventoryTransactionDO; import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryReserveMapper; import cn.iocoder.yudao.module.wms.dal.mysql.inventory.WmsInventoryTransactionMapper; import cn.iocoder.yudao.module.wms.enums.inventory.WmsReserveStatusEnum; import cn.iocoder.yudao.module.wms.enums.inventory.WmsTransactionTypeEnum; import cn.iocoder.yudao.module.wms.service.inventory.dto.WmsInventoryChangeReqDTO; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.UUID; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*; /** * WMS 库存预留 Service 实现类 * * @author 芋道源码 */ @Service @Validated @Slf4j public class WmsInventoryReserveServiceImpl implements WmsInventoryReserveService { @Resource private WmsInventoryReserveMapper reserveMapper; @Resource private WmsInventoryTransactionMapper transactionMapper; @Resource private WmsInventoryService inventoryService; @Resource private WmsInventoryTransactionService transactionService; @Override @Transactional(rollbackFor = Exception.class) public String createReserve(WmsReserveReqDTO reqDTO) { // 1. 校验可用库存 BigDecimal availableQuantity = inventoryService.getAvailableQuantity(reqDTO.getSkuId(), reqDTO.getWarehouseId()); if (availableQuantity.compareTo(reqDTO.getQuantity()) < 0) { throw exception(INVENTORY_QUANTITY_NOT_ENOUGH_FOR_RESERVE, reqDTO.getSkuId(), reqDTO.getWarehouseId(), availableQuantity, reqDTO.getQuantity()); } // 2. 生成预留单号 String reserveNo = generateReserveNo(); // 3. 创建预留记录 WmsInventoryReserveDO reserve = BeanUtils.toBean(reqDTO, WmsInventoryReserveDO.class); reserve.setReserveNo(reserveNo); reserve.setReserveQuantity(reqDTO.getQuantity()); reserve.setUsedQuantity(BigDecimal.ZERO); reserve.setStatus(WmsReserveStatusEnum.RESERVED.getStatus()); reserveMapper.insert(reserve); // 4. 记录事务流水 transactionService.createTransaction(buildReserveTransaction(reserve, reqDTO.getQuantity())); return reserveNo; } @Override @Transactional(rollbackFor = Exception.class) public void releaseReserve(String reserveNo) { // 1. 查询预留记录 WmsInventoryReserveDO reserve = reserveMapper.selectByReserveNo(reserveNo); if (reserve == null) { throw exception(INVENTORY_RESERVE_NOT_EXISTS); } // 1.1 校验状态:只有预留中或部分使用状态可释放 if (reserve.getStatus() != WmsReserveStatusEnum.RESERVED.getStatus() && reserve.getStatus() != WmsReserveStatusEnum.PARTIAL_USED.getStatus()) { throw exception(INVENTORY_RESERVE_STATUS_INVALID); } // 2. 计算释放数量 BigDecimal releaseQuantity = reserve.getReserveQuantity().subtract(reserve.getUsedQuantity()); // 3. 更新预留状态 reserve.setStatus(WmsReserveStatusEnum.RELEASED.getStatus()); reserveMapper.updateById(reserve); // 4. 记录事务流水(释放为负数) transactionService.createTransaction(buildReleaseTransaction(reserve, releaseQuantity.negate())); } @Override @Transactional(rollbackFor = Exception.class) public void useReserve(WmsUseReserveReqDTO reqDTO) { // 1. 查询预留记录 WmsInventoryReserveDO reserve = reserveMapper.selectByReserveNo(reqDTO.getReserveNo()); if (reserve == null) { throw exception(INVENTORY_RESERVE_NOT_EXISTS); } // 1.1 校验状态 if (reserve.getStatus() != WmsReserveStatusEnum.RESERVED.getStatus() && reserve.getStatus() != WmsReserveStatusEnum.PARTIAL_USED.getStatus()) { throw exception(INVENTORY_RESERVE_STATUS_INVALID); } // 2. 计算剩余可使用数量 BigDecimal remainQuantity = reserve.getReserveQuantity().subtract(reserve.getUsedQuantity()); if (reqDTO.getQuantity().compareTo(remainQuantity) > 0) { throw exception(INVENTORY_RESERVE_QUANTITY_NOT_ENOUGH, reqDTO.getReserveNo(), remainQuantity, reqDTO.getQuantity()); } // 3. 更新已使用数量和状态 BigDecimal newUsedQuantity = reserve.getUsedQuantity().add(reqDTO.getQuantity()); reserve.setUsedQuantity(newUsedQuantity); if (newUsedQuantity.compareTo(reserve.getReserveQuantity()) == 0) { reserve.setStatus(WmsReserveStatusEnum.USED.getStatus()); } else { reserve.setStatus(WmsReserveStatusEnum.PARTIAL_USED.getStatus()); } reserveMapper.updateById(reserve); } @Override public List getReserveListByBiz(Integer bizType, Long bizId) { return reserveMapper.selectListByBiz(bizType, bizId); } @Override public PageResult getReservePage(WmsInventoryReservePageReqVO reqVO) { return reserveMapper.selectPage(reqVO); } @Override public WmsInventoryReserveDO getReserve(Long id) { return reserveMapper.selectById(id); } @Override public BigDecimal getReserveQuantity(Long skuId, Long warehouseId) { return reserveMapper.selectReserveQuantity(skuId, warehouseId); } /** * 生成预留单号 */ private String generateReserveNo() { return "RES" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + UUID.randomUUID().toString().substring(0, 8).toUpperCase(); } /** * 构建预留事务流水 */ private WmsInventoryTransactionDO buildReserveTransaction(WmsInventoryReserveDO reserve, BigDecimal quantity) { return WmsInventoryTransactionDO.builder() .transactionNo(generateTransactionNo()) .transactionType(WmsTransactionTypeEnum.RESERVE.getType()) .bizType(reserve.getBizType()) .bizId(reserve.getBizId()) .bizNo(reserve.getBizNo()) .skuId(reserve.getSkuId()) .warehouseId(reserve.getWarehouseId()) .quantity(quantity) .reserveId(reserve.getId()) .reserveNo(reserve.getReserveNo()) .transactionTime(LocalDateTime.now()) .remark(reserve.getRemark()) .build(); } /** * 构建释放事务流水 */ private WmsInventoryTransactionDO buildReleaseTransaction(WmsInventoryReserveDO reserve, BigDecimal quantity) { return WmsInventoryTransactionDO.builder() .transactionNo(generateTransactionNo()) .transactionType(WmsTransactionTypeEnum.RELEASE.getType()) .bizType(reserve.getBizType()) .bizId(reserve.getBizId()) .bizNo(reserve.getBizNo()) .skuId(reserve.getSkuId()) .warehouseId(reserve.getWarehouseId()) .quantity(quantity) .reserveId(reserve.getId()) .reserveNo(reserve.getReserveNo()) .transactionTime(LocalDateTime.now()) .remark("释放预留") .build(); } /** * 生成事务流水号 */ private String generateTransactionNo() { return "TRX" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + UUID.randomUUID().toString().substring(0, 6).toUpperCase(); } }