云
6 天以前 9265065bea5a40bcaced67857cb3ba22324ce06c
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
@@ -20,6 +20,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockFreezeClaimMapper;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
@@ -70,6 +71,8 @@
    @Resource
    private MesWmMaterialStockMapper materialStockMapper;
    @Resource
    private MesWmMaterialStockFreezeClaimMapper freezeClaimMapper;
    @Resource
    private MesMdmItemService mdmItemService;
@@ -132,6 +135,8 @@
            MesMdItemDO item = mdItemService.getItem(pageReqVO.getItemId());
            if (item != null && item.getMdmItemId() != null) {
                itemIds = SetUtils.asSet(item.getMdmItemId());
            } else {
                return PageResult.empty();
            }
        }
        // 1.3 解析 virtualFilter:转换为虚拟仓 warehouseId
@@ -150,21 +155,34 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMaterialStockFrozen(MesWmMaterialStockFreezeReqVO updateReqVO) {
        // 校验存在
        validateMaterialStockExists(updateReqVO.getId());
        // 更新冻结状态
        materialStockMapper.updateById(new MesWmMaterialStockDO()
                .setId(updateReqVO.getId()).setFrozen(updateReqVO.getFrozen()));
        setMaterialStockFreeze(List.of(updateReqVO.getId()), "MANUAL", 0L, updateReqVO.getFrozen());
    }
    @Override
    public void updateMaterialStockFrozen(Collection<Long> ids, boolean frozen) {
    @Transactional(rollbackFor = Exception.class)
    public void setMaterialStockFreeze(Collection<Long> ids, String sourceType, Long sourceId, boolean frozen) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        materialStockMapper.updateByIds(ids, new MesWmMaterialStockDO().setFrozen(frozen));
        // 按 ID 固定顺序加锁,避免两个任务同时冻结相同库存时产生锁顺序反转。
        for (Long stockId : ids.stream().filter(Objects::nonNull).distinct().sorted().toList()) {
            MesWmMaterialStockDO stock = materialStockMapper.lockById(stockId);
            if (stock == null) {
                throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
            }
            if (frozen) {
                freezeClaimMapper.acquire(stockId, sourceType, sourceId);
            } else {
                freezeClaimMapper.release(stockId, sourceType, sourceId);
            }
            boolean effectiveFrozen = freezeClaimMapper.countByStockId(stockId) > 0;
            if (!Objects.equals(stock.getFrozen(), effectiveFrozen)) {
                materialStockMapper.updateById(new MesWmMaterialStockDO()
                        .setId(stockId).setFrozen(effectiveFrozen));
            }
        }
    }
    @Override
@@ -399,7 +417,7 @@
        } else {
            // 出库时校验批次存在
            if (batchId != null) {
                batchService.validateBatchExists(batchId, reqVO.getItemId());
                batchCode = batchService.validateBatchExists(batchId, reqVO.getItemId()).getCode();
            }
        }
@@ -457,20 +475,26 @@
     * @return 批次记录(物料未启用批次管理时返回 null)
     */
    private MesWmBatchDO resolveBatch(MesWmMaterialStockAdjustReqVO reqVO, MesMdmItemDO item) {
        // 1. 物料未启用批次管理,返回 null
        if (Boolean.FALSE.equals(item.getIsBatchManaged())) {
            return null;
        // 即使物料未启用普通批次管理,也要保留并校验盘号追溯批次。
        if (reqVO.getBatchId() != null) {
            MesWmBatchDO existing = batchService.validateBatchExists(reqVO.getBatchId(), reqVO.getItemId());
            if (StrUtil.isNotBlank(reqVO.getReelNo())
                    && !Objects.equals(StrUtil.trim(reqVO.getReelNo()), StrUtil.trim(existing.getReelNo()))) {
                throw exception(WM_BATCH_REEL_MISMATCH);
            }
            return existing;
        }
        // 2. 传入 batchId,校验并返回已有批次
        if (reqVO.getBatchId() != null) {
            return batchService.validateBatchExists(reqVO.getBatchId(), reqVO.getItemId());
        // 1. 未指定追溯批次且物料未启用批次管理,返回 null
        if (Boolean.FALSE.equals(item.getIsBatchManaged()) && StrUtil.isBlank(reqVO.getReelNo())) {
            return null;
        }
        // 3. 未传 batchId,自动创建批次
        MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO()
                .setItemId(reqVO.getItemId())
                .setBatchCode(reqVO.getBatchCode())
                .setReelNo(StrUtil.trimToNull(reqVO.getReelNo()))
                .setVendorId(reqVO.getVendorId())
                .setProduceDate(reqVO.getProduceDate() != null ? reqVO.getProduceDate().atStartOfDay() : null)
                .setExpireDate(reqVO.getExpireDate() != null ? reqVO.getExpireDate().atStartOfDay() : null)