云
2026-10-26 63ba72f5d65a3a4dcd56d9228582042448af61d7
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemTypeServiceImpl.java
@@ -4,16 +4,16 @@
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mdm.api.category.MdmItemCategoryApi;
import cn.iocoder.yudao.module.mdm.api.category.dto.MdmItemCategoryRespDTO;
import cn.iocoder.yudao.module.mdm.api.category.dto.MdmItemCategorySaveReqDTO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeListReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.type.MesMdItemTypeSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemTypeMapper;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -29,8 +29,8 @@
/**
 * MES 物料产品分类 Service 实现类
 *
 * 兼容层实现:数据存储在 mes_md_item_type 表,但优先从 MDM 分类 API 获取
 * 实际分类数据已迁移到 mdm_item_category 表
 * 物料分类已统一由 MDM 管理,数据存储在 mdm_item_category 表。
 * 本服务保留 MES 的分类接口,底层全部通过 {@link MdmItemCategoryApi} 读写 MDM 分类。
 *
 * @author 超级管理员
 */
@@ -38,73 +38,82 @@
@Validated
public class MesMdItemTypeServiceImpl implements MesMdItemTypeService {
    @Resource
    private MdmItemCategoryApi mdmItemCategoryApi;
    /** MDM 物料类型:物料 */
    private static final Integer MDM_ITEM_TYPE_MATERIAL = 1;
    /** MDM 物料类型:产品 */
    private static final Integer MDM_ITEM_TYPE_PRODUCT = 2;
    /** MES 物料/产品标识:产品 */
    private static final String ITEM_OR_PRODUCT_PRODUCT = "PRODUCT";
    @Resource
    private MesMdItemTypeMapper itemTypeMapper;
    private MdmItemCategoryApi mdmItemCategoryApi;
    @Resource
    @Lazy
    private MesMdItemService itemService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createItemType(MesMdItemTypeSaveReqVO createReqVO) {
        // 校验数据
        validateItemTypeSaveData(createReqVO);
        // 插入到本地表(保持兼容)
        MesMdItemTypeDO itemType = BeanUtils.toBean(createReqVO, MesMdItemTypeDO.class);
        itemTypeMapper.insert(itemType);
        return itemType.getId();
        // 校验父分类
        validateParentItemType(createReqVO.getId(), createReqVO.getParentId());
        // 写入 MDM 分类表
        CommonResult<Long> result = mdmItemCategoryApi.createItemCategory(buildSaveDTO(createReqVO));
        return result.getData();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateItemType(MesMdItemTypeSaveReqVO updateReqVO) {
        // 校验存在
        validateItemTypeExists(updateReqVO.getId());
        // 校验数据
        validateItemTypeSaveData(updateReqVO);
        // 更新本地表
        MesMdItemTypeDO updateObj = BeanUtils.toBean(updateReqVO, MesMdItemTypeDO.class);
        itemTypeMapper.updateById(updateObj);
        // 校验父分类
        validateParentItemType(updateReqVO.getId(), updateReqVO.getParentId());
        // 更新 MDM 分类表
        MdmItemCategorySaveReqDTO updateDTO = buildSaveDTO(updateReqVO);
        updateDTO.setId(updateReqVO.getId());
        mdmItemCategoryApi.updateItemCategory(updateDTO);
    }
    private void validateItemTypeSaveData(MesMdItemTypeSaveReqVO reqVO) {
        // 校验父分类编号的有效性
        validateParentItemType(reqVO.getId(), reqVO.getParentId());
        // 校验分类名称的唯一性
        validateItemTypeNameUnique(reqVO.getId(), reqVO.getParentId(), reqVO.getName());
        // 校验分类编码的唯一性
        validateItemTypeCodeUnique(reqVO.getId(), reqVO.getParentId(), reqVO.getCode());
    private MdmItemCategorySaveReqDTO buildSaveDTO(MesMdItemTypeSaveReqVO reqVO) {
        MdmItemCategorySaveReqDTO dto = new MdmItemCategorySaveReqDTO();
        dto.setParentId(ObjUtil.defaultIfNull(reqVO.getParentId(), MesMdItemTypeDO.PARENT_ID_ROOT));
        dto.setCode(reqVO.getCode());
        dto.setName(reqVO.getName());
        dto.setItemOrProduct(reqVO.getItemOrProduct());
        dto.setItemType(convertMdmItemType(reqVO.getItemOrProduct()));
        dto.setSort(ObjUtil.defaultIfNull(reqVO.getSort(), 0));
        dto.setStatus(ObjUtil.defaultIfNull(reqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus()));
        dto.setRemark(reqVO.getRemark());
        return dto;
    }
    /**
     * MES 物料/产品标识 -> MDM 物料类型
     */
    private Integer convertMdmItemType(String itemOrProduct) {
        if (StrUtil.isBlank(itemOrProduct)) {
            return null;
        }
        return ITEM_OR_PRODUCT_PRODUCT.equalsIgnoreCase(itemOrProduct)
                ? MDM_ITEM_TYPE_PRODUCT : MDM_ITEM_TYPE_MATERIAL;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteItemType(Long id) {
        // 1.1 校验存在
        validateItemTypeExists(id);
        // 1.2 校验是否有子分类
        if (itemTypeMapper.selectCountByParentId(id) > 0) {
            throw exception(MD_ITEM_TYPE_EXITS_CHILDREN);
        }
        // 1.3 校验是否有物料
        // 1.2 校验是否有物料
        if (itemService.getItemCountByItemTypeId(id) > 0) {
            throw exception(MD_ITEM_TYPE_EXITS_ITEM);
        }
        // 2. 删除
        itemTypeMapper.deleteById(id);
        // 2. 删除(子分类、MDM 物料引用由 MDM 侧校验)
        mdmItemCategoryApi.deleteItemCategory(id);
    }
    private void validateItemTypeExists(Long id) {
        // 优先从 MDM 获取
        CommonResult<MdmItemCategoryRespDTO> result = mdmItemCategoryApi.getItemCategory(id);
        if (result.isSuccess() && result.getData() != null) {
            return;
        }
        // 回退到本地表
        if (itemTypeMapper.selectById(id) == null) {
        if (getItemType(id) == null) {
            throw exception(MD_ITEM_TYPE_NOT_EXISTS);
        }
    }
@@ -117,14 +126,8 @@
        if (Objects.equals(id, parentId)) {
            throw exception(MD_ITEM_TYPE_PARENT_ERROR);
        }
        // 2. 父分类不存在 - 优先从 MDM 获取
        CommonResult<MdmItemCategoryRespDTO> result = mdmItemCategoryApi.getItemCategory(parentId);
        MesMdItemTypeDO parentItemType = null;
        if (result.isSuccess() && result.getData() != null) {
            parentItemType = convertToMesDO(result.getData());
        } else {
            parentItemType = itemTypeMapper.selectById(parentId);
        }
        // 2. 父分类不存在
        MesMdItemTypeDO parentItemType = getItemType(parentId);
        if (parentItemType == null) {
            throw exception(MD_ITEM_TYPE_PARENT_NOT_EXITS);
        }
@@ -140,60 +143,35 @@
            if (parentId == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(parentId)) {
                break;
            }
            // 继续查找父级
            CommonResult<MdmItemCategoryRespDTO> parentResult = mdmItemCategoryApi.getItemCategory(parentId);
            if (parentResult.isSuccess() && parentResult.getData() != null) {
                parentItemType = convertToMesDO(parentResult.getData());
            } else {
                parentItemType = itemTypeMapper.selectById(parentId);
            }
            parentItemType = getItemType(parentId);
            if (parentItemType == null) {
                break;
            }
        }
    }
    private void validateItemTypeNameUnique(Long id, Long parentId, String name) {
        MesMdItemTypeDO itemType = itemTypeMapper.selectByParentIdAndName(parentId, name);
        if (itemType == null) {
            return;
        }
        if (ObjUtil.notEqual(itemType.getId(), id)) {
            throw exception(MD_ITEM_TYPE_NAME_DUPLICATE);
        }
    }
    private void validateItemTypeCodeUnique(Long id, Long parentId, String code) {
        MesMdItemTypeDO itemType = itemTypeMapper.selectByParentIdAndCode(parentId, code);
        if (itemType == null) {
            return;
        }
        if (ObjUtil.notEqual(itemType.getId(), id)) {
            throw exception(MD_ITEM_TYPE_CODE_DUPLICATE);
        }
    }
    @Override
    public MesMdItemTypeDO getItemType(Long id) {
        // 优先从 MDM 获取
        CommonResult<MdmItemCategoryRespDTO> result = mdmItemCategoryApi.getItemCategory(id);
        if (result.isSuccess() && result.getData() != null) {
            return convertToMesDO(result.getData());
        }
        // 回退到本地表
        return itemTypeMapper.selectById(id);
        return result.isSuccess() ? convertToMesDO(result.getData()) : null;
    }
    @Override
    public MesMdItemTypeDO getItemTypeByCode(String code) {
        // 从本地表查
        return itemTypeMapper.selectByCode(code);
        CommonResult<MdmItemCategoryRespDTO> result = mdmItemCategoryApi.getItemCategoryByCode(code);
        return result.isSuccess() ? convertToMesDO(result.getData()) : null;
    }
    @Override
    public List<MesMdItemTypeDO> getItemTypeList(MesMdItemTypeListReqVO listReqVO) {
        // 从本地表查
        return itemTypeMapper.selectList(listReqVO);
        List<MesMdItemTypeDO> list = getAllItemTypeList();
        return list.stream()
                .filter(itemType -> StrUtil.isBlank(listReqVO.getName())
                        || StrUtil.contains(itemType.getName(), listReqVO.getName()))
                .filter(itemType -> listReqVO.getStatus() == null
                        || Objects.equals(listReqVO.getStatus(), itemType.getStatus()))
                .sorted(Comparator.comparing(itemType -> ObjUtil.defaultIfNull(itemType.getSort(), 0)))
                .toList();
    }
    @Override
@@ -201,12 +179,25 @@
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return itemTypeMapper.selectByIds(ids);
        CommonResult<List<MdmItemCategoryRespDTO>> result = mdmItemCategoryApi.getItemCategoryList(ids);
        return convertToMesDOList(result.isSuccess() ? result.getData() : null);
    }
    private List<MesMdItemTypeDO> getAllItemTypeList() {
        CommonResult<List<MdmItemCategoryRespDTO>> result = mdmItemCategoryApi.getItemCategoryAllList();
        return convertToMesDOList(result.isSuccess() ? result.getData() : null);
    }
    private List<MesMdItemTypeDO> convertToMesDOList(List<MdmItemCategoryRespDTO> list) {
        if (CollUtil.isEmpty(list)) {
            return new ArrayList<>();
        }
        return new ArrayList<>(list.stream().map(this::convertToMesDO).toList());
    }
    @Override
    public List<MesMdItemTypeDO> getItemTypeChildrenList(Long parentId) {
        List<MesMdItemTypeDO> allList = itemTypeMapper.selectList();
        List<MesMdItemTypeDO> allList = getAllItemTypeList();
        List<MesMdItemTypeDO> result = new ArrayList<>();
        collectItemTypeChildren(result, parentId, allList);
        return result;
@@ -225,6 +216,9 @@
     * 转换 MDM 分类 DTO 为 MES DO
     */
    private MesMdItemTypeDO convertToMesDO(MdmItemCategoryRespDTO dto) {
        if (dto == null) {
            return null;
        }
        MesMdItemTypeDO mesDO = new MesMdItemTypeDO();
        mesDO.setId(dto.getId());
        mesDO.setCode(dto.getCode());
@@ -233,6 +227,8 @@
        mesDO.setItemOrProduct(dto.getItemOrProduct());
        mesDO.setSort(dto.getSort());
        mesDO.setStatus(dto.getStatus());
        mesDO.setRemark(dto.getRemark());
        mesDO.setCreateTime(dto.getCreateTime());
        return mesDO;
    }
@@ -249,6 +245,7 @@
        MesMdItemTypeImportRespVO respVO = MesMdItemTypeImportRespVO.builder()
                .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>())
                .failureCodes(new LinkedHashMap<>()).build();
        List<MesMdItemTypeDO> allList = getAllItemTypeList();
        AtomicInteger index = new AtomicInteger(1);
        importItemTypeList.forEach(importItem -> {
            int currentIndex = index.getAndIncrement();
@@ -268,7 +265,9 @@
            // 2.1 解析父分类(为空则默认为根分类)
            Long parentId = MesMdItemTypeDO.PARENT_ID_ROOT;
            if (StrUtil.isNotBlank(importItem.getParentCode())) {
                MesMdItemTypeDO parentItemType = itemTypeMapper.selectByCode(importItem.getParentCode());
                MesMdItemTypeDO parentItemType = allList.stream()
                        .filter(itemType -> Objects.equals(importItem.getParentCode(), itemType.getCode()))
                        .findFirst().orElse(null);
                if (parentItemType == null) {
                    respVO.getFailureCodes().put(key, "父分类编码[" + importItem.getParentCode() + "]不存在");
                    return;
@@ -276,25 +275,47 @@
                parentId = parentItemType.getId();
            }
            // 2.2 判断是否存在(同一父分类下编码唯一)
            MesMdItemTypeDO existItemType = itemTypeMapper.selectByParentIdAndCode(parentId, importItem.getCode());
            Long finalParentId = parentId;
            MesMdItemTypeDO existItemType = allList.stream()
                    .filter(itemType -> Objects.equals(finalParentId, itemType.getParentId())
                            && Objects.equals(importItem.getCode(), itemType.getCode()))
                    .findFirst().orElse(null);
            try {
                MdmItemCategorySaveReqDTO dto = new MdmItemCategorySaveReqDTO();
                dto.setParentId(parentId);
                dto.setCode(importItem.getCode());
                dto.setName(importItem.getName());
                dto.setItemOrProduct(importItem.getItemOrProduct());
                dto.setItemType(convertMdmItemType(importItem.getItemOrProduct()));
                dto.setSort(ObjUtil.defaultIfNull(importItem.getSort(), 0));
                dto.setStatus(ObjUtil.defaultIfNull(importItem.getStatus(), CommonStatusEnum.ENABLE.getStatus()));
                dto.setRemark(importItem.getRemark());
            if (existItemType == null) {
                // 2.2.1 新增
                MesMdItemTypeDO itemType = BeanUtils.toBean(importItem, MesMdItemTypeDO.class);
                itemType.setParentId(parentId);
                itemType.setSort(ObjUtil.defaultIfNull(importItem.getSort(), 0));
                itemType.setStatus(ObjUtil.defaultIfNull(importItem.getStatus(), CommonStatusEnum.ENABLE.getStatus()));
                itemTypeMapper.insert(itemType);
                    dto.setId(null);
                    Long id = mdmItemCategoryApi.createItemCategory(dto).getData();
                    MesMdItemTypeDO created = new MesMdItemTypeDO();
                    created.setId(id);
                    created.setParentId(parentId);
                    created.setCode(importItem.getCode());
                    created.setName(importItem.getName());
                    created.setItemOrProduct(importItem.getItemOrProduct());
                    created.setSort(dto.getSort());
                    created.setStatus(dto.getStatus());
                    created.setRemark(importItem.getRemark());
                    allList.add(created);
                respVO.getCreateCodes().add(importItem.getCode());
            } else if (updateSupport) {
                // 2.2.2 更新
                MesMdItemTypeDO updateObj = BeanUtils.toBean(importItem, MesMdItemTypeDO.class);
                updateObj.setId(existItemType.getId());
                updateObj.setParentId(parentId);
                itemTypeMapper.updateById(updateObj);
                    dto.setId(existItemType.getId());
                    mdmItemCategoryApi.updateItemCategory(dto);
                respVO.getUpdateCodes().add(importItem.getCode());
            } else {
                respVO.getFailureCodes().put(key, "分类编码已存在");
            }
            } catch (ServiceException e) {
                respVO.getFailureCodes().put(key, e.getMessage());
            }
        });
        return respVO;
    }