云
7 小时以前 3edf8c4fc07f9784e2d04ee7a98692bb457749eb
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java
@@ -4,7 +4,17 @@
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;
@@ -12,6 +22,9 @@
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.*;
@@ -19,7 +32,7 @@
/**
 * MES 工艺路线产品 Service 实现类
 *
 * @author 芋道源码
 * @author 超级管理员
 */
@Service
@Validated
@@ -34,13 +47,25 @@
    @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 校验产品唯一性(一个产品只能关联一条工艺路线)
        // 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);
@@ -53,9 +78,16 @@
        // 1.0 已启用的工艺路线,不允许操作
        routeService.validateRouteNotEnable(updateReqVO.getRouteId());
        // 1.1 校验存在
        validateRouteProductExists(updateReqVO.getId());
        // 1.2 校验产品唯一性
        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);
@@ -89,6 +121,44 @@
        }
    }
    private void validateProductItem(Long itemId) {
        MesMdItemDO item = itemService.validateItemExistsAndEnable(itemId);
        Long typeId = item.getItemTypeId();
        Set<Long> 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<MdmItemRespDTO> 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) {