From fe1b02a121c173e9b2d56bbe5a1982e0e69e0548 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期二, 28 四月 2026 18:06:08 +0800
Subject: [PATCH] feat(production): 新增生产工单管理功能 - 创建 ProductionOperationTask 实体类定义工单数据结构 - 实现 ProductionOperationTaskController 提供工单的增删改查接口 - 开发 ProductionOperationTaskService 和实现类处理业务逻辑 - 配置 ProductionOperationTaskMapper 及 XML 文件实现数据库操作 - 添加 ProductionOperationTaskVo 视图对象用于数据展示 - 扩展 ProductionOrder 实体类增加生产订单相关属性 - 更新 ProductionOrderMapper.xml 完善订单查询映射配置 - 优化 ProductionOrderPickRecordMapper.xml 记录物料领取明细 - 新增 ProductionOrderRoutingOperationParam 参数配置实体 - 完善 工序参数处理逻辑

---
 src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java |  173 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 153 insertions(+), 20 deletions(-)

diff --git a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
index 92fd9f2..abbc525 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -19,6 +19,7 @@
 import com.ruoyi.procurementrecord.utils.StockUtils;
 import com.ruoyi.production.bean.dto.ProductStructureDto;
 import com.ruoyi.production.bean.dto.ProductionProductMainDto;
+import com.ruoyi.production.enums.ProductOrderStatusEnum;
 import com.ruoyi.production.mapper.*;
 import com.ruoyi.production.pojo.*;
 import com.ruoyi.production.service.ProductionProductMainService;
@@ -39,8 +40,10 @@
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 @Service
@@ -62,7 +65,10 @@
     private final ProductionAccountMapper productionAccountMapper;
     private final ProductionOperationTaskMapper productionOperationTaskMapper;
     private final ProductionOrderMapper productionOrderMapper;
+    private final ProductionOrderBomMapper productionOrderBomMapper;
+    private final ProductionBomStructureMapper productionBomStructureMapper;
     private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
+    private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
     private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
     private final TechnologyOperationMapper technologyOperationMapper;
     private final StockUtils stockUtils;
@@ -86,7 +92,8 @@
 
     @Override
     public Boolean addProductMain(ProductionProductMainDto dto) {
-        if (dto.getProductionOperationTaskId() == null) {
+        Long taskId = resolveTaskId(dto);
+        if (taskId == null) {
             throw new ServiceException("璇蜂紶鍏ョ敓浜у伐鍗旾D");
         }
         return addProductMainByProductionTask(dto);
@@ -107,12 +114,17 @@
     }
 
     private Boolean addProductMainByProductionTask(ProductionProductMainDto dto) {
+        // 鎶ュ伐浠ヨ鍗曞伐搴忓揩鐓т负鍑嗭紝閬垮厤宸ヨ壓涓绘暟鎹彉鏇村悗褰卞搷鍘嗗彶宸ュ崟鎵ц銆�
+        Long taskId = resolveTaskId(dto);
+        if (taskId == null) {
+            throw new ServiceException("productionOperationTaskId can not be null");
+        }
         SysUser user = userMapper.selectUserById(dto.getUserId());
-        ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(dto.getProductionOperationTaskId());
+        ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(taskId);
         if (productionOperationTask == null) {
             throw new ServiceException("鐢熶骇宸ュ崟涓嶅瓨鍦�");
         }
-        ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getTechnologyRoutingOperationId());
+        ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getProductionOrderRoutingOperationId());
         if (routingOperation == null) {
             throw new ServiceException("璁㈠崟宸ヨ壓璺嚎宸ュ簭涓嶅瓨鍦�");
         }
@@ -120,6 +132,7 @@
         if (productionOrder == null) {
             throw new ServiceException("鐢熶骇璁㈠崟涓嶅瓨鍦�");
         }
+        syncOperationParamInputValue(dto, routingOperation.getId());
         TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId());
         TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null
                 : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId());
@@ -131,19 +144,19 @@
 
         ProductionProductMain productionProductMain = new ProductionProductMain();
         productionProductMain.setProductNo(generateProductNo());
-        productionProductMain.setUserId(dto.getUserId());
-        productionProductMain.setUserName(dto.getUserName());
-        productionProductMain.setProductionOperationTaskId(productionOperationTask.getId());
-        productionProductMain.setWorkOrderId(productionOperationTask.getId());
+        productionProductMain.setUserId(user == null ? dto.getUserId() : user.getUserId());
+        productionProductMain.setUserName(user == null ? dto.getUserName() : user.getNickName());
+        productionProductMain.setProductionOperationTaskId(taskId);
         productionProductMain.setStatus(0);
         productionProductMainMapper.insert(productionProductMain);
 
-        List<ProductStructureDto> productStructureDtos = new ArrayList<>();
-        ProductStructureDto productStructureDto = new ProductStructureDto();
-        productStructureDto.setProductModelId(productModel.getId());
-        productStructureDto.setUnitQuantity(BigDecimal.ONE);
-        productStructureDtos.add(productStructureDto);
+        List<ProductStructureDto> productStructureDtos = resolveInputStructures(
+                productionOrder.getId(), routingOperation, productModel.getId());
+        if (productStructureDtos.isEmpty()) {
+            throw new ServiceException("鏈壘鍒板綋鍓嶅伐搴忓搴旂殑BOM鎶曞叆鑺傜偣");
+        }
         for (ProductStructureDto item : productStructureDtos) {
+            // 褰撳墠瀹炵幇鎸夊伐搴忔垚鍝佺洿鎺ヤ綔涓烘姇鍏ワ紝鍚庣画鑻ユ帴鍏ラ鏂欒褰曞彲鍦ㄨ繖閲屾浛鎹㈡潵婧愩��
             ProductionProductInput productionProductInput = new ProductionProductInput();
             productionProductInput.setProductionProductMainId(productionProductMain.getId());
             productionProductInput.setProductMainId(productionProductMain.getId());
@@ -151,8 +164,6 @@
             productionProductInput.setInputQuantity(item.getUnitQuantity().multiply(defaultDecimal(dto.getQuantity())));
             productionProductInput.setQuantity(productionProductInput.getInputQuantity());
             productionProductInputMapper.insert(productionProductInput);
-            stockUtils.substractStock(item.getProductModelId(), productionProductInput.getInputQuantity(),
-                    StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode(), productionProductMain.getId());
         }
 
         ProductionProductOutput productionProductOutput = new ProductionProductOutput();
@@ -166,11 +177,12 @@
 
         List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList(
                 Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
-                        .eq(ProductionOrderRoutingOperation::getTechnologyRoutingId, routingOperation.getTechnologyRoutingId())
+                        .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId())
                         .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId()));
         boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size());
         if (productQty.compareTo(BigDecimal.ZERO) > 0) {
             if (Boolean.TRUE.equals(routingOperation.getIsQuality())) {
+                // 璐ㄦ宸ュ簭鍏堢敓鎴愭楠屽崟锛岄潪璐ㄦ宸ュ簭鐩存帴鍏ュ悎鏍煎搧搴撳瓨銆�
                 int inspectType = isLastOperation ? 2 : 1;
                 String process = isLastOperation ? null : technologyOperation == null ? null : technologyOperation.getName();
                 Product product = productMapper.selectById(productModel.getProductId());
@@ -209,20 +221,26 @@
             if (ObjectUtils.isNull(productionOperationTask.getActualStartTime())) {
                 productionOperationTask.setActualStartTime(LocalDate.now());
             }
+            // 鎶ュ伐椹卞姩宸ュ崟鐘舵�佹祦杞細鏈変骇鍑哄嵆杩涜涓紝杈惧埌璁″垝閲忓嵆瀹屽伐銆�
+            productionOperationTask.setStatus(3);
             if (productionOperationTask.getPlanQuantity() != null
                     && productionOperationTask.getCompleteQuantity().compareTo(productionOperationTask.getPlanQuantity()) >= 0) {
                 productionOperationTask.setActualEndTime(LocalDate.now());
+                productionOperationTask.setStatus(4);
             }
             productionOperationTaskMapper.updateById(productionOperationTask);
 
             if (ObjectUtils.isNull(productionOrder.getStartTime())) {
                 productionOrder.setStartTime(LocalDateTime.now());
             }
+            // 璁㈠崟鐘舵�佺敱鏈�鍚庝竴閬撳伐搴忕殑鍚堟牸浜у嚭鎺ㄥ姩锛岄伩鍏嶄腑闂村伐搴忔彁鍓嶅畬宸ャ��
+            productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
             if (isLastOperation) {
                 productionOrder.setCompleteQuantity(defaultDecimal(productionOrder.getCompleteQuantity()).add(productQty));
                 if (productionOrder.getQuantity() != null
                         && productionOrder.getCompleteQuantity().compareTo(productionOrder.getQuantity()) >= 0) {
                     productionOrder.setEndTime(LocalDateTime.now());
+                    productionOrder.setStatus(ProductOrderStatusEnum.FINISHED.getCode());
                 }
             }
             productionOrderMapper.updateById(productionOrder);
@@ -235,8 +253,8 @@
             }
             ProductionAccount productionAccount = new ProductionAccount();
             productionAccount.setProductionProductMainId(productionProductMain.getId());
-            productionAccount.setSalesLedgerId(productionOrder.getSalesLedgerId());
-            productionAccount.setSalesLedgerProductId(productionOrder.getSaleLedgerProductId() == null ? null : productionOrder.getSaleLedgerProductId().longValue());
+//            productionAccount.setSalesLedgerId(productionOrder.getSalesLedgerId());
+//            productionAccount.setSalesLedgerProductId(productionOrder.getSalesLedgerProductId() == null ? null : productionOrder.getSalesLedgerProductId().longValue());
             productionAccount.setSchedulingUserId(user == null ? null : user.getUserId());
             productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName());
             productionAccount.setFinishedNum(productQty);
@@ -252,7 +270,101 @@
         return true;
     }
 
+    private void syncOperationParamInputValue(ProductionProductMainDto dto, Long productionOrderRoutingOperationId) {
+        if (dto == null || productionOrderRoutingOperationId == null) {
+            return;
+        }
+        List<ProductionOrderRoutingOperationParam> paramList = dto.getProductionOperationParamList();
+        if (paramList == null || paramList.isEmpty()) {
+            return;
+        }
+        List<ProductionOrderRoutingOperationParam> dbParamList = productionOrderRoutingOperationParamMapper.selectList(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .eq(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, productionOrderRoutingOperationId));
+        if (dbParamList == null || dbParamList.isEmpty()) {
+            return;
+        }
+        Map<Long, ProductionOrderRoutingOperationParam> dbParamMap = dbParamList.stream()
+                .filter(item -> item != null && item.getId() != null)
+                .collect(Collectors.toMap(ProductionOrderRoutingOperationParam::getId, item -> item, (left, right) -> left));
+        for (ProductionOrderRoutingOperationParam param : paramList) {
+            if (param == null || param.getId() == null) {
+                continue;
+            }
+            ProductionOrderRoutingOperationParam dbParam = dbParamMap.get(param.getId());
+            if (dbParam == null) {
+                throw new ServiceException("宸ュ簭鍙傛暟涓嶅瓨鍦ㄦ垨涓嶅睘浜庡綋鍓嶅伐鍗曞伐搴忥紝ID=" + param.getId());
+            }
+            if (Objects.equals(dbParam.getInputValue(), param.getInputValue())) {
+                continue;
+            }
+            ProductionOrderRoutingOperationParam updateParam = new ProductionOrderRoutingOperationParam();
+            updateParam.setId(dbParam.getId());
+            updateParam.setInputValue(param.getInputValue());
+            productionOrderRoutingOperationParamMapper.updateById(updateParam);
+        }
+    }
+
+    private List<ProductStructureDto> resolveInputStructures(Long productionOrderId,
+                                                             ProductionOrderRoutingOperation routingOperation,
+                                                             Long outputProductModelId) {
+        if (productionOrderId == null || routingOperation == null || routingOperation.getTechnologyOperationId() == null) {
+            return new ArrayList<>();
+        }
+        ProductionOrderBom orderBom = productionOrderBomMapper.selectOne(
+                Wrappers.<ProductionOrderBom>lambdaQuery()
+                        .eq(ProductionOrderBom::getProductionOrderId, productionOrderId)
+                        .orderByDesc(ProductionOrderBom::getId)
+                        .last("limit 1"));
+        if (orderBom == null || orderBom.getId() == null) {
+            return new ArrayList<>();
+        }
+
+        List<ProductionBomStructure> bomNodeList = productionBomStructureMapper.selectList(
+                Wrappers.<ProductionBomStructure>lambdaQuery()
+                        .eq(ProductionBomStructure::getProductionOrderBomId, orderBom.getId())
+                        .orderByAsc(ProductionBomStructure::getId));
+        if (bomNodeList.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        Map<Long, ProductionBomStructure> nodeMap = bomNodeList.stream()
+                .filter(item -> item != null && item.getId() != null)
+                .collect(Collectors.toMap(ProductionBomStructure::getId, item -> item, (left, right) -> left));
+        Long currentOutputModelId = routingOperation.getProductModelId() != null
+                ? routingOperation.getProductModelId()
+                : outputProductModelId;
+
+        Map<Long, BigDecimal> unitQtyByProductModel = new LinkedHashMap<>();
+        for (ProductionBomStructure node : bomNodeList) {
+            if (node == null || node.getParentId() == null || node.getProductModelId() == null) {
+                continue;
+            }
+            if (!Objects.equals(node.getTechnologyOperationId(), routingOperation.getTechnologyOperationId())) {
+                continue;
+            }
+            ProductionBomStructure parent = nodeMap.get(node.getParentId());
+            if (parent == null || !Objects.equals(parent.getProductModelId(), currentOutputModelId)) {
+                continue;
+            }
+            unitQtyByProductModel.merge(node.getProductModelId(), defaultDecimal(node.getUnitQuantity()), BigDecimal::add);
+        }
+
+        List<ProductStructureDto> result = new ArrayList<>();
+        for (Map.Entry<Long, BigDecimal> entry : unitQtyByProductModel.entrySet()) {
+            if (entry.getValue().compareTo(BigDecimal.ZERO) <= 0) {
+                continue;
+            }
+            ProductStructureDto item = new ProductStructureDto();
+            item.setProductModelId(entry.getKey());
+            item.setUnitQuantity(entry.getValue());
+            result.add(item);
+        }
+        return result;
+    }
+
     private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) {
+        // 鍒犻櫎鎶ュ伐闇�瑕佸悓姝ュ洖婊氳川妫�銆佸簱瀛樸�佸伐鏃舵牳绠楀拰璁㈠崟/宸ュ崟杩涘害銆�
         List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
                 Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, productionProductMain.getId()));
         if (qualityInspects.size() > 0) {
@@ -275,22 +387,36 @@
             BigDecimal validQuantity = defaultDecimal(productionProductOutput.getQuantity()).subtract(defaultDecimal(productionProductOutput.getScrapQty()));
             productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(validQuantity));
             productionOperationTask.setActualEndTime(null);
+            if (defaultDecimal(productionOperationTask.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
+                productionOperationTask.setCompleteQuantity(BigDecimal.ZERO);
+                productionOperationTask.setActualStartTime(null);
+                productionOperationTask.setStatus(2);
+            } else {
+                productionOperationTask.setStatus(3);
+            }
             productionOperationTaskMapper.updateById(productionOperationTask);
 
             ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId());
-            ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getTechnologyRoutingOperationId());
+            ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getProductionOrderRoutingOperationId());
             if (productionOrder != null && routingOperation != null) {
+                // 鍙湁鏈�鍚庝竴閬撳伐搴忕殑鎶ュ伐鎵嶄細褰卞搷鐢熶骇璁㈠崟瀹屽伐鏁伴噺銆�
                 List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList(
                         Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
-                                .eq(ProductionOrderRoutingOperation::getTechnologyRoutingId, routingOperation.getTechnologyRoutingId())
+                                .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId())
                                 .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId()));
                 boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size());
                 if (isLastOperation) {
                     BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(validQuantity);
                     productionOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty);
                     productionOrder.setEndTime(null);
-                    productionOrderMapper.updateById(productionOrder);
                 }
+                if (defaultDecimal(productionOrder.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
+                    productionOrder.setStartTime(null);
+                    productionOrder.setStatus(ProductOrderStatusEnum.WAIT.getCode());
+                } else {
+                    productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
+                }
+                productionOrderMapper.updateById(productionOrder);
             }
         }
 
@@ -338,6 +464,13 @@
         return value == null ? BigDecimal.ZERO : value;
     }
 
+    private Long resolveTaskId(ProductionProductMainDto dto) {
+        if (dto == null) {
+            return null;
+        }
+        return dto.getProductionOperationTaskId();
+    }
+
     @Override
     public ArrayList<Long> listMain(List<Long> idList) {
         return productionProductMainMapper.listMain(idList);

--
Gitblit v1.9.3