package cn.iocoder.yudao.module.mes.service.md.item; import cn.hutool.core.collection.CollUtil; 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.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 jakarta.annotation.Resource; 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.concurrent.atomic.AtomicInteger; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 物料产品分类 Service 实现类 * * 物料分类已统一由 MDM 管理,数据存储在 mdm_item_category 表。 * 本服务保留 MES 的分类接口,底层全部通过 {@link MdmItemCategoryApi} 读写 MDM 分类。 * * @author 超级管理员 */ @Service @Validated public class MesMdItemTypeServiceImpl implements MesMdItemTypeService { /** 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 MdmItemCategoryApi mdmItemCategoryApi; @Resource @Lazy private MesMdItemService itemService; @Override @Transactional(rollbackFor = Exception.class) public Long createItemType(MesMdItemTypeSaveReqVO createReqVO) { // 校验父分类 validateParentItemType(createReqVO.getId(), createReqVO.getParentId()); // 写入 MDM 分类表 CommonResult result = mdmItemCategoryApi.createItemCategory(buildSaveDTO(createReqVO)); return result.getData(); } @Override @Transactional(rollbackFor = Exception.class) public void updateItemType(MesMdItemTypeSaveReqVO updateReqVO) { // 校验存在 validateItemTypeExists(updateReqVO.getId()); // 校验父分类 validateParentItemType(updateReqVO.getId(), updateReqVO.getParentId()); // 更新 MDM 分类表 MdmItemCategorySaveReqDTO updateDTO = buildSaveDTO(updateReqVO); updateDTO.setId(updateReqVO.getId()); mdmItemCategoryApi.updateItemCategory(updateDTO); } 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 (itemService.getItemCountByItemTypeId(id) > 0) { throw exception(MD_ITEM_TYPE_EXITS_ITEM); } // 2. 删除(子分类、MDM 物料引用由 MDM 侧校验) mdmItemCategoryApi.deleteItemCategory(id); } private void validateItemTypeExists(Long id) { if (getItemType(id) == null) { throw exception(MD_ITEM_TYPE_NOT_EXISTS); } } private void validateParentItemType(Long id, Long parentId) { if (parentId == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(parentId)) { return; } // 1. 不能设置自己为父分类 if (Objects.equals(id, parentId)) { throw exception(MD_ITEM_TYPE_PARENT_ERROR); } // 2. 父分类不存在 MesMdItemTypeDO parentItemType = getItemType(parentId); if (parentItemType == null) { throw exception(MD_ITEM_TYPE_PARENT_NOT_EXITS); } // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路 if (id == null) { return; } for (int i = 0; i < Short.MAX_VALUE; i++) { parentId = parentItemType.getParentId(); if (Objects.equals(id, parentId)) { throw exception(MD_ITEM_TYPE_PARENT_IS_CHILD); } if (parentId == null || MesMdItemTypeDO.PARENT_ID_ROOT.equals(parentId)) { break; } parentItemType = getItemType(parentId); if (parentItemType == null) { break; } } } @Override public MesMdItemTypeDO getItemType(Long id) { CommonResult result = mdmItemCategoryApi.getItemCategory(id); return result.isSuccess() ? convertToMesDO(result.getData()) : null; } @Override public MesMdItemTypeDO getItemTypeByCode(String code) { CommonResult result = mdmItemCategoryApi.getItemCategoryByCode(code); return result.isSuccess() ? convertToMesDO(result.getData()) : null; } @Override public List getItemTypeList(MesMdItemTypeListReqVO listReqVO) { List 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 public List getItemTypeList(Collection ids) { if (CollUtil.isEmpty(ids)) { return Collections.emptyList(); } CommonResult> result = mdmItemCategoryApi.getItemCategoryList(ids); return convertToMesDOList(result.isSuccess() ? result.getData() : null); } private List getAllItemTypeList() { CommonResult> result = mdmItemCategoryApi.getItemCategoryAllList(); return convertToMesDOList(result.isSuccess() ? result.getData() : null); } private List convertToMesDOList(List list) { if (CollUtil.isEmpty(list)) { return new ArrayList<>(); } return new ArrayList<>(list.stream().map(this::convertToMesDO).toList()); } @Override public List getItemTypeChildrenList(Long parentId) { List allList = getAllItemTypeList(); List result = new ArrayList<>(); collectItemTypeChildren(result, parentId, allList); return result; } private void collectItemTypeChildren(List result, Long parentId, List allList) { for (MesMdItemTypeDO itemType : allList) { if (Objects.equals(itemType.getParentId(), parentId)) { result.add(itemType); collectItemTypeChildren(result, itemType.getId(), allList); } } } /** * 转换 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()); mesDO.setName(dto.getName()); mesDO.setParentId(dto.getParentId()); mesDO.setItemOrProduct(dto.getItemOrProduct()); mesDO.setSort(dto.getSort()); mesDO.setStatus(dto.getStatus()); mesDO.setRemark(dto.getRemark()); mesDO.setCreateTime(dto.getCreateTime()); return mesDO; } @Override @Transactional(rollbackFor = Exception.class) public MesMdItemTypeImportRespVO importItemTypeList(List importItemTypeList, boolean updateSupport) { // 1. 校验非空 if (CollUtil.isEmpty(importItemTypeList)) { throw exception(MD_ITEM_TYPE_IMPORT_LIST_IS_EMPTY); } // 2. 逐行处理 MesMdItemTypeImportRespVO respVO = MesMdItemTypeImportRespVO.builder() .createCodes(new ArrayList<>()).updateCodes(new ArrayList<>()) .failureCodes(new LinkedHashMap<>()).build(); List allList = getAllItemTypeList(); AtomicInteger index = new AtomicInteger(1); importItemTypeList.forEach(importItem -> { int currentIndex = index.getAndIncrement(); String key = StrUtil.blankToDefault(importItem.getCode(), "第 " + currentIndex + " 行"); if (StrUtil.isBlank(importItem.getCode())) { respVO.getFailureCodes().put(key, "分类编码不能为空"); return; } if (StrUtil.isBlank(importItem.getName())) { respVO.getFailureCodes().put(key, "分类名称不能为空"); return; } if (StrUtil.isBlank(importItem.getItemOrProduct())) { respVO.getFailureCodes().put(key, "物料/产品标识不能为空"); return; } // 2.1 解析父分类(为空则默认为根分类) Long parentId = MesMdItemTypeDO.PARENT_ID_ROOT; if (StrUtil.isNotBlank(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; } parentId = parentItemType.getId(); } // 2.2 判断是否存在(同一父分类下编码唯一) 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 新增 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 更新 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; } }