gongchunyi
2026-08-22 34eca433a02bee81572159c946fc7a6b08b448b2
src/main/java/com/ruoyi/production/service/impl/ProductionOrderServiceImpl.java
@@ -556,6 +556,40 @@
        return value == null ? BigDecimal.ZERO : value;
    }
    /**
     * 批次号按前缀日期升序排序(格式如 20260703-ed01-001,日期早的在前)
     */
    private void sortBatchNoList(List<String> batchNoList) {
        if (batchNoList == null || batchNoList.size() < 2) {
            return;
        }
        batchNoList.sort(Comparator
                .comparingLong(this::batchDateKey)
                .thenComparing(batchNo -> batchNo == null ? "" : batchNo));
    }
    /**
     * 提取批次号开头的数字作为排序键(yyyyMMdd),无数字时放到最后
     */
    private long batchDateKey(String batchNo) {
        if (batchNo == null) {
            return Long.MAX_VALUE;
        }
        String trimmed = batchNo.trim();
        int end = 0;
        while (end < trimmed.length() && Character.isDigit(trimmed.charAt(end))) {
            end++;
        }
        if (end == 0) {
            return Long.MAX_VALUE;
        }
        try {
            return Long.parseLong(trimmed.substring(0, end));
        } catch (NumberFormatException e) {
            return Long.MAX_VALUE;
        }
    }
    private void validateAndFillOrder(ProductionOrder productionOrder, ProductionOrder oldOrder) {
        // 校验订单参数并补齐默认值
        if (productionOrder == null) {
@@ -1107,9 +1141,30 @@
            }
        }
        // 过滤出非根节点(实际领料项)
        // 收集订单成品规格ID:包含生产订单成品规格与BOM根节点规格,用于排除“自己”这一物料
        Set<Long> finishedProductModelIds = new HashSet<>();
        if (productionOrder != null && productionOrder.getProductModelId() != null) {
            finishedProductModelIds.add(productionOrder.getProductModelId());
        }
        bomStructureList.stream()
                .filter(s -> s != null && (s.getParentId() == null || s.getParentId() == 0))
                .map(ProductionBomStructureVo::getProductModelId)
                .filter(Objects::nonNull)
                .forEach(finishedProductModelIds::add);
        // 收集所有被引用为父级的节点ID:BOM从最后一道工序往里配置前一道工序,
        // 中间节点是上一道工序的产出(自制),只有每条分支最里层的叶子节点才是真正需要领用的物料。
        Set<Long> parentNodeIds = bomStructureList.stream()
                .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0)
                .map(ProductionBomStructureVo::getParentId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        // 过滤出叶子节点(实际领料项):非根节点、非中间节点,并排除与订单成品同规格的物料(自制成品无需领料)
        List<ProductionBomStructureVo> childStructureList = bomStructureList.stream()
                .filter(s -> s != null && s.getParentId() != null && s.getParentId() != 0)
                .filter(s -> s.getId() == null || !parentNodeIds.contains(s.getId()))
                .filter(s -> s.getProductModelId() == null || !finishedProductModelIds.contains(s.getProductModelId()))
                .collect(Collectors.toList());
        // 遍历处理数据并组装结果
@@ -1158,6 +1213,7 @@
                List<String> batchNoList = stockBatchNoMap.get(productModelId) == null
                        ? Collections.emptyList()
                        : new ArrayList<>(stockBatchNoMap.get(productModelId));
                sortBatchNoList(batchNoList);
                vo.setBatchNoList(batchNoList);
                vo.setStockQuantity(stockQuantityMap.getOrDefault(productModelId, BigDecimal.ZERO));
                vo.setBom(true);