src/main/java/com/ruoyi/technology/service/impl/TechnologyRoutingServiceImpl.java
@@ -9,6 +9,8 @@
import com.ruoyi.common.utils.OrderUtils;
import com.ruoyi.technology.bean.dto.TechnologyRoutingDto;
import com.ruoyi.technology.bean.vo.TechnologyRoutingVo;
import com.ruoyi.production.mapper.ProductionOrderRoutingMapper;
import com.ruoyi.production.pojo.ProductionOrderRouting;
import com.ruoyi.technology.mapper.*;
import com.ruoyi.technology.pojo.*;
import com.ruoyi.technology.service.TechnologyRoutingService;
@@ -18,10 +20,9 @@
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@@ -39,6 +40,8 @@
    private final TechnologyOperationParamMapper technologyOperationParamMapper;
    private final TechnologyParamMapper technologyParamMapper;
    private final TechnologyRoutingOperationParamMapper technologyRoutingOperationParamMapper;
    private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
    @Override
    public IPage<TechnologyRoutingVo> pageTechnologyRouting(Page<TechnologyRoutingDto> page, TechnologyRoutingDto technologyRoutingDto) {
@@ -79,6 +82,13 @@
        if (ids == null || ids.isEmpty()) {
            return false;
        }
        // 判断是否有生产订单绑定该工艺路线
        Long count = productionOrderRoutingMapper.selectCount(
                Wrappers.<ProductionOrderRouting>lambdaQuery()
                        .in(ProductionOrderRouting::getTechnologyRoutingId, ids));
        if (count > 0) {
            throw new ServiceException("该工艺路线已绑定生产订单,无法删除!");
        }
        List<TechnologyRoutingOperation> routingOperationList = technologyRoutingOperationMapper.selectList(
                Wrappers.<TechnologyRoutingOperation>lambdaQuery()
                        .in(TechnologyRoutingOperation::getTechnologyRoutingId, ids));
@@ -94,9 +104,10 @@
    /**
     * 按当前选择的 BOM 重建工艺路线工序:
     * 1. 先删除该工艺路线下已有工序
     * 2. 读取 BOM 结构中的 operation_id
     * 3. 按 BOM 出现顺序去重后重新写入工艺路线工序表
     * 1. 先删除该工艺路线下已有工序及参数快照
     * 2. 读取扁平 BOM 明细中的 operation_id
     * 3. 按 BOM 中首次出现的顺序对工序去重,每道工序生成一条路线工序
     * 4. 每条路线工序的产出规格固定为工艺路线的成品规格
     */
    private void syncRoutingOperationsByBom(TechnologyRouting technologyRouting) {
        List<TechnologyRoutingOperation> oldRoutingOperationList = technologyRoutingOperationMapper.selectList(
@@ -122,56 +133,26 @@
            throw new ServiceException("bom产品结构为空!");
        }
        // 同一个 BOM 中可能重复引用相同工序,这里按首次出现顺序去重。
        Map<Long, TechnologyBomStructure> structureById = new HashMap<>();
        // 扁平 BOM 中多个物料可以指向同一道工序,这里只按工序去重
        Set<Long> uniqueOperationIdList = new LinkedHashSet<>();
        for (TechnologyBomStructure bomStructure : bomStructures) {
            if (bomStructure != null && bomStructure.getId() != null) {
                structureById.put(bomStructure.getId(), bomStructure);
            }
        }
        Map<String, TechnologyBomStructure> uniqueOperationMap = new LinkedHashMap<>();
        for (TechnologyBomStructure bomStructure : bomStructures) {
            Long outputProductModelId = resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId());
            uniqueOperationMap.putIfAbsent(buildBomOperationDedupKey(bomStructure, outputProductModelId), bomStructure);
            uniqueOperationIdList.add(bomStructure.getOperationId());
        }
        int dragSort = 1;
        for (TechnologyBomStructure bomStructure : uniqueOperationMap.values()) {
        for (Long operationId : uniqueOperationIdList) {
            TechnologyRoutingOperation routingOperation = new TechnologyRoutingOperation();
            routingOperation.setTechnologyRoutingId(technologyRouting.getId());
            routingOperation.setProductModelId(resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId()));
            routingOperation.setTechnologyOperationId(bomStructure.getOperationId());
            routingOperation.setProductModelId(technologyRouting.getProductModelId());
            routingOperation.setTechnologyOperationId(operationId);
            routingOperation.setDragSort(dragSort++);
            routingOperation.setIsQuality(getOperationQuality(bomStructure.getOperationId()));
            TechnologyOperation technologyOperation = getOperation(operationId);
            routingOperation.setIsQuality(technologyOperation != null ? technologyOperation.getIsQuality() : null);
            routingOperation.setIsProduction(technologyOperation != null ? technologyOperation.getIsProduction() : null);
            routingOperation.setType(technologyOperation != null ? technologyOperation.getType() : null);
            technologyRoutingOperationMapper.insert(routingOperation);
            syncRoutingOperationParams(routingOperation.getId(), bomStructure.getOperationId());
            syncRoutingOperationParams(routingOperation.getId(), operationId);
        }
    }
    private String buildBomOperationDedupKey(TechnologyBomStructure bomStructure, Long outputProductModelId) {
        Long operationId = bomStructure == null ? null : bomStructure.getOperationId();
        Long parentId = bomStructure == null ? null : bomStructure.getParentId();
        return operationId + "#"
                + outputProductModelId + "#"
                + parentId;
    }
    private Long resolveOutputProductModelId(TechnologyBomStructure bomStructure,
                                             Map<Long, TechnologyBomStructure> structureById,
                                             Long routingProductModelId) {
        if (bomStructure == null) {
            return routingProductModelId;
        }
        Long parentId = bomStructure.getParentId();
        if (parentId == null) {
            return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
        }
        TechnologyBomStructure parent = structureById.get(parentId);
        if (parent != null && parent.getProductModelId() != null) {
            return parent.getProductModelId();
        }
        return routingProductModelId != null ? routingProductModelId : bomStructure.getProductModelId();
    }
    private void syncRoutingOperationParams(Long technologyRoutingOperationId, Long technologyOperationId) {
@@ -204,12 +185,11 @@
        }
    }
    /**
     * 质检标识以工序基础表定义为准。
     */
    private Boolean getOperationQuality(Long operationId) {
        TechnologyOperation technologyOperation = technologyOperationMapper.selectById(operationId);
        return technologyOperation != null ? technologyOperation.getIsQuality() : null;
    private TechnologyOperation getOperation(Long operationId) {
        if (operationId == null) {
            return null;
        }
        return technologyOperationMapper.selectById(operationId);
    }
    private String buildProcessRouteCode(Long id) {