huminmin
4 天以前 5af23a1b205787b50abdfbb3835619efa3911d60
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
@@ -2,17 +2,23 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
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.MdmItemImportExcelVO;
import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemImportRespVO;
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.MdmItemBatchConfigDO;
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.category.MdmItemCategoryMapper;
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.dal.mysql.unit.MdmUnitMeasureMapper;
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;
@@ -38,7 +44,7 @@
/**
 * MDM 物料主数据 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -61,6 +67,12 @@
    private MdmItemBatchConfigMapper itemBatchConfigMapper;
    @Resource
    private MdmItemCategoryMapper itemCategoryMapper;
    @Resource
    private MdmUnitMeasureMapper unitMeasureMapper;
    @Resource
    private MesMdItemTypeApi mesMdItemTypeApi;
@@ -77,9 +89,9 @@
        // 保存批次配置(如果有)
        saveBatchConfig(item.getId(), createReqVO);
        // 同步到 MES(勾选 syncMes 时)
        createReqVO.setId(item.getId());
        // 勾选同步时,同步到 MES
        if (Boolean.TRUE.equals(createReqVO.getSyncMes())) {
            createReqVO.setId(item.getId());
            syncItemToMes(createReqVO);
        }
        return item.getId();
@@ -100,9 +112,11 @@
        // 保存批次配置(如果有)
        saveBatchConfig(updateReqVO.getId(), updateReqVO);
        // 同步到 MES(勾选 syncMes 时)
        // 根据同步开关决定:打开则同步到 MES,关闭则取消同步(从 MES 删除)
        if (Boolean.TRUE.equals(updateReqVO.getSyncMes())) {
            syncItemToMes(updateReqVO);
        } else {
            mesMdItemApi.deleteItem(updateReqVO.getId());
        }
    }
@@ -147,6 +161,7 @@
        syncReqDTO.setMaxStock(reqVO.getMaxStock());
        syncReqDTO.setHighValue(reqVO.getIsHighValue());
        syncReqDTO.setBatchFlag(reqVO.getIsBatchManaged());
        syncReqDTO.setStatus(reqVO.getStatus());
        syncReqDTO.setRemark(reqVO.getRemark());
        // 批次配置字段
        syncReqDTO.setProduceDateFlag(reqVO.getProduceDateFlag());
@@ -165,11 +180,14 @@
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateItemStatus(Long id, Integer status) {
        // 校验存在
        validateItemExists(id);
        // 更新状态
        itemMapper.updateById(new MdmItemDO().setId(id).setStatus(status));
        // 同步状态到 MES
        mesMdItemApi.updateItemStatus(id, status);
    }
    @Override
@@ -177,13 +195,66 @@
    public void deleteItem(Long id) {
        // 校验存在
        MdmItemDO item = validateItemExists(id);
        // 解决软删除唯一键冲突:如果已存在相同 code 且已软删除的记录,先物理删除它
//        MdmItemDO existingDeleted = itemMapper.selectByCodeAndDeleted(item.getCode(), true);
//        if (existingDeleted != null && !existingDeleted.getId().equals(id)) {
//            itemMapper.physicalDeleteById(existingDeleted.getId());
//        }
        // 校验物料是否被业务引用
        validateItemNoReference(id);
        // 软删除当前记录
        itemMapper.deleteById(id);
        // 同步删除到 MES
        mesMdItemApi.deleteItem(id);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateItemSync(Long id, Boolean sync) {
        // 校验存在
        MdmItemDO item = validateItemExists(id);
        if (Boolean.TRUE.equals(sync)) {
            // 同步到 MES:从 DB 读取完整物料数据组装后走 syncItemToMes
            MdmItemSaveReqVO reqVO = BeanUtils.toBean(item, MdmItemSaveReqVO.class);
            // DO 字段名为 highValue,与 VO 的 isHighValue 不自动匹配,手动补齐
            reqVO.setIsHighValue(item.getHighValue());
            // 补齐批次属性配置
            MdmItemBatchConfigDO config = itemBatchConfigMapper.selectOne(
                    MdmItemBatchConfigDO::getItemId, id);
            if (config != null) {
                reqVO.setProduceDateFlag(config.getProduceDateFlag());
                reqVO.setExpireDateFlag(config.getExpireDateFlag());
                reqVO.setReceiptDateFlag(config.getReceiptDateFlag());
                reqVO.setVendorFlag(config.getVendorFlag());
                reqVO.setPurchaseOrderCodeFlag(config.getPurchaseOrderCodeFlag());
                reqVO.setLotNumberFlag(config.getLotNumberFlag());
                reqVO.setQualityStatusFlag(config.getQualityStatusFlag());
            }
            syncItemToMes(reqVO);
        } else {
            // 取消同步:从 MES 删除该物料(MES 无此物料时内部为空操作)
            mesMdItemApi.deleteItem(id);
        }
    }
    /**
     * 校验物料未被业务引用(销售、采购、生产、质检、库存等)
     *
     * @param id 物料编号
     */
    private void validateItemNoReference(Long id) {
        List<Map<String, Object>> references = itemMapper.selectItemReferenceCount(id);
        if (CollUtil.isNotEmpty(references)) {
            String refDesc = references.stream()
                    .map(r -> r.get("source") + "(" + r.get("cnt") + ")")
                    .collect(Collectors.joining("、"));
            throw exception(MDM_ITEM_EXISTS_REFERENCE, refDesc);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteItemList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 逐个删除(复用单条删除逻辑,保持校验)
        ids.forEach(this::deleteItem);
    }
    @Override
@@ -309,6 +380,12 @@
        return itemMapper.selectByCode(code);
    }
    @Override
    public List<MdmItemDO> getItemSimpleList() {
        return itemMapper.selectList(MdmItemDO::getStatus,
                cn.iocoder.yudao.framework.common.enums.CommonStatusEnum.ENABLE.getStatus());
    }
    /**
     * 保存批次配置(到 MDM 本地表)
     */
@@ -356,4 +433,121 @@
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public MdmItemImportRespVO importItemList(List<MdmItemImportExcelVO> importList, Boolean isUpdateSupport) {
        if (CollUtil.isEmpty(importList)) {
            throw exception(MDM_ITEM_IMPORT_LIST_IS_EMPTY);
        }
        MdmItemImportRespVO respVO = MdmItemImportRespVO.builder()
                .createList(new ArrayList<>()).updateList(new ArrayList<>())
                .failureList(new LinkedHashMap<>()).build();
        // 1. 预热:分类编码 -> ID、单位编码 -> ID
        Map<String, Long> categoryCodeIdMap = new HashMap<>();
        for (MdmItemCategoryDO category : itemCategoryMapper.selectList()) {
            if (category.getCode() != null) {
                categoryCodeIdMap.put(category.getCode(), category.getId());
            }
        }
        Map<String, Long> unitCodeIdMap = new HashMap<>();
        for (MdmUnitMeasureDO unit : unitMeasureMapper.selectList()) {
            unitCodeIdMap.put(unit.getCode(), unit.getId());
        }
        // 2. 遍历导入
        for (MdmItemImportExcelVO importVO : importList) {
            // 2.1 基础校验
            if (StrUtil.isBlank(importVO.getCode()) || StrUtil.isBlank(importVO.getName())) {
                respVO.getFailureList().put(StrUtil.blankToDefault(importVO.getName(), importVO.getCode()), "物料编码和物料名称不能为空");
                continue;
            }
            // 2.2 解析分类
            Long categoryId = null;
            if (StrUtil.isNotBlank(importVO.getCategoryCode())) {
                categoryId = categoryCodeIdMap.get(importVO.getCategoryCode());
                if (categoryId == null) {
                    respVO.getFailureList().put(importVO.getName(), "物料分类编码不存在:" + importVO.getCategoryCode());
                    continue;
                }
            }
            // 2.3 解析主单位
            Long unitMeasureId = null;
            if (StrUtil.isNotBlank(importVO.getUnitMeasureCode())) {
                unitMeasureId = unitCodeIdMap.get(importVO.getUnitMeasureCode());
                if (unitMeasureId == null) {
                    respVO.getFailureList().put(importVO.getName(), "计量单位编码不存在:" + importVO.getUnitMeasureCode());
                    continue;
                }
            }
            // 2.4 解析辅单位1
            Long unitMeasureId2 = null;
            if (StrUtil.isNotBlank(importVO.getUnitMeasureCode2())) {
                unitMeasureId2 = unitCodeIdMap.get(importVO.getUnitMeasureCode2());
                if (unitMeasureId2 == null) {
                    respVO.getFailureList().put(importVO.getName(), "辅单位1编码不存在:" + importVO.getUnitMeasureCode2());
                    continue;
                }
            }
            // 2.5 解析辅单位2
            Long unitMeasureId3 = null;
            if (StrUtil.isNotBlank(importVO.getUnitMeasureCode3())) {
                unitMeasureId3 = unitCodeIdMap.get(importVO.getUnitMeasureCode3());
                if (unitMeasureId3 == null) {
                    respVO.getFailureList().put(importVO.getName(), "辅单位2编码不存在:" + importVO.getUnitMeasureCode3());
                    continue;
                }
            }
            // 2.6 组装 SaveReqVO(复用 create/update 逻辑)
            MdmItemSaveReqVO saveReqVO = new MdmItemSaveReqVO();
            saveReqVO.setCode(importVO.getCode());
            saveReqVO.setName(importVO.getName());
            saveReqVO.setBarCode(importVO.getBarCode());
            saveReqVO.setSpecification(importVO.getSpecification());
            saveReqVO.setCategoryId(categoryId);
            saveReqVO.setUnitMeasureId(unitMeasureId);
            saveReqVO.setUnitMeasureId2(unitMeasureId2);
            saveReqVO.setUnitMeasureRate1(importVO.getUnitMeasureRate1());
            saveReqVO.setUnitMeasureId3(unitMeasureId3);
            saveReqVO.setUnitMeasureRate2(importVO.getUnitMeasureRate2());
            saveReqVO.setPurchasePrice(importVO.getPurchasePrice());
            saveReqVO.setSalesPrice(importVO.getSalesPrice());
            saveReqVO.setCostPrice(importVO.getCostPrice());
            saveReqVO.setSafetyStock(importVO.getSafetyStock());
            saveReqVO.setMinStock(importVO.getMinStock());
            saveReqVO.setMaxStock(importVO.getMaxStock());
            saveReqVO.setIsBatchManaged(importVO.getIsBatchManaged());
            saveReqVO.setExpiryDay(importVO.getExpiryDay());
            saveReqVO.setStatus(importVO.getStatus() != null ? importVO.getStatus() : cn.iocoder.yudao.framework.common.enums.CommonStatusEnum.ENABLE.getStatus());
            saveReqVO.setSyncMes(importVO.getSyncMes() != null ? importVO.getSyncMes() : false);
            saveReqVO.setRemark(importVO.getRemark());
            saveReqVO.setItemType(1); // 默认物料类型:物料
            // 2.7 已存在(按编码判断)
            MdmItemDO existByCode = itemMapper.selectByCode(importVO.getCode());
            try {
                if (existByCode != null) {
                    if (!Boolean.TRUE.equals(isUpdateSupport)) {
                        respVO.getFailureList().put(importVO.getName(), "物料编码已存在");
                        continue;
                    }
                    saveReqVO.setId(existByCode.getId());
                    updateItem(saveReqVO);
                    respVO.getUpdateList().add(importVO.getName());
                } else {
                    createItem(saveReqVO);
                    respVO.getCreateList().add(importVO.getName());
                }
            } catch (Exception ex) {
                // 单条失败,记录并继续
                String reason = ex instanceof cn.iocoder.yudao.framework.common.exception.ServiceException
                        ? ((cn.iocoder.yudao.framework.common.exception.ServiceException) ex).getMessage()
                        : ex.getMessage();
                respVO.getFailureList().put(importVO.getName(), reason == null ? "导入失败" : reason);
            }
        }
        return respVO;
    }
}