2026-06-29 940c8481d2fdcf51341db4ccd6fd6fcc2d43debb
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java
@@ -1,22 +1,34 @@
package cn.iocoder.yudao.module.erp.service.stock;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemSkuApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemSkuRespDTO;
import cn.iocoder.yudao.module.wms.api.inventory.WmsInventoryApi;
import cn.iocoder.yudao.module.wms.api.inventory.dto.WmsInventoryQueryReqDTO;
import cn.iocoder.yudao.module.wms.api.inventory.dto.WmsInventoryRespDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE2;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
/**
 * ERP 产品库存 Service 实现类
 *
 * 改造说明:ERP 库存调用 WMS 库存 API
 * - productId 对应 MDM itemId
 * - 通过 MDM SKU API 获取物料的默认 SKU
 * - 库存操作调用 WMS Inventory API
 *
 * @author 芋道源码
 */
@@ -26,18 +38,24 @@
    /**
     * 允许库存为负数
     *
     * TODO 芋艿:后续做成 db 配置
     */
    private static final Boolean NEGATIVE_STOCK_COUNT_ENABLE = false;
    @Resource
    private ErpProductService productService;
    @Resource
    private ErpWarehouseService warehouseService;
    @Resource
    private ErpStockMapper stockMapper;
    @Resource
    private WmsInventoryApi wmsInventoryApi;
    @Resource
    private MdmItemSkuApi mdmItemSkuApi;
    @Override
    public ErpStockDO getStock(Long id) {
@@ -46,44 +64,117 @@
    @Override
    public ErpStockDO getStock(Long productId, Long warehouseId) {
        return stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);
        // 通过 MDM 获取产品的默认 SKU,然后查询 WMS 库存
        Long skuId = getDefaultSkuId(productId);
        if (skuId == null) {
            return null;
        }
        // 查询 WMS 库存
        BigDecimal quantity = wmsInventoryApi.getAvailableQuantity(skuId, warehouseId).getCheckedData();
        if (quantity == null || quantity.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }
        // 转换为 ERP Stock DO
        ErpStockDO stock = new ErpStockDO();
        stock.setProductId(productId);
        stock.setWarehouseId(warehouseId);
        stock.setCount(quantity);
        return stock;
    }
    @Override
    public BigDecimal getStockCount(Long productId) {
        BigDecimal count = stockMapper.selectSumByProductId(productId);
        return count != null ? count : BigDecimal.ZERO;
        // 通过 MDM 获取产品的默认 SKU,然后查询 WMS 库存总量
        Long skuId = getDefaultSkuId(productId);
        if (skuId == null) {
            return BigDecimal.ZERO;
        }
        // 查询该 SKU 在所有仓库的库存
        WmsInventoryQueryReqDTO queryReq = new WmsInventoryQueryReqDTO();
        queryReq.setSkuIds(List.of(skuId));
        queryReq.setOnlyPositive(true);
        List<WmsInventoryRespDTO> inventories = wmsInventoryApi.getInventoryList(queryReq).getCheckedData();
        if (CollUtil.isEmpty(inventories)) {
            return BigDecimal.ZERO;
        }
        return inventories.stream()
                .map(WmsInventoryRespDTO::getQuantity)
                .filter(q -> q != null)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
    @Override
    public PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO) {
        // 暂时保留原逻辑,后续可改为调用 WMS 分页 API
        return stockMapper.selectPage(pageReqVO);
    }
    @Override
    public BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count) {
        // 1.1 查询当前库存
        ErpStockDO stock = stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);
        if (stock == null) {
            stock = new ErpStockDO().setProductId(productId).setWarehouseId(warehouseId).setCount(BigDecimal.ZERO);
            stockMapper.insert(stock);
        }
        // 1.2 校验库存是否充足
        if (!NEGATIVE_STOCK_COUNT_ENABLE && stock.getCount().add(count).compareTo(BigDecimal.ZERO) < 0) {
            throw exception(STOCK_COUNT_NEGATIVE, productService.getProduct(productId).getName(),
                    warehouseService.getWarehouse(warehouseId).getName(), stock.getCount(), count);
        }
        // 2. 库存变更
        int updateCount = stockMapper.updateCountIncrement(stock.getId(), count, NEGATIVE_STOCK_COUNT_ENABLE);
        if (updateCount == 0) {
            // 此时不好去查询最新库存,所以直接抛出该提示,不提供具体库存数字
            throw exception(STOCK_COUNT_NEGATIVE2, productService.getProduct(productId).getName(),
        // 通过 MDM 获取产品的默认 SKU
        Long skuId = getDefaultSkuId(productId);
        if (skuId == null) {
            throw exception(STOCK_COUNT_NEGATIVE2,
                    productService.getProduct(productId).getName(),
                    warehouseService.getWarehouse(warehouseId).getName());
        }
        // 3. 返回最新库存
        // 查询当前库存
        BigDecimal currentStock = wmsInventoryApi.getAvailableQuantity(skuId, warehouseId).getCheckedData();
        if (currentStock == null) {
            currentStock = BigDecimal.ZERO;
        }
        // 校验库存是否充足(出库时)
        if (!NEGATIVE_STOCK_COUNT_ENABLE && count.compareTo(BigDecimal.ZERO) < 0) {
            BigDecimal afterStock = currentStock.add(count);
            if (afterStock.compareTo(BigDecimal.ZERO) < 0) {
                throw exception(STOCK_COUNT_NEGATIVE,
                        productService.getProduct(productId).getName(),
                        warehouseService.getWarehouse(warehouseId).getName(),
                        currentStock, count.abs());
            }
        }
        // TODO: 调用 WMS 库存变更 API(需要 WMS 提供 increment/decrement 方法)
        // 暂时保留原逻辑,使用本地库存表
        ErpStockDO stock = stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);
        if (stock == null) {
            stock = new ErpStockDO()
                    .setProductId(productId)
                    .setWarehouseId(warehouseId)
                    .setCount(BigDecimal.ZERO);
            stockMapper.insert(stock);
        }
        int updateCount = stockMapper.updateCountIncrement(stock.getId(), count, NEGATIVE_STOCK_COUNT_ENABLE);
        if (updateCount == 0) {
            throw exception(STOCK_COUNT_NEGATIVE2,
                    productService.getProduct(productId).getName(),
                    warehouseService.getWarehouse(warehouseId).getName());
        }
        return stock.getCount().add(count);
    }
    /**
     * 获取产品的默认 SKU ID
     * ERP productId 对应 MDM itemId,默认使用第一个 SKU
     */
    private Long getDefaultSkuId(Long productId) {
        if (productId == null) {
            return null;
        }
        List<MdmItemSkuRespDTO> skuList = mdmItemSkuApi.getItemSkuListByItemId(productId).getCheckedData();
        if (CollUtil.isEmpty(skuList)) {
            return null;
        }
        return skuList.get(0).getId();
    }
}