xiaoyi
6 天以前 05ab6e3f69a5e73624b2573db043f5923e8f7d8b
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java
@@ -3,7 +3,6 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.MesMdItemImportExcelVO;
@@ -16,8 +15,9 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
import cn.iocoder.yudao.module.mes.enums.md.MesMdItemTypeEnum;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
@@ -31,7 +31,6 @@
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -42,7 +41,7 @@
 *
 * 支持 MES 和 MDM 物料数据双向同步
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -77,9 +76,27 @@
        // 校验数据
        validateItemSaveData(createReqVO);
        // 如果指定了 ID,检查该 ID 是否已存在(用于 MDM 同步时保持 ID 一致)
        if (createReqVO.getId() != null) {
            MesMdItemDO existItem = itemMapper.selectById(createReqVO.getId());
            if (existItem != null) {
                // ID 已存在,转为更新
                updateItem(createReqVO);
                return createReqVO.getId();
            }
            // 存在逻辑删除记录(取消同步后重新同步):恢复该记录后走更新,避免主键冲突
            if (itemMapper.selectAnyById(createReqVO.getId()) != null) {
                itemMapper.restoreById(createReqVO.getId());
                updateItem(createReqVO);
                return createReqVO.getId();
            }
        }
        // 插入
        MesMdItemDO item = BeanUtils.toBean(createReqVO, MesMdItemDO.class);
        item.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 默认禁用。信息完成后,再启用
        // 状态只能由基础数据(MDM)控制:MDM 同步时沿用其状态;MES 本地新增默认禁用
        item.setStatus(createReqVO.getMdmItemId() != null && createReqVO.getStatus() != null
                ? createReqVO.getStatus() : CommonStatusEnum.DISABLE.getStatus());
        clearStockIfNotSafe(item); // 如果未启用安全库存,清零库存上下限
        itemMapper.insert(item);
@@ -106,6 +123,10 @@
        // 更新
        MesMdItemDO updateObj = BeanUtils.toBean(updateReqVO, MesMdItemDO.class);
        clearStockIfNotSafe(updateObj); // 如果未启用安全库存,清零库存上下限
        // 状态只能由基础数据(MDM)控制,MES 本地更新不允许修改状态
        if (updateReqVO.getMdmItemId() == null) {
            updateObj.setStatus(null);
        }
        itemMapper.updateById(updateObj);
        // 同步到 MDM(勾选 syncMdm 时)
@@ -118,8 +139,7 @@
    private void validateItemSaveData(MesMdItemSaveReqVO reqVO) {
        // 校验物料编码的唯一性
        validateItemCodeUnique(reqVO.getId(), reqVO.getCode());
        // 校验物料名称的唯一性
        validateItemNameUnique(reqVO.getId(), reqVO.getName());
        // 不再校验物料名称的唯一性:允许同名不同编码的物料(与 MDM 物料规则保持一致)
        // 校验物料分类存在
        validateItemTypeExists(reqVO.getItemTypeId());
        // 校验计量单位存在
@@ -194,24 +214,9 @@
        }
    }
    private void validateItemNameUnique(Long id, String name) {
        MesMdItemDO item = itemMapper.selectByName(name);
        if (item == null) {
            return;
        }
        if (ObjUtil.notEqual(item.getId(), id)) {
            throw exception(MD_ITEM_NAME_DUPLICATE);
        }
    }
    private void validateItemTypeExists(Long itemTypeId) {
        if (itemTypeService.getItemType(itemTypeId) == null) {
            throw exception(MD_ITEM_TYPE_NOT_EXISTS);
        }
        // 校验必须是叶子分类(没有子分类)
        List<MesMdItemTypeDO> children = itemTypeService.getItemTypeChildrenList(itemTypeId);
        if (CollUtil.isNotEmpty(children)) {
            throw exception(MD_ITEM_TYPE_NOT_LEAF);
        }
    }
@@ -298,94 +303,6 @@
    @Override
    public Long getItemCountByUnitMeasureId(Long unitMeasureId) {
        return itemMapper.selectCountByUnitMeasureId(unitMeasureId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MesMdItemImportRespVO importItemList(List<MesMdItemImportExcelVO> importItems, boolean updateSupport) {
        // 1. 参数校验
        if (CollUtil.isEmpty(importItems)) {
            throw exception(MD_ITEM_IMPORT_LIST_IS_EMPTY);
        }
        // 2. 遍历,逐个创建 or 更新
        MesMdItemImportRespVO respVO = MesMdItemImportRespVO.builder()
                .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
                .failureCodes(new LinkedHashMap<>()).build();
        AtomicInteger index = new AtomicInteger(1);
        importItems.forEach(importItem -> {
            int currentIndex = index.getAndIncrement();
            // 2.1 校验字段
            if (StrUtil.isBlank(importItem.getCode())) {
                // 空编码时自动生成
                importItem.setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.MD_ITEM_CODE.getCode()));
            }
            String key = importItem.getCode();
            if (StrUtil.isBlank(importItem.getName())) {
                respVO.getFailureCodes().put(key, "物料名称不能为空");
                return;
            }
            if (StrUtil.isBlank(importItem.getUnitMeasureCode())) {
                respVO.getFailureCodes().put(key, "单位编码不能为空");
                return;
            }
            // 将单位编码转换为单位 ID
            MesMdUnitMeasureDO unitMeasure = unitMeasureService.getUnitMeasureByCode(importItem.getUnitMeasureCode());
            if (unitMeasure == null) {
                respVO.getFailureCodes().put(key, "单位编码[" + importItem.getUnitMeasureCode() + "]不存在");
                return;
            }
            if (importItem.getItemTypeId() == null) {
                respVO.getFailureCodes().put(key, "物料分类编号不能为空");
                return;
            }
            // 2.2 校验分类是否存在
            try {
                validateItemTypeExists(importItem.getItemTypeId());
            } catch (ServiceException ex) {
                respVO.getFailureCodes().put(key, ex.getMessage());
                return;
            }
            // 2.3 判断:创建 or 更新
            MesMdItemDO existItem = itemMapper.selectByCode(importItem.getCode());
            if (existItem == null) {
                // 2.3.1 创建
                try {
                    validateItemNameUnique(null, importItem.getName());
                } catch (ServiceException ex) {
                    respVO.getFailureCodes().put(key, ex.getMessage());
                    return;
                }
                MesMdItemDO item = BeanUtils.toBean(importItem, MesMdItemDO.class);
                item.setUnitMeasureId(unitMeasure.getId());
                item.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 默认禁用。信息完成后,再启用
                clearStockIfNotSafe(item);
                itemMapper.insert(item);
                // 自动生成条码
                barcodeService.autoGenerateBarcode(BarcodeBizTypeEnum.ITEM.getValue(),
                        item.getId(), item.getCode(), item.getName());
                respVO.getCreateCodes().add(importItem.getCode());
            } else if (updateSupport) {
                // 2.3.2 更新
                try {
                    validateItemNameUnique(existItem.getId(), importItem.getName());
                } catch (ServiceException ex) {
                    respVO.getFailureCodes().put(key, ex.getMessage());
                    return;
                }
                MesMdItemDO updateObj = BeanUtils.toBean(importItem, MesMdItemDO.class);
                updateObj.setId(existItem.getId());
                updateObj.setUnitMeasureId(unitMeasure.getId());
                clearStockIfNotSafe(updateObj);
                itemMapper.updateById(updateObj);
                respVO.getUpdateCodes().add(importItem.getCode());
            } else {
                // 不支持更新
                respVO.getFailureCodes().put(key, "物料编码已存在");
            }
        });
        return respVO;
    }
    /**
@@ -478,4 +395,99 @@
        return 1; // 默认原料
    }
}
    @Override
    public MesMdItemDO getItemByMdmItemId(Long mdmItemId) {
        if (mdmItemId == null) {
            return null;
        }
        return itemMapper.selectByMdmItemId(mdmItemId);
    }
    @Override
    public MesMdItemDO validateItemExistsByMdmItemId(Long mdmItemId) {
        MesMdItemDO item = getItemByMdmItemId(mdmItemId);
        if (item == null) {
            throw exception(MD_ITEM_NOT_EXISTS);
        }
        return item;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MesMdItemImportRespVO importItemList(List<MesMdItemImportExcelVO> importList, Boolean updateSupport) {
        MesMdItemImportRespVO respVO = MesMdItemImportRespVO.empty();
        if (CollUtil.isEmpty(importList)) {
            return respVO;
        }
        // 预加载计量单位、物料分类,按名称建索引,避免逐行查库
        Map<String, Long> unitMeasureMap = new HashMap<>();
        unitMeasureService.getUnitMeasureListByStatus(CommonStatusEnum.ENABLE.getStatus())
                .forEach(unit -> unitMeasureMap.put(unit.getName().trim(), unit.getId()));
        Map<String, Long> itemTypeMap = new HashMap<>();
        itemTypeService.getItemTypeList(new MesMdItemTypeListReqVO())
                .forEach(type -> itemTypeMap.put(type.getName().trim(), type.getId()));
        int index = 0;
        for (MesMdItemImportExcelVO row : importList) {
            index++;
            String key = StrUtil.isNotBlank(row.getCode()) ? row.getCode().trim() : "第 " + index + " 行";
            // 1. 基础校验
            if (StrUtil.isBlank(row.getCode())) {
                respVO.getFailureCodes().put(key, "物料编码不能为空");
                continue;
            }
            if (StrUtil.isBlank(row.getName())) {
                respVO.getFailureCodes().put(key, "物料名称不能为空");
                continue;
            }
            String code = row.getCode().trim();
            String unitName = StrUtil.trim(row.getUnitMeasureName());
            Long unitMeasureId = unitName == null ? null : unitMeasureMap.get(unitName);
            if (unitMeasureId == null) {
                respVO.getFailureCodes().put(key, "计量单位[" + unitName + "]不存在");
                continue;
            }
            String typeName = StrUtil.trim(row.getItemTypeName());
            Long itemTypeId = typeName == null ? null : itemTypeMap.get(typeName);
            if (itemTypeId == null) {
                respVO.getFailureCodes().put(key, "物料分类[" + typeName + "]不存在");
                continue;
            }
            // 2. 组装保存对象(颜色一并写入)
            MesMdItemSaveReqVO saveReqVO = new MesMdItemSaveReqVO();
            saveReqVO.setCode(code);
            saveReqVO.setName(row.getName().trim());
            saveReqVO.setSpecification(StrUtil.trim(row.getSpecification()));
            saveReqVO.setColor(StrUtil.trim(row.getColor()));
            saveReqVO.setUnitMeasureId(unitMeasureId);
            saveReqVO.setItemTypeId(itemTypeId);
            saveReqVO.setSafeStockFlag(row.getSafeStockFlag());
            saveReqVO.setMinStock(row.getMinStock());
            saveReqVO.setMaxStock(row.getMaxStock());
            saveReqVO.setHighValue(row.getHighValue());
            saveReqVO.setBatchFlag(row.getBatchFlag());
            saveReqVO.setRemark(StrUtil.trim(row.getRemark()));
            // 3. 新增或更新,单行失败不影响整批
            MesMdItemDO existItem = itemMapper.selectByCode(code);
            try {
                if (existItem == null) {
                    createItem(saveReqVO);
                    respVO.getCreateCodes().add(code);
                } else if (!Boolean.TRUE.equals(updateSupport)) {
                    respVO.getFailureCodes().put(key, "物料编码已存在(勾选「是否覆盖」可更新)");
                } else {
                    saveReqVO.setId(existItem.getId());
                    saveReqVO.setMdmItemId(existItem.getMdmItemId());
                    updateItem(saveReqVO);
                    respVO.getUpdateCodes().add(code);
                }
            } catch (Exception e) {
                respVO.getFailureCodes().put(key, e.getMessage() == null ? "导入失败" : e.getMessage());
            }
        }
        return respVO;
    }
}