昨天 eccad5a129106377a275be4f7cdc58e99e9b95d4
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
@@ -2,20 +2,34 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemPageReqVO;
import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemSaveReqVO;
import cn.iocoder.yudao.module.mdm.dal.dataobject.category.MdmItemCategoryDO;
import cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemDO;
import cn.iocoder.yudao.module.mdm.dal.dataobject.unit.MdmUnitMeasureDO;
import cn.iocoder.yudao.module.mdm.dal.mysql.item.MdmItemBatchConfigMapper;
import cn.iocoder.yudao.module.mdm.dal.mysql.item.MdmItemMapper;
import cn.iocoder.yudao.module.mdm.service.brand.MdmBrandService;
import cn.iocoder.yudao.module.mdm.service.category.MdmItemCategoryService;
import cn.iocoder.yudao.module.mdm.service.unit.MdmUnitMeasureService;
import cn.iocoder.yudao.module.mdm.service.brand.MdmBrandService;
import cn.iocoder.yudao.module.mes.api.item.MesMdItemApi;
import cn.iocoder.yudao.module.mes.api.item.MesMdItemTypeApi;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemTypeRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -40,39 +54,113 @@
    @Resource
    private MdmBrandService brandService;
    @Resource
    @Lazy
    private MesMdItemApi mesMdItemApi;
    @Resource
    private MdmItemBatchConfigMapper itemBatchConfigMapper;
    @Resource
    private MesMdItemTypeApi mesMdItemTypeApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createItem(MdmItemSaveReqVO createReqVO) {
        // 校验数据
        validateItemSaveData(createReqVO);
        // 插入
        // 插入 MDM 物料
        MdmItemDO item = BeanUtils.toBean(createReqVO, MdmItemDO.class);
        itemMapper.insert(item);
        // 保存批次配置(如果有)
        saveBatchConfig(item.getId(), createReqVO);
        // 同步到 MES(勾选 syncMes 时)
        createReqVO.setId(item.getId());
        if (Boolean.TRUE.equals(createReqVO.getSyncMes())) {
            syncItemToMes(createReqVO);
        }
        return item.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateItem(MdmItemSaveReqVO updateReqVO) {
        // 校验存在
        validateItemExists(updateReqVO.getId());
        // 校验数据
        validateItemSaveData(updateReqVO);
        // 更新
        // 更新 MDM 物料
        MdmItemDO updateObj = BeanUtils.toBean(updateReqVO, MdmItemDO.class);
        itemMapper.updateById(updateObj);
        // 保存批次配置(如果有)
        saveBatchConfig(updateReqVO.getId(), updateReqVO);
        // 同步到 MES(勾选 syncMes 时)
        if (Boolean.TRUE.equals(updateReqVO.getSyncMes())) {
            syncItemToMes(updateReqVO);
        }
    }
    private void validateItemSaveData(MdmItemSaveReqVO reqVO) {
        // 校验物料编码的唯一性
        validateItemCodeUnique(reqVO.getId(), reqVO.getCode());
        // 校验物料分类存在
        validateCategoryExists(reqVO.getCategoryId());
        // 校验计量单位存在
        validateUnitMeasureExists(reqVO.getUnitMeasureId());
        // 校验品牌存在(如果有)
        if (reqVO.getBrandId() != null) {
            validateBrandExists(reqVO.getBrandId());
    }
    /**
     * 同步物料到 MES(单位/分类按 code 映射)
     *
     * MDM 的 unitMeasureId/categoryId 与 MES 的 unitMeasureId/itemTypeId 分属两套主数据,
     * 这里先在 MDM 侧解析出 code,再交由 MesMdItemApi 在 MES 侧解析为对应 id。
     * 同步失败将抛出异常,触发外层 @Transactional 整体回滚(MDM 操作一并回滚)。
     */
    private void syncItemToMes(MdmItemSaveReqVO reqVO) {
        // 解析计量单位 id -> code
        String unitCode = null;
        if (reqVO.getUnitMeasureId() != null) {
            MdmUnitMeasureDO unitMeasure = unitMeasureService.getUnitMeasure(reqVO.getUnitMeasureId());
            if (unitMeasure == null) {
                throw exception(MDM_UNIT_MEASURE_NOT_EXISTS);
            }
            unitCode = unitMeasure.getCode();
        }
        // 解析分类 id -> code(使用 MDM 本地分类服务)
        String itemTypeCode = null;
        if (reqVO.getCategoryId() != null) {
            CommonResult<MesMdItemTypeRespDTO> itemType = mesMdItemTypeApi.getItemType(reqVO.getCategoryId());
            itemTypeCode = itemType.getData().getCode();
        }
        // 组装同步 DTO
        MesMdItemSyncReqDTO syncReqDTO = new MesMdItemSyncReqDTO();
        syncReqDTO.setCode(reqVO.getCode());
        syncReqDTO.setMdmItemId(reqVO.getId());
        syncReqDTO.setName(reqVO.getName());
        syncReqDTO.setSpecification(reqVO.getSpecification());
        syncReqDTO.setUnitCode(unitCode);
        syncReqDTO.setItemTypeCode(itemTypeCode);
        syncReqDTO.setSafeStockFlag(Boolean.TRUE); // 保留 MDM 的 minStock/maxStock,避免被 MES 清零
        syncReqDTO.setMinStock(reqVO.getMinStock());
        syncReqDTO.setMaxStock(reqVO.getMaxStock());
        syncReqDTO.setHighValue(reqVO.getIsHighValue());
        syncReqDTO.setBatchFlag(reqVO.getIsBatchManaged());
        syncReqDTO.setRemark(reqVO.getRemark());
        // 批次配置字段
        syncReqDTO.setProduceDateFlag(reqVO.getProduceDateFlag());
        syncReqDTO.setExpireDateFlag(reqVO.getExpireDateFlag());
        syncReqDTO.setReceiptDateFlag(reqVO.getReceiptDateFlag());
        syncReqDTO.setVendorFlag(reqVO.getVendorFlag());
        syncReqDTO.setPurchaseOrderCodeFlag(reqVO.getPurchaseOrderCodeFlag());
        syncReqDTO.setLotNumberFlag(reqVO.getLotNumberFlag());
        syncReqDTO.setQualityStatusFlag(reqVO.getQualityStatusFlag());
        // create 走 createItem,update 走 updateItem(MES 侧按 code upsert)
        if (reqVO.getId() == null) {
            mesMdItemApi.createItem(syncReqDTO);
        } else {
            mesMdItemApi.updateItem(syncReqDTO);
        }
    }
@@ -85,11 +173,27 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteItem(Long id) {
        // 校验存在
        validateItemExists(id);
        // 删除
        MdmItemDO item = validateItemExists(id);
        // 解决软删除唯一键冲突:如果已存在相同 code 且已软删除的记录,先物理删除它
//        MdmItemDO existingDeleted = itemMapper.selectByCodeAndDeleted(item.getCode(), true);
//        if (existingDeleted != null && !existingDeleted.getId().equals(id)) {
//            itemMapper.physicalDeleteById(existingDeleted.getId());
//        }
        // 软删除当前记录
        itemMapper.deleteById(id);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteItemList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 逐个删除(复用单条删除逻辑,保持校验)
        ids.forEach(this::deleteItem);
    }
    @Override
@@ -121,7 +225,12 @@
    }
    private void validateCategoryExists(Long categoryId) {
        if (itemCategoryService.getItemCategory(categoryId) == null) {
        if (categoryId == null) {
            return;
        }
        // 使用 MDM 本地分类服务校验
        MdmItemCategoryDO category = itemCategoryService.getItemCategory(categoryId);
        if (category == null) {
            throw exception(MDM_ITEM_CATEGORY_NOT_EXISTS);
        }
    }
@@ -135,6 +244,30 @@
    private void validateBrandExists(Long brandId) {
        if (brandService.getBrand(brandId) == null) {
            throw exception(MDM_BRAND_NOT_EXISTS);
        }
    }
    /**
     * 将 itemType 转换为中文名称
     *
     * @param itemType 物料类型:1原料, 2半成品, 3成品, 4辅料
     * @return 中文名称
     */
    private String convertItemTypeToName(Integer itemType) {
        if (itemType == null) {
            return null;
        }
        switch (itemType) {
            case 1:
                return "原料";
            case 2:
                return "半成品";
            case 3:
                return "成品";
            case 4:
                return "辅料";
            default:
                return null;
        }
    }
@@ -186,4 +319,51 @@
        return itemMapper.selectByCode(code);
    }
    /**
     * 保存批次配置(到 MDM 本地表)
     */
    private void saveBatchConfig(Long itemId, MdmItemSaveReqVO reqVO) {
        // 检查是否有批次配置数据
        if (reqVO.getProduceDateFlag() == null
                && reqVO.getExpireDateFlag() == null
                && reqVO.getReceiptDateFlag() == null
                && reqVO.getVendorFlag() == null
                && reqVO.getPurchaseOrderCodeFlag() == null
                && reqVO.getLotNumberFlag() == null
                && reqVO.getQualityStatusFlag() == null) {
            return;
        }
        // 查询已有配置
        cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO existing =
                itemBatchConfigMapper.selectOne(
                        cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO::getItemId, itemId);
        cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO config =
                new cn.iocoder.yudao.module.mdm.dal.dataobject.item.MdmItemBatchConfigDO();
        config.setItemId(itemId);
        config.setProduceDateFlag(Boolean.TRUE.equals(reqVO.getProduceDateFlag()));
        config.setExpireDateFlag(Boolean.TRUE.equals(reqVO.getExpireDateFlag()));
        config.setReceiptDateFlag(Boolean.TRUE.equals(reqVO.getReceiptDateFlag()));
        config.setVendorFlag(Boolean.TRUE.equals(reqVO.getVendorFlag()));
        config.setPurchaseOrderCodeFlag(Boolean.TRUE.equals(reqVO.getPurchaseOrderCodeFlag()));
        config.setLotNumberFlag(Boolean.TRUE.equals(reqVO.getLotNumberFlag()));
        config.setQualityStatusFlag(Boolean.TRUE.equals(reqVO.getQualityStatusFlag()));
        // 清空不需要的字段
        config.setClientFlag(false);
        config.setSalesOrderCodeFlag(false);
        config.setWorkOrderFlag(false);
        config.setTaskFlag(false);
        config.setWorkstationFlag(false);
        config.setToolFlag(false);
        config.setMoldFlag(false);
        if (existing != null) {
            config.setId(existing.getId());
            itemBatchConfigMapper.updateById(config);
        } else {
            itemBatchConfigMapper.insert(config);
        }
    }
}