package cn.iocoder.yudao.module.mes.service.pro.route; import cn.hutool.core.util.ObjUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductMapper; import cn.iocoder.yudao.module.mes.enums.md.MesMdItemTypeEnum; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; import cn.iocoder.yudao.module.mdm.enums.MdmItemTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; 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.List; import java.util.HashSet; import java.util.Set; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * MES 工艺路线产品 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class MesProRouteProductServiceImpl implements MesProRouteProductService { @Resource private MesProRouteProductMapper routeProductMapper; @Resource @Lazy private MesProRouteService routeService; @Resource @Lazy private MesProRouteProductBomService routeProductBomService; @Resource private MesMdItemService itemService; @Resource private MesMdItemTypeService itemTypeService; @Resource private MesWmWarehouseAreaService warehouseAreaService; @Resource private MdmItemApi mdmItemApi; @Override public Long createRouteProduct(MesProRouteProductSaveReqVO createReqVO) { // 1.0 已启用的工艺路线,不允许操作 routeService.validateRouteNotEnable(createReqVO.getRouteId()); // 1.1 校验产品存在、启用且类型正确 validateProductItem(createReqVO.getItemId()); // 1.2 校验产品唯一性(一个产品只能关联一条工艺路线) validateItemUnique(null, createReqVO.getItemId()); warehouseAreaService.validateWarehouseAreaExists(createReqVO.getWarehouseId(), createReqVO.getLocationId(), createReqVO.getAreaId()); // 2. 插入 MesProRouteProductDO routeProduct = BeanUtils.toBean(createReqVO, MesProRouteProductDO.class); routeProductMapper.insert(routeProduct); return routeProduct.getId(); } @Override public void updateRouteProduct(MesProRouteProductSaveReqVO updateReqVO) { // 1.0 已启用的工艺路线,不允许操作 routeService.validateRouteNotEnable(updateReqVO.getRouteId()); // 1.1 校验存在 MesProRouteProductDO existing = routeProductMapper.selectById(updateReqVO.getId()); validateRouteProductExists(existing); // 历史路线产品可能采用旧分类;只修改仓位等配置时不重新拦截原有物料。 if (ObjUtil.notEqual(existing.getItemId(), updateReqVO.getItemId())) { validateProductItem(updateReqVO.getItemId()); } // 1.3 校验产品唯一性 validateItemUnique(updateReqVO.getId(), updateReqVO.getItemId()); warehouseAreaService.validateWarehouseAreaExists(updateReqVO.getWarehouseId(), updateReqVO.getLocationId(), updateReqVO.getAreaId()); // 2. 更新 MesProRouteProductDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProductDO.class); routeProductMapper.updateById(updateObj); } @Override @Transactional(rollbackFor = Exception.class) public void deleteRouteProduct(Long id) { // 1.1 校验存在 MesProRouteProductDO routeProduct = routeProductMapper.selectById(id); validateRouteProductExists(routeProduct); // 1.2 已启用的工艺路线,不允许操作 routeService.validateRouteNotEnable(routeProduct.getRouteId()); // 2.1 级联删除关联的 BOM routeProductBomService.deleteRouteProductBomByRouteIdAndProductId(routeProduct.getRouteId(), routeProduct.getItemId()); // 2.2 删除产品关联 routeProductMapper.deleteById(id); } private void validateRouteProductExists(Long id) { if (routeProductMapper.selectById(id) == null) { throw exception(PRO_ROUTE_PRODUCT_NOT_EXISTS); } } private void validateRouteProductExists(MesProRouteProductDO routeProduct) { if (routeProduct == null) { throw exception(PRO_ROUTE_PRODUCT_NOT_EXISTS); } } private void validateProductItem(Long itemId) { MesMdItemDO item = itemService.validateItemExistsAndEnable(itemId); Long typeId = item.getItemTypeId(); Set visited = new HashSet<>(); while (typeId != null && !MesMdItemTypeDO.PARENT_ID_ROOT.equals(typeId) && visited.add(typeId)) { MesMdItemTypeDO itemType = itemTypeService.getItemType(typeId); if (itemType == null) { break; } // 兼容旧分类:可能未设置 PRODUCT 标识,也未采用 FG/SF 编码。 if (MesMdItemTypeEnum.isProduct(itemType.getItemOrProduct()) || "FG".equalsIgnoreCase(itemType.getCode()) || "SF".equalsIgnoreCase(itemType.getCode()) || isProductCategoryName(itemType.getName())) { return; } typeId = itemType.getParentId(); } // MES 兼容分类可能仍标记为 ITEM;以 MDM 物料主数据的成品/半成品类型兜底。 CommonResult mdmResult = mdmItemApi.getItemByCode(item.getCode()); if (mdmResult != null && mdmResult.isSuccess() && mdmResult.getData() != null) { MdmItemRespDTO mdmItem = mdmResult.getData(); Integer mdmType = mdmItem.getItemType(); // 历史同步曾将分类 ID 写入 itemType;与 categoryId 相同的值不能当作类型枚举。 if (!Objects.equals(mdmItem.getCategoryId(), mdmType == null ? null : mdmType.longValue()) && (MdmItemTypeEnum.PRODUCT.getType().equals(mdmType) || MdmItemTypeEnum.SEMI_FINISHED.getType().equals(mdmType))) { return; } } throw exception(PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID, item.getName()); } private boolean isProductCategoryName(String name) { return name != null && (name.contains("半成品") || name.contains("产成品") || (name.contains("成品") && !name.contains("非成品"))); } private void validateItemUnique(Long id, Long itemId) { MesProRouteProductDO existing = routeProductMapper.selectByItemId(itemId); if (existing == null) { return; } if (ObjUtil.notEqual(existing.getId(), id)) { throw exception(PRO_ROUTE_PRODUCT_ITEM_DUPLICATE); } } @Override public MesProRouteProductDO getRouteProduct(Long id) { return routeProductMapper.selectById(id); } @Override public MesProRouteProductDO getRouteProductByItemId(Long itemId) { return routeProductMapper.selectByItemId(itemId); } @Override public List getRouteProductListByRouteId(Long routeId) { return routeProductMapper.selectListByRouteId(routeId); } @Override public void deleteRouteProductByRouteId(Long routeId) { routeProductMapper.deleteByRouteId(routeId); } }