From de4ca61aa27d7f284a03aebd801bcc3d6a75bc6a Mon Sep 17 00:00:00 2001
From: huminmin <mac@MacBook-Pro.local>
Date: 星期四, 21 五月 2026 15:03:35 +0800
Subject: [PATCH] 修改生产订单中的工艺路线项目是否生产后,更新生产工单数据

---
 src/main/resources/mapper/production/ProductionOperationTaskMapper.xml                          |    3 
 src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java          |    2 
 src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java          |    3 
 src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java                     |    3 
 src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java |  149 +++++++++++++++++++++++++++--
 src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java             |    2 
 src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java                               |  137 +++++++++++++++++++++++++++
 7 files changed, 283 insertions(+), 16 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java b/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
index 965cbf3..8a75dcd 100644
--- a/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
+++ b/src/main/java/com/ruoyi/production/bean/dto/ProductionOperationTaskDto.java
@@ -44,4 +44,7 @@
     @Schema(description = "缁撴潫鏃ユ湡")
     @DateTimeFormat(pattern = "yyyy-MM-dd")
     private LocalDate endDate;
+
+    @Schema(description = "鏄惁鐢熶骇")
+    private Integer isProduction;
 }
diff --git a/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java b/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
index dda1aa0..2765c10 100644
--- a/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
+++ b/src/main/java/com/ruoyi/production/controller/ProductionOrderRoutingController.java
@@ -39,7 +39,7 @@
     @PostMapping("/updateRouteItem")
     @Operation(summary = "淇敼鐢熶骇璁㈠崟鐨勫伐鑹鸿矾绾胯鎯�")
     public R updateRouteItem(@RequestBody ProductionOrderRoutingOperation productionOrderRoutingOperation) {
-        return R.ok(productionOrderRoutingOperationService.updateById(productionOrderRoutingOperation));
+        return R.ok(productionOrderRoutingOperationService.updateRouteItem(productionOrderRoutingOperation));
     }
 
     @DeleteMapping("/deleteRouteItem/{id}")
diff --git a/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java b/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
index 2377dc3..8878006 100644
--- a/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
+++ b/src/main/java/com/ruoyi/production/service/ProductionOrderRoutingOperationService.java
@@ -8,6 +8,8 @@
 
     R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
 
+    R updateRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
+
     R deleteRouteItem(Long id);
 
     int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation);
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
index 200e95f..40dd74c 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionBomStructureServiceImpl.java
@@ -23,6 +23,7 @@
 import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
 import com.ruoyi.production.pojo.ProductionProductMain;
 import com.ruoyi.production.service.ProductionBomStructureService;
+import com.ruoyi.production.util.TaskPlanQuantityUtil;
 import com.ruoyi.technology.mapper.TechnologyOperationMapper;
 import com.ruoyi.technology.mapper.TechnologyOperationParamMapper;
 import com.ruoyi.technology.mapper.TechnologyParamMapper;
@@ -262,7 +263,7 @@
                 .filter(item -> item != null && item.getId() != null)
                 .collect(Collectors.toMap(ProductionOrderRoutingOperation::getId, item -> item, (left, right) -> left));
         // Keep task plan quantities aligned with the same order BOM snapshot demand used during snapshot creation.
-        Map<String, BigDecimal> demandedQuantityMap = buildOperationDemandedQuantityMap(structureList, rootProductModelId);
+        Map<String, BigDecimal> demandedQuantityMap = TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(structureList, rootProductModelId);
         for (ProductionOperationTask task : taskList) {
             if (task == null || task.getId() == null || task.getProductionOrderRoutingOperationId() == null) {
                 continue;
diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java
index 3d16a07..75d0696 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionOrderRoutingOperationServiceImpl.java
@@ -4,21 +4,15 @@
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.exception.ServiceException;
 import com.ruoyi.framework.web.domain.R;
-import com.ruoyi.production.mapper.ProductionOperationTaskMapper;
-import com.ruoyi.production.mapper.ProductionOrderRoutingOperationMapper;
-import com.ruoyi.production.mapper.ProductionOrderRoutingOperationParamMapper;
-import com.ruoyi.production.mapper.ProductionProductMainMapper;
-import com.ruoyi.production.pojo.ProductionOperationTask;
-import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
-import com.ruoyi.production.pojo.ProductionOrderRoutingOperationParam;
-import com.ruoyi.production.pojo.ProductionProductMain;
+import com.ruoyi.production.mapper.*;
+import com.ruoyi.production.util.TaskPlanQuantityUtil;
+import com.ruoyi.technology.mapper.*;
+import com.ruoyi.production.pojo.*;
 import com.ruoyi.production.service.ProductionOrderRoutingOperationService;
 import com.ruoyi.production.service.ProductionProductMainService;
-import com.ruoyi.technology.mapper.TechnologyOperationParamMapper;
-import com.ruoyi.technology.mapper.TechnologyParamMapper;
-import com.ruoyi.technology.pojo.TechnologyOperationParam;
-import com.ruoyi.technology.pojo.TechnologyParam;
+import com.ruoyi.technology.pojo.*;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -26,8 +20,7 @@
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
 
 @Service
 @Transactional(rollbackFor = Exception.class)
@@ -42,6 +35,11 @@
     private final TechnologyOperationParamMapper technologyOperationParamMapper;
     private final TechnologyParamMapper technologyParamMapper;
     private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
+    private final ProductionOrderMapper productionOrderMapper;
+    private final ProductionOrderRoutingMapper productionOrderRoutingMapper;
+    private final ProductionOrderBomMapper productionOrderBomMapper;
+    private final ProductionBomStructureMapper productionBomStructureMapper;
+    private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
 
     @Override
     public R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) {
@@ -102,6 +100,109 @@
             productionOperationTaskMapper.insert(productionOperationTask);
         }
         return R.ok();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public R updateRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) {
+        Long operationId = productionOrderRoutingOperation.getId();
+
+        // 鏇存柊宸ヨ壓璺嚎宸ュ簭
+        productionOrderRoutingOperationMapper.updateById(productionOrderRoutingOperation);
+
+        // 閲嶆柊鏌ヨ瀹屾暣璁板綍锛堝墠绔彲鑳芥病鏈変紶閫掓墍鏈夊瓧娈碉紝濡� productionOrderId锛�
+        ProductionOrderRoutingOperation updatedOperation = productionOrderRoutingOperationMapper.selectById(operationId);
+        if (updatedOperation == null) {
+            throw new ServiceException("宸ヨ壓璺嚎宸ュ簭涓嶅瓨鍦�");
+        }
+
+        // 鏌ヨ鏄惁瀛樺湪宸ュ崟
+        ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectOne(
+                new LambdaQueryWrapper<ProductionOperationTask>()
+                        .eq(ProductionOperationTask::getProductionOrderRoutingOperationId, operationId)
+                        .last("limit 1"));
+
+        // 鏍规嵁鏄惁闇�瑕佺敓浜ц繘琛屽鐞�
+        Boolean isProduction = updatedOperation.getIsProduction();
+
+        if (Boolean.TRUE.equals(isProduction)) {
+            // 闇�瑕佺敓浜э細妫�鏌ュ伐鍗曟槸鍚﹀瓨鍦紝涓嶅瓨鍦ㄥ垯鐢熸垚
+            if (productionOperationTask == null) {
+                ProductionOperationTask task = new ProductionOperationTask();
+                task.setProductionOrderRoutingOperationId(updatedOperation.getId());
+                task.setProductionOrderId(updatedOperation.getProductionOrderId());
+                // 鑾峰彇鐢熶骇璁㈠崟
+                ProductionOrder productionOrder = productionOrderMapper.selectById(updatedOperation.getProductionOrderId());
+                if (productionOrder == null) {
+                    throw new ServiceException("鐢熶骇璁㈠崟涓嶅瓨鍦�");
+                }
+
+                // 鑾峰彇璁㈠崟BOM
+                ProductionOrderBom orderBom = productionOrderBomMapper.selectOne(
+                        Wrappers.<ProductionOrderBom>lambdaQuery()
+                                .eq(ProductionOrderBom::getProductionOrderId, productionOrder.getId()));
+
+                // 纭畾鏍逛骇鍝佽鏍糏D
+                Long rootProductModelId = orderBom != null && orderBom.getProductModelId() != null
+                        ? orderBom.getProductModelId()
+                        : productionOrder.getProductModelId();
+
+                // 鑾峰彇BOM缁撴瀯鍒楄〃
+                List<ProductionBomStructure> orderBomStructureList = orderBom == null || orderBom.getId() == null
+                        ? Collections.emptyList()
+                        : productionBomStructureMapper.selectList(
+                        Wrappers.<ProductionBomStructure>lambdaQuery()
+                                .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId())
+                                .orderByAsc(ProductionBomStructure::getId));
+
+                // 鏋勫缓宸ュ簭闇�姹傞噺鏄犲皠
+                Map<String, BigDecimal> operationDemandedQuantityMap =
+                        TaskPlanQuantityUtil.buildOperationDemandedQuantityMap(orderBomStructureList, rootProductModelId);
+
+                // 鑾峰彇宸ヨ壓璺嚎宸ュ簭锛堢敤浜庤绠楄鍒掓暟閲忥級
+                TechnologyRoutingOperation sourceOperation = technologyRoutingOperationMapper.selectById(
+                        updatedOperation.getTechnologyRoutingOperationId());
+                // 灏嗗師鏉ョ殑绉佹湁鏂规硶鏇挎崲涓鸿皟鐢ㄥ伐鍏风被
+                BigDecimal planQuantity = TaskPlanQuantityUtil.resolveTaskPlanQuantity(
+                        sourceOperation,
+                        operationDemandedQuantityMap,
+                        productionOrder,
+                        rootProductModelId);
+                task.setPlanQuantity(planQuantity);
+                task.setCompleteQuantity(BigDecimal.ZERO);
+                task.setWorkOrderNo(generateNextTaskNo());
+                task.setStatus(2);
+                productionOperationTaskMapper.insert(task);
+            }
+        } else {
+            // 涓嶉渶瑕佺敓浜э細妫�鏌ュ伐鍗曟槸鍚﹀瓨鍦�
+            if (productionOperationTask != null) {
+                validateTaskCanRemove(productionOperationTask);
+                // 娌℃湁鎶ュ伐锛屽垯鍒犻櫎宸ュ崟
+                productionOperationTaskMapper.deleteById(productionOperationTask.getId());
+            }
+        }
+
+        return R.ok();
+    }
+
+    private void validateTaskCanRemove(ProductionOperationTask task) {
+        if (task == null || task.getId() == null) {
+            return;
+        }
+        if (defaultDecimal(task.getCompleteQuantity()).compareTo(BigDecimal.ZERO) > 0) {
+            throw new ServiceException("宸ュ簭宸蹭骇鐢熸姤宸ヨ褰曪紝鏃犳硶鏍规嵁 BOM 鍙樻洿鍒犻櫎瀵瑰簲宸ュ簭蹇収");
+        }
+        long reportCount = productionProductMainMapper.selectCount(
+                Wrappers.<ProductionProductMain>lambdaQuery()
+                        .eq(ProductionProductMain::getProductionOperationTaskId, task.getId()));
+        if (reportCount > 0) {
+            throw new ServiceException("宸ュ簭宸蹭骇鐢熸姤宸ヨ褰曪紝鏃犳硶鏍规嵁 BOM 鍙樻洿鍒犻櫎瀵瑰簲宸ュ崟");
+        }
+    }
+
+    private BigDecimal defaultDecimal(BigDecimal value) {
+        return value == null ? BigDecimal.ZERO : value;
     }
 
     @Override
@@ -182,4 +283,24 @@
         }
         return 0;
     }
+
+    private String generateNextTaskNo() {
+        // 鐢熸垚涓嬩竴涓敓浜у伐鍗曞彿
+        String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+        String prefix = "GD" + datePrefix;
+        ProductionOperationTask lastTask = productionOperationTaskMapper.selectOne(
+                Wrappers.<ProductionOperationTask>lambdaQuery()
+                        .likeRight(ProductionOperationTask::getWorkOrderNo, prefix)
+                        .orderByDesc(ProductionOperationTask::getWorkOrderNo)
+                        .last("limit 1"));
+        int sequence = 1;
+        if (lastTask != null && lastTask.getWorkOrderNo() != null && lastTask.getWorkOrderNo().startsWith(prefix)) {
+            try {
+                sequence = Integer.parseInt(lastTask.getWorkOrderNo().substring(prefix.length())) + 1;
+            } catch (NumberFormatException ignored) {
+                sequence = 1;
+            }
+        }
+        return prefix + String.format("%03d", sequence);
+    }
 }
diff --git a/src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java b/src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java
new file mode 100644
index 0000000..e82d7e4
--- /dev/null
+++ b/src/main/java/com/ruoyi/production/util/TaskPlanQuantityUtil.java
@@ -0,0 +1,137 @@
+package com.ruoyi.production.util;
+
+import com.ruoyi.production.pojo.ProductionBomStructure;
+import com.ruoyi.production.pojo.ProductionOrder;
+import com.ruoyi.production.pojo.ProductionOrderRoutingOperation;
+import com.ruoyi.technology.pojo.TechnologyRoutingOperation;
+import lombok.experimental.UtilityClass;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 宸ュ崟璁″垝鏁伴噺璁$畻宸ュ叿绫�
+ */
+@UtilityClass
+public class TaskPlanQuantityUtil {
+
+    /**
+     * 璁$畻宸ュ崟璁″垝鏁伴噺锛堜娇鐢� TechnologyRoutingOperation锛�
+     */
+    public BigDecimal resolveTaskPlanQuantity(TechnologyRoutingOperation sourceOperation,
+                                              Map<String, BigDecimal> operationDemandedQuantityMap,
+                                              ProductionOrder productionOrder,
+                                              Long rootProductModelId) {
+        if (sourceOperation == null || operationDemandedQuantityMap == null || operationDemandedQuantityMap.isEmpty()) {
+            return defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
+        }
+        Long outputProductModelId = sourceOperation.getProductModelId() != null
+                ? sourceOperation.getProductModelId()
+                : rootProductModelId;
+        String key = buildOperationDemandedQuantityKey(sourceOperation.getTechnologyOperationId(), outputProductModelId);
+        BigDecimal planQuantity = operationDemandedQuantityMap.get(key);
+        return planQuantity != null ? planQuantity : defaultDecimal(productionOrder == null ? null : productionOrder.getQuantity());
+    }
+
+    /**
+     * 璁$畻宸ュ崟璁″垝鏁伴噺锛堜娇鐢� ProductionOrderRoutingOperation锛�
+     */
+    public BigDecimal resolveTaskPlanQuantity(ProductionOrderRoutingOperation routingOperation,
+                                              Map<String, BigDecimal> demandedQuantityMap,
+                                              BigDecimal orderQuantity,
+                                              Long rootProductModelId) {
+        if (routingOperation == null || demandedQuantityMap == null || demandedQuantityMap.isEmpty()) {
+            return orderQuantity;
+        }
+        Long outputProductModelId = routingOperation.getProductModelId() != null
+                ? routingOperation.getProductModelId()
+                : rootProductModelId;
+        String key = buildOperationDemandedQuantityKey(routingOperation.getTechnologyOperationId(), outputProductModelId);
+        BigDecimal planQuantity = demandedQuantityMap.get(key);
+        return planQuantity != null ? planQuantity : orderQuantity;
+    }
+
+    /**
+     * 鏋勫缓宸ュ簭闇�姹傞噺鏄犲皠琛�
+     */
+    public Map<String, BigDecimal> buildOperationDemandedQuantityMap(List<ProductionBomStructure> bomStructures, Long rootProductModelId) {
+        if (bomStructures == null || bomStructures.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<Long, ProductionBomStructure> structureById = new HashMap<>();
+        for (ProductionBomStructure item : bomStructures) {
+            if (item != null && item.getId() != null) {
+                structureById.put(item.getId(), item);
+            }
+        }
+        Map<String, BigDecimal> demandedQuantityMap = new HashMap<>();
+        Set<String> mergedOutputNodeKeySet = new HashSet<>();
+        for (ProductionBomStructure bomStructure : bomStructures) {
+            if (bomStructure == null || bomStructure.getTechnologyOperationId() == null) {
+                continue;
+            }
+            ProductionBomStructure outputNode = resolveOperationOutputNode(bomStructure, structureById);
+            Long outputProductModelId = resolveOutputProductModelId(outputNode, rootProductModelId);
+            if (outputProductModelId == null) {
+                continue;
+            }
+            String mergedOutputNodeKey = buildOperationOutputNodeKey(bomStructure.getTechnologyOperationId(),
+                    outputNode == null ? null : outputNode.getId(), outputProductModelId);
+            if (!mergedOutputNodeKeySet.add(mergedOutputNodeKey)) {
+                continue;
+            }
+            BigDecimal demandedQuantity = defaultDecimal(outputNode == null ? null : outputNode.getDemandedQuantity());
+            String key = buildOperationDemandedQuantityKey(bomStructure.getTechnologyOperationId(), outputProductModelId);
+            demandedQuantityMap.merge(key, demandedQuantity, BigDecimal::add);
+        }
+        return demandedQuantityMap;
+    }
+
+    /**
+     * 鏋勫缓宸ュ簭闇�姹傞噺key
+     */
+    public String buildOperationDemandedQuantityKey(Long operationId, Long outputProductModelId) {
+        return String.valueOf(operationId) + "#" + String.valueOf(outputProductModelId);
+    }
+
+    /**
+     * 鏋勫缓杈撳嚭鑺傜偣key
+     */
+    public String buildOperationOutputNodeKey(Long operationId, Long outputNodeId, Long outputProductModelId) {
+        return String.valueOf(operationId) + "#" + String.valueOf(outputNodeId) + "#" + String.valueOf(outputProductModelId);
+    }
+
+    /**
+     * 瑙f瀽宸ュ簭杈撳嚭鑺傜偣
+     */
+    public ProductionBomStructure resolveOperationOutputNode(ProductionBomStructure bomStructure,
+                                                             Map<Long, ProductionBomStructure> structureById) {
+        if (bomStructure == null) {
+            return null;
+        }
+        if (bomStructure.getParentId() == null) {
+            return bomStructure;
+        }
+        ProductionBomStructure parent = structureById.get(bomStructure.getParentId());
+        return parent != null ? parent : bomStructure;
+    }
+
+    /**
+     * 瑙f瀽杈撳嚭浜у搧瑙勬牸ID
+     */
+    public Long resolveOutputProductModelId(ProductionBomStructure outputNode, Long rootProductModelId) {
+        if (outputNode == null) {
+            return rootProductModelId;
+        }
+        return outputNode.getProductModelId() != null ? outputNode.getProductModelId() : rootProductModelId;
+    }
+
+    /**
+     * 榛樿BigDecimal鍊�
+     */
+    public BigDecimal defaultDecimal(BigDecimal value) {
+        return value == null ? BigDecimal.ZERO : value;
+    }
+
+}
diff --git a/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml b/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
index f029ef7..199eb21 100644
--- a/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
+++ b/src/main/resources/mapper/production/ProductionOperationTaskMapper.xml
@@ -64,6 +64,9 @@
             <if test="c != null and c.status != null">
                 and pot.status = #{c.status}
             </if>
+            <if test="c != null and c.isProduction != null">
+                and poro.is_production = #{c.isProduction}
+            </if>
             <if test="c != null and c.workOrderNo != null and c.workOrderNo != ''">
                 and pot.work_order_no like concat('%', #{c.workOrderNo}, '%')
             </if>

--
Gitblit v1.9.3