From 741918a903e17b2ec7522556d2c043b8d35dd8a1 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 15 六月 2026 17:42:58 +0800
Subject: [PATCH] 生产取消bom,不合格管理定制化

---
 src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java |  610 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 494 insertions(+), 116 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 ff43daf..3514da3 100644
--- a/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
+++ b/src/main/java/com/ruoyi/production/service/impl/ProductionProductMainServiceImpl.java
@@ -1,5 +1,6 @@
 package com.ruoyi.production.service.impl;
 
+import com.alibaba.fastjson2.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
@@ -12,12 +13,11 @@
 import com.ruoyi.basic.pojo.Product;
 import com.ruoyi.basic.pojo.ProductModel;
 import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
-import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
 import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum;
 import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 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.*;
@@ -27,6 +27,10 @@
 import com.ruoyi.project.system.mapper.SysUserMapper;
 import com.ruoyi.quality.mapper.*;
 import com.ruoyi.quality.pojo.*;
+import com.ruoyi.stock.pojo.StockInRecord;
+import com.ruoyi.stock.service.StockInRecordService;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.service.StockInventoryService;
 import com.ruoyi.technology.mapper.TechnologyOperationMapper;
 import com.ruoyi.technology.mapper.TechnologyRoutingOperationMapper;
 import com.ruoyi.technology.pojo.TechnologyOperation;
@@ -36,12 +40,19 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 @Service
@@ -64,117 +75,323 @@
     private final ProductionOperationTaskMapper productionOperationTaskMapper;
     private final ProductionOrderMapper productionOrderMapper;
     private final ProductionOrderRoutingOperationMapper productionOrderRoutingOperationMapper;
+    private final ProductionOrderRoutingOperationParamMapper productionOrderRoutingOperationParamMapper;
     private final TechnologyRoutingOperationMapper technologyRoutingOperationMapper;
     private final TechnologyOperationMapper technologyOperationMapper;
     private final StockUtils stockUtils;
+    private final StockInRecordService stockInRecordService;
+    private final StockInventoryService stockInventoryService;
 
     @Override
     public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) {
-        return productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
+        // 鍒嗛〉鏌ヨ鐢熶骇鎶ュ伐涓昏〃
+        IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto);
+        fillOperationParamList(result.getRecords());
+        return result;
     }
 
     @Override
     public IPage<ProductionProductMainDto> pageProductionProductMain(Page page, ProductionProductMainDto productionProductMainDto) {
+        // 鍒嗛〉鏌ヨ鐢熶骇鎶ュ伐涓昏〃
         return listPageProductionProductMainDto(page, productionProductMainDto);
     }
 
     @Override
     public ProductionProductMainDto getProductionProductMainInfo(Long id) {
-        return productionProductMainMapper.listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{
+        // 鑾峰彇鐢熶骇浜у搧涓昏〃璇︽儏
+        return listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{
             setId(id);
         }}).getRecords().stream().findFirst().orElse(null);
     }
 
+    private void fillOperationParamList(List<ProductionProductMainDto> recordList) {
+        // 濉厖宸ュ簭鍙傛暟鍒楄〃
+        if (recordList == null || recordList.isEmpty()) {
+            return;
+        }
+        // 閬嶅巻澶勭悊鏁版嵁骞剁粍瑁呯粨鏋�
+        Set<Long> mainIdSet = recordList.stream()
+                .map(ProductionProductMainDto::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (mainIdSet.isEmpty()) {
+            recordList.forEach(item -> item.setProductionOperationParamList(Collections.emptyList()));
+            return;
+        }
+
+        // 鏌ヨ骞跺噯澶囦笟鍔℃暟鎹�
+        List<ProductionOrderRoutingOperationParam> paramList = productionOrderRoutingOperationParamMapper.selectList(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .in(ProductionOrderRoutingOperationParam::getProductionProductMainId, mainIdSet)
+                        .orderByAsc(ProductionOrderRoutingOperationParam::getId));
+        Map<Long, List<ProductionOrderRoutingOperationParam>> paramGroupMap = new HashMap<>();
+        for (ProductionOrderRoutingOperationParam param : paramList) {
+            if (param == null || param.getProductionProductMainId() == null) {
+                continue;
+            }
+            paramGroupMap.computeIfAbsent(param.getProductionProductMainId(), key -> new ArrayList<>()).add(param);
+        }
+
+        Set<Long> missingMainIdSet = new LinkedHashSet<>();
+        for (ProductionProductMainDto item : recordList) {
+            Long mainId = item.getId();
+            if (mainId == null) {
+                item.setProductionOperationParamList(Collections.emptyList());
+                continue;
+            }
+            List<ProductionOrderRoutingOperationParam> params = paramGroupMap.get(mainId);
+            if (params != null && !params.isEmpty()) {
+                item.setProductionOperationParamList(params);
+                continue;
+            }
+            missingMainIdSet.add(mainId);
+        }
+        if (missingMainIdSet.isEmpty()) {
+            return;
+        }
+
+        // 鍏煎鍘嗗彶鏁版嵁锛氭棫鎶ュ伐璁板綍娌℃湁鎸夋姤宸D钀藉弬鏁板揩鐓ф椂锛屽洖閫�灞曠ず宸ュ簭妯℃澘鍙傛暟銆�
+        List<ProductionProductMain> mainList = productionProductMainMapper.selectBatchIds(missingMainIdSet);
+        Map<Long, Long> mainIdToTaskIdMap = mainList.stream()
+                .filter(Objects::nonNull)
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(ProductionProductMain::getId,
+                        ProductionProductMain::getProductionOperationTaskId, (left, right) -> left));
+        Set<Long> taskIdSet = mainIdToTaskIdMap.values().stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (taskIdSet.isEmpty()) {
+            for (ProductionProductMainDto item : recordList) {
+                if (item.getId() != null && missingMainIdSet.contains(item.getId())) {
+                    item.setProductionOperationParamList(Collections.emptyList());
+                }
+            }
+            return;
+        }
+
+        List<ProductionOperationTask> taskList = productionOperationTaskMapper.selectList(
+                Wrappers.<ProductionOperationTask>lambdaQuery()
+                        .in(ProductionOperationTask::getId, taskIdSet));
+        Map<Long, Long> taskIdToRoutingOperationIdMap = taskList.stream()
+                .filter(Objects::nonNull)
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(ProductionOperationTask::getId,
+                        ProductionOperationTask::getProductionOrderRoutingOperationId, (left, right) -> left));
+        Set<Long> routingOperationIdSet = taskIdToRoutingOperationIdMap.values().stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (routingOperationIdSet.isEmpty()) {
+            for (ProductionProductMainDto item : recordList) {
+                if (item.getId() != null && missingMainIdSet.contains(item.getId())) {
+                    item.setProductionOperationParamList(Collections.emptyList());
+                }
+            }
+            return;
+        }
+
+        List<ProductionOrderRoutingOperationParam> fallbackParamList = productionOrderRoutingOperationParamMapper.selectList(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .in(ProductionOrderRoutingOperationParam::getProductionOrderRoutingOperationId, routingOperationIdSet)
+                        .isNull(ProductionOrderRoutingOperationParam::getProductionProductMainId)
+                        .orderByAsc(ProductionOrderRoutingOperationParam::getId));
+        Map<Long, List<ProductionOrderRoutingOperationParam>> fallbackGroupMap = new HashMap<>();
+        for (ProductionOrderRoutingOperationParam param : fallbackParamList) {
+            if (param == null || param.getProductionOrderRoutingOperationId() == null) {
+                continue;
+            }
+            fallbackGroupMap.computeIfAbsent(param.getProductionOrderRoutingOperationId(), key -> new ArrayList<>()).add(param);
+        }
+
+        for (ProductionProductMainDto item : recordList) {
+            Long mainId = item.getId();
+            if (mainId == null || !missingMainIdSet.contains(mainId)) {
+                continue;
+            }
+            Long taskId = mainIdToTaskIdMap.get(mainId);
+            Long routingOperationId = taskId == null ? null : taskIdToRoutingOperationIdMap.get(taskId);
+            if (routingOperationId == null) {
+                item.setProductionOperationParamList(Collections.emptyList());
+                continue;
+            }
+            item.setProductionOperationParamList(fallbackGroupMap.getOrDefault(routingOperationId, Collections.emptyList()));
+        }
+    }
+
     @Override
     public Boolean addProductMain(ProductionProductMainDto dto) {
-        if (dto.getProductionOperationTaskId() == null) {
+        // 鍏煎鏃ф祦绋嬶細濡傛灉娌℃湁寮�濮嬫姤宸ヨ褰旾D锛岃蛋涓�姝ユ姤宸ワ紙鑷姩鍒涘缓+缁撴潫锛�
+        Long startRecordId = dto.resolveStartRecordId();
+        if (startRecordId == null) {
+            return oneStepWork(dto);
+        }
+        dto.setId(startRecordId);
+        return finishWork(dto);
+    }
+
+    @Override
+    public Boolean startWork(ProductionProductMainDto dto) {
+        // 寮�濮嬫姤宸ワ細鍒涘缓鎶ュ伐璁板綍锛屾爣璁板疄闄呭紑濮嬫椂闂�
+        Long taskId = resolveTaskId(dto);
+        if (taskId == null) {
             throw new ServiceException("璇蜂紶鍏ョ敓浜у伐鍗旾D");
         }
-        return addProductMainByProductionTask(dto);
-    }
-
-    @Override
-    public Boolean saveProductionProductMain(ProductionProductMainDto productionProductMainDto) {
-        return addProductMain(productionProductMainDto);
-    }
-
-    @Override
-    public Boolean removeProductMain(Long id) {
-        ProductionProductMain currentMain = productionProductMainMapper.selectById(id);
-        if (currentMain == null) {
-            return true;
-        }
-        return removeProductMainByProductionTask(currentMain);
-    }
-
-    private Boolean addProductMainByProductionTask(ProductionProductMainDto dto) {
-        // 鎶ュ伐浠ヨ鍗曞伐搴忓揩鐓т负鍑嗭紝閬垮厤宸ヨ壓涓绘暟鎹彉鏇村悗褰卞搷鍘嗗彶宸ュ崟鎵ц銆�
-        SysUser user = userMapper.selectUserById(dto.getUserId());
-        ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(dto.getProductionOperationTaskId());
-        if (productionOperationTask == null) {
+        ProductionOperationTask task = productionOperationTaskMapper.selectById(taskId);
+        if (task == null) {
             throw new ServiceException("鐢熶骇宸ュ崟涓嶅瓨鍦�");
         }
-        ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOperationTask.getTechnologyRoutingOperationId());
-        if (routingOperation == null) {
-            throw new ServiceException("璁㈠崟宸ヨ壓璺嚎宸ュ簭涓嶅瓨鍦�");
+        // 鏉冮檺鏍¢獙锛氬凡鎸囨淳鏃朵粎琚寚娲句汉鍙搷浣�
+        validateWorkerPermission(task);
+
+        if (task.getStatus() != null && task.getStatus() != 2 && task.getStatus() != 3) {
+            throw new ServiceException("褰撳墠宸ュ崟鐘舵�佷笉鍏佽寮�濮嬫姤宸ワ紝浠呭緟鐢熶骇鎴栫敓浜т腑鐘舵�佺殑宸ュ崟鍙搷浣�");
         }
-        ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId());
-        if (productionOrder == null) {
-            throw new ServiceException("鐢熶骇璁㈠崟涓嶅瓨鍦�");
+        SysUser user = userMapper.selectUserById(dto.getUserId());
+        ProductionProductMain main = new ProductionProductMain();
+        main.setProductNo(generateProductNo());
+        main.setUserId(user == null ? dto.getUserId() : user.getUserId());
+        main.setUserName(user == null ? dto.getUserName() : user.getNickName());
+        main.setProductionOperationTaskId(taskId);
+        main.setStatus(0);
+        main.setActualStartTime(LocalDateTime.now());
+        productionProductMainMapper.insert(main);
+
+        // 宸ュ崟鐘舵�� -> 鐢熶骇涓�
+        task.setStatus(3);
+        if (task.getActualStartTime() == null) {
+            task.setActualStartTime(LocalDateTime.now());
         }
-        TechnologyRoutingOperation technologyRoutingOperation = technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId());
-        TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null
-                : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId());
-        ProductModel productModel = productModelMapper.selectById(
-                routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : productionOrder.getProductModelId());
+        productionOperationTaskMapper.updateById(task);
+
+        // 鐢熶骇璁㈠崟 -> 鐢熶骇杩涜涓�
+        ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId());
+        if (productionOrder != null && productionOrder.getStartTime() == null) {
+            productionOrder.setStartTime(LocalDateTime.now());
+            productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
+            productionOrderMapper.updateById(productionOrder);
+        }
+        return true;
+    }
+
+    private Boolean oneStepWork(ProductionProductMainDto dto) {
+        // 鍏煎鏃т竴姝ユ姤宸ユ祦绋嬶細鑷姩鍒涘缓寮�濮嬭褰曞苟绔嬪嵆缁撴潫
+        Long taskId = resolveTaskId(dto);
+        if (taskId == null) {
+            throw new ServiceException("璇蜂紶鍏ョ敓浜у伐鍗旾D");
+        }
+        ProductionOperationTask task = productionOperationTaskMapper.selectById(taskId);
+        if (task == null) {
+            throw new ServiceException("鐢熶骇宸ュ崟涓嶅瓨鍦�");
+        }
+        validateWorkerPermission(task);
+        if (task.getStatus() != null && task.getStatus() != 2 && task.getStatus() != 3) {
+            throw new ServiceException("褰撳墠宸ュ崟鐘舵�佷笉鍏佽鎶ュ伐");
+        }
+        SysUser user = userMapper.selectUserById(dto.getUserId());
+        ProductionProductMain main = new ProductionProductMain();
+        main.setProductNo(generateProductNo());
+        main.setUserId(user == null ? dto.getUserId() : user.getUserId());
+        main.setUserName(user == null ? dto.getUserName() : user.getNickName());
+        main.setProductionOperationTaskId(taskId);
+        main.setStatus(0);
+        main.setActualStartTime(LocalDateTime.now());
+        productionProductMainMapper.insert(main);
+
+        if (task.getActualStartTime() == null) {
+            task.setActualStartTime(LocalDateTime.now());
+        }
+        task.setStatus(3);
+        productionOperationTaskMapper.updateById(task);
+
+        ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId());
+        if (productionOrder != null && productionOrder.getStartTime() == null) {
+            productionOrder.setStartTime(LocalDateTime.now());
+            productionOrder.setStatus(ProductOrderStatusEnum.RUNNING.getCode());
+            productionOrderMapper.updateById(productionOrder);
+        }
+
+        dto.setId(main.getId());
+        return finishWork(dto);
+    }
+
+    private Boolean finishWork(ProductionProductMainDto dto) {
+        // 缁撴潫鎶ュ伐锛氭洿鏂板紑濮嬫姤宸ヨ褰曪紝鍒涘缓浜у嚭銆佹姇鍏ュ搧銆佹牳绠楄褰�
+        ProductionProductMain currentMain = productionProductMainMapper.selectById(dto.getId());
+        if (currentMain == null) {
+            throw new ServiceException("寮�濮嬫姤宸ヨ褰曚笉瀛樺湪");
+        }
+        if (currentMain.getStatus() == null || currentMain.getStatus() != 0) {
+            throw new ServiceException("璇ユ姤宸ヨ褰曞凡缁撴潫鎴栫姸鎬佸紓甯�");
+        }
+        Long taskId = currentMain.getProductionOperationTaskId();
+        ProductionOperationTask task = productionOperationTaskMapper.selectById(taskId);
+        if (task == null) {
+            throw new ServiceException("鐢熶骇宸ュ崟涓嶅瓨鍦�");
+        }
+        validateWorkerPermission(task);
+
+        // 鏇存柊鎶ュ伐璁板綍涓哄凡缁撴潫锛岃褰曠粨鏉熸椂闂�
+        currentMain.setActualEndTime(LocalDateTime.now());
+        currentMain.setStatus(1);
+        if (dto.getWorkHour() != null) {
+            currentMain.setWorkHour(dto.getWorkHour());
+        } else if (currentMain.getActualStartTime() != null) {
+            // 鏍规嵁寮�濮�/缁撴潫鏃堕棿璁$畻瀹為檯宸ユ椂锛堝皬鏃讹級
+            long seconds = Duration.between(currentMain.getActualStartTime(), currentMain.getActualEndTime()).getSeconds();
+            currentMain.setWorkHour(BigDecimal.valueOf(seconds).divide(BigDecimal.valueOf(3600), 4, RoundingMode.HALF_UP));
+        }
+        productionProductMainMapper.updateById(currentMain);
+
+        // 鍚屾宸ュ簭鍙傛暟
+        ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(task.getProductionOrderRoutingOperationId());
+        if (routingOperation != null) {
+            syncOperationParamInputValue(dto, routingOperation.getId(), currentMain.getId());
+        }
+
+        // 鑾峰彇浜у搧瑙勬牸
+        ProductModel productModel = null;
+        if (routingOperation != null) {
+            productModel = productModelMapper.selectById(
+                    routingOperation.getProductModelId() != null ? routingOperation.getProductModelId() : getProductionOrderProductModelId(task));
+        }
         if (productModel == null) {
             throw new ServiceException("浜у搧瑙勬牸涓嶅瓨鍦�");
         }
 
-        ProductionProductMain productionProductMain = new ProductionProductMain();
-        productionProductMain.setProductNo(generateProductNo());
-        productionProductMain.setUserId(dto.getUserId());
-        productionProductMain.setUserName(dto.getUserName());
-        productionProductMain.setProductionOperationTaskId(productionOperationTask.getId());
-        productionProductMain.setWorkOrderId(productionOperationTask.getId());
-        productionProductMain.setStatus(0);
-        productionProductMainMapper.insert(productionProductMain);
+        // 鎶曞叆鍝�
+        ProductionProductInput productionProductInput = new ProductionProductInput();
+        productionProductInput.setProductionProductMainId(currentMain.getId());
+        productionProductInput.setProductModelId(productModel.getId());
+        productionProductInput.setInputQuantity(defaultDecimal(dto.getQuantity()));
+        productionProductInput.setQuantity(productionProductInput.getInputQuantity());
+        productionProductInputMapper.insert(productionProductInput);
 
-        List<ProductStructureDto> productStructureDtos = new ArrayList<>();
-        ProductStructureDto productStructureDto = new ProductStructureDto();
-        productStructureDto.setProductModelId(productModel.getId());
-        productStructureDto.setUnitQuantity(BigDecimal.ONE);
-        productStructureDtos.add(productStructureDto);
-        for (ProductStructureDto item : productStructureDtos) {
-            // 褰撳墠瀹炵幇鎸夊伐搴忔垚鍝佺洿鎺ヤ綔涓烘姇鍏ワ紝鍚庣画鑻ユ帴鍏ラ鏂欒褰曞彲鍦ㄨ繖閲屾浛鎹㈡潵婧愩��
-            ProductionProductInput productionProductInput = new ProductionProductInput();
-            productionProductInput.setProductionProductMainId(productionProductMain.getId());
-            productionProductInput.setProductMainId(productionProductMain.getId());
-            productionProductInput.setProductModelId(item.getProductModelId());
-            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();
-        productionProductOutput.setProductionProductMainId(productionProductMain.getId());
-        productionProductOutput.setProductMainId(productionProductMain.getId());
+        productionProductOutput.setProductionProductMainId(currentMain.getId());
         productionProductOutput.setProductModelId(productModel.getId());
         productionProductOutput.setQuantity(defaultDecimal(dto.getQuantity()));
         productionProductOutput.setScrapQty(defaultDecimal(dto.getScrapQty()));
         productionProductOutputMapper.insert(productionProductOutput);
-        BigDecimal productQty = productionProductOutput.getQuantity().subtract(productionProductOutput.getScrapQty());
 
-        List<ProductionOrderRoutingOperation> routingOperationList = productionOrderRoutingOperationMapper.selectList(
+        BigDecimal reportQty = defaultDecimal(productionProductOutput.getQuantity());
+        BigDecimal scrapQty = defaultDecimal(productionProductOutput.getScrapQty());
+        BigDecimal productQty = reportQty;
+        String qualifiedBatchNo = null;
+
+        TechnologyRoutingOperation technologyRoutingOperation = routingOperation != null
+                ? technologyRoutingOperationMapper.selectById(routingOperation.getTechnologyRoutingOperationId()) : null;
+        TechnologyOperation technologyOperation = technologyRoutingOperation == null ? null
+                : technologyOperationMapper.selectById(technologyRoutingOperation.getTechnologyOperationId());
+
+        List<ProductionOrderRoutingOperation> routingOperationList = routingOperation != null ? productionOrderRoutingOperationMapper.selectList(
                 Wrappers.<ProductionOrderRoutingOperation>lambdaQuery()
-                        .eq(ProductionOrderRoutingOperation::getTechnologyRoutingId, routingOperation.getTechnologyRoutingId())
-                        .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId()));
-        boolean isLastOperation = routingOperation.getDragSort() != null && routingOperation.getDragSort().equals(routingOperationList.size());
+                        .eq(ProductionOrderRoutingOperation::getOrderRoutingId, routingOperation.getOrderRoutingId())
+                        .eq(ProductionOrderRoutingOperation::getProductionOrderId, routingOperation.getProductionOrderId())) : new ArrayList<>();
+        boolean isLastOperation = routingOperation != null && routingOperation.getDragSort() != null
+                && routingOperation.getDragSort().equals(routingOperationList.size());
+
         if (productQty.compareTo(BigDecimal.ZERO) > 0) {
-            if (Boolean.TRUE.equals(routingOperation.getIsQuality())) {
-                // 璐ㄦ宸ュ簭鍏堢敓鎴愭楠屽崟锛岄潪璐ㄦ宸ュ簭鐩存帴鍏ュ悎鏍煎搧搴撳瓨銆�
+            if (routingOperation != null && 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());
@@ -187,11 +404,11 @@
                 qualityInspect.setProcess(process);
                 qualityInspect.setInspectState(0);
                 qualityInspect.setInspectType(inspectType);
-                qualityInspect.setProductMainId(productionProductMain.getId());
+                qualityInspect.setProductMainId(currentMain.getId());
                 qualityInspect.setProductModelId(productModel.getId());
                 qualityInspectMapper.insert(qualityInspect);
                 List<QualityTestStandard> qualityTestStandard = qualityTestStandardMapper.getQualityTestStandardByProductId(product.getId(), inspectType, process);
-                if (qualityTestStandard.size() > 0) {
+                if (!qualityTestStandard.isEmpty()) {
                     qualityInspect.setTestStandardId(qualityTestStandard.get(0).getId());
                     qualityInspectMapper.updateById(qualityInspect);
                     qualityTestStandardParamMapper.selectList(Wrappers.<QualityTestStandardParam>lambdaQuery()
@@ -205,37 +422,47 @@
                             });
                 }
             } else {
-                stockUtils.addStock(productModel.getId(), productQty,
-                        StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(), productionProductMain.getId());
+                StockInventoryDto stockInventoryDto = new StockInventoryDto();
+                stockInventoryDto.setRecordId(currentMain.getId());
+                stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode()));
+                stockInventoryDto.setQualitity(productQty);
+                stockInventoryDto.setProductModelId(productModel.getId());
+                stockInventoryService.addStockInRecordOnly(stockInventoryDto);
+                qualifiedBatchNo = resolveLatestStockInBatchNo(
+                        currentMain.getId(),
+                        StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode(),
+                        productModel.getId(),
+                        "0");
             }
 
-            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).add(productQty));
-            if (ObjectUtils.isNull(productionOperationTask.getActualStartTime())) {
-                productionOperationTask.setActualStartTime(LocalDate.now());
+            task.setCompleteQuantity(defaultDecimal(task.getCompleteQuantity()).add(productQty));
+            if (task.getActualStartTime() == null) {
+                task.setActualStartTime(LocalDateTime.now());
             }
-            // 鎶ュ伐椹卞姩宸ュ崟鐘舵�佹祦杞細鏈変骇鍑哄嵆杩涜涓紝杈惧埌璁″垝閲忓嵆瀹屽伐銆�
-            productionOperationTask.setStatus(3);
-            if (productionOperationTask.getPlanQuantity() != null
-                    && productionOperationTask.getCompleteQuantity().compareTo(productionOperationTask.getPlanQuantity()) >= 0) {
-                productionOperationTask.setActualEndTime(LocalDate.now());
-                productionOperationTask.setStatus(4);
+            task.setStatus(3);
+            if (task.getPlanQuantity() != null
+                    && task.getCompleteQuantity().compareTo(task.getPlanQuantity()) >= 0) {
+                task.setActualEndTime(LocalDateTime.now());
+                task.setStatus(4);
             }
-            productionOperationTaskMapper.updateById(productionOperationTask);
+            productionOperationTaskMapper.updateById(task);
 
-            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());
+            ProductionOrder productionOrder = productionOrderMapper.selectById(task.getProductionOrderId());
+            if (productionOrder != null) {
+                if (productionOrder.getStartTime() == null) {
+                    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);
             }
-            productionOrderMapper.updateById(productionOrder);
 
             BigDecimal workHours = BigDecimal.ZERO;
             if (technologyOperation != null && technologyOperation.getSalaryQuota() != null) {
@@ -244,31 +471,167 @@
                         : technologyOperation.getSalaryQuota();
             }
             ProductionAccount productionAccount = new ProductionAccount();
-            productionAccount.setProductionProductMainId(productionProductMain.getId());
-            productionAccount.setSalesLedgerId(productionOrder.getSalesLedgerId());
-            productionAccount.setSalesLedgerProductId(productionOrder.getSaleLedgerProductId() == null ? null : productionOrder.getSaleLedgerProductId().longValue());
-            productionAccount.setSchedulingUserId(user == null ? null : user.getUserId());
-            productionAccount.setSchedulingUserName(user == null ? dto.getUserName() : user.getNickName());
+            productionAccount.setProductionProductMainId(currentMain.getId());
+            SysUser user = userMapper.selectUserById(dto.getUserId());
+            productionAccount.setSchedulingUserId(user == null ? currentMain.getUserId() : user.getUserId());
+            productionAccount.setSchedulingUserName(user == null ? currentMain.getUserName() : user.getNickName());
             productionAccount.setFinishedNum(productQty);
-            productionAccount.setWorkHours(workHours);
+            productionAccount.setWorkHours(technologyOperation != null ? technologyOperation.getSalaryQuota() : null);
             productionAccount.setTechnologyOperationName(technologyOperation == null ? null : technologyOperation.getName());
-            productionAccount.setSchedulingDate(LocalDateTime.now());
+            productionAccount.setSchedulingDate(currentMain.getActualEndTime() != null ? currentMain.getActualEndTime() : LocalDateTime.now());
             productionAccountMapper.insert(productionAccount);
         }
-        if (defaultDecimal(dto.getScrapQty()).compareTo(BigDecimal.ZERO) > 0) {
-            stockUtils.addUnStock(productModel.getId(), dto.getScrapQty(),
-                    StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(), productionProductMain.getId());
+        if (scrapQty.compareTo(BigDecimal.ZERO) > 0) {
+            stockUtils.addUnStockWithBatchNo(
+                    productModel.getId(),
+                    scrapQty,
+                    StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode(),
+                    currentMain.getId(),
+                    qualifiedBatchNo);
         }
         return true;
     }
 
+    private Long getProductionOrderProductModelId(ProductionOperationTask task) {
+        // 浠庣敓浜ц鍗曡幏鍙栦骇鍝佽鏍糏D
+        if (task == null || task.getProductionOrderId() == null) {
+            return null;
+        }
+        ProductionOrder order = productionOrderMapper.selectById(task.getProductionOrderId());
+        return order == null ? null : order.getProductModelId();
+    }
+
+    private void validateWorkerPermission(ProductionOperationTask task) {
+        // 鏍¢獙褰撳墠鐢ㄦ埛鏄惁鏈夋潈鎿嶄綔姝ゅ伐鍗曪細鏈寚娲炬椂浜轰汉鍙搷浣滐紝宸叉寚娲炬椂浠呰鎸囨淳浜哄彲鎿嶄綔
+        if (task == null) {
+            return;
+        }
+        String userIds = task.getUserIds();
+        if (userIds == null || userIds.isEmpty() || "[]".equals(userIds.trim())) {
+            return;
+        }
+        Long currentUserId = SecurityUtils.getUserId();
+        if (currentUserId == null) {
+            return;
+        }
+        List<Long> assignedIds = JSON.parseArray(userIds, Long.class);
+        if (assignedIds == null || !assignedIds.contains(currentUserId)) {
+            throw new ServiceException("鎮ㄦ湭琚寚娲惧埌姝ゅ伐鍗曪紝鏃犳硶鎿嶄綔");
+        }
+    }
+
+    @Override
+    public Boolean saveProductionProductMain(ProductionProductMainDto productionProductMainDto) {
+        // 淇濆瓨鐢熶骇鎶ュ伐涓昏褰�
+        return addProductMain(productionProductMainDto);
+    }
+
+    @Override
+    public Boolean removeProductMain(Long id) {
+        // 鍒犻櫎鐢熶骇鎶ュ伐涓昏褰�
+        ProductionProductMain currentMain = productionProductMainMapper.selectById(id);
+        if (currentMain == null) {
+            return true;
+        }
+        return removeProductMainByProductionTask(currentMain);
+    }
+
+    private String resolveLatestStockInBatchNo(Long recordId,
+                                               String recordType,
+                                               Long productModelId,
+                                               String stockType) {
+        if (recordId == null || productModelId == null) {
+            return null;
+        }
+        StockInRecord stockInRecord = stockInRecordService.getOne(
+                Wrappers.<StockInRecord>lambdaQuery()
+                        .eq(StockInRecord::getRecordId, recordId)
+                        .eq(StockInRecord::getRecordType, recordType)
+                        .eq(StockInRecord::getProductModelId, productModelId)
+                        .eq(StockInRecord::getType, stockType)
+                        .orderByDesc(StockInRecord::getId)
+                        .last("limit 1"),
+                false);
+        if (stockInRecord == null) {
+            throw new ServiceException("鏈壘鍒板搴旂殑鍏ュ簱鐢宠璁板綍");
+        }
+        return stockInRecord.getBatchNo();
+    }
+
+    private void syncOperationParamInputValue(ProductionProductMainDto dto,
+                                              Long productionOrderRoutingOperationId,
+                                              Long productionProductMainId) {
+        if (dto == null || productionOrderRoutingOperationId == null || productionProductMainId == null) {
+            return;
+        }
+        List<ProductionOrderRoutingOperationParam> paramList = dto.getProductionOperationParamList();
+        if (paramList == null || paramList.isEmpty()) {
+            return;
+        }
+        Set<Long> sourceParamIdSet = paramList.stream()
+                .filter(Objects::nonNull)
+                .map(ProductionOrderRoutingOperationParam::getId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toCollection(LinkedHashSet::new));
+        if (sourceParamIdSet.isEmpty()) {
+            return;
+        }
+
+        List<ProductionOrderRoutingOperationParam> dbParamList = productionOrderRoutingOperationParamMapper.selectList(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .in(ProductionOrderRoutingOperationParam::getId, sourceParamIdSet)
+                        .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());
+            }
+            productionOrderRoutingOperationParamMapper.insert(buildReportParamSnapshot(dbParam, param.getInputValue(), productionProductMainId));
+        }
+    }
+
+    private ProductionOrderRoutingOperationParam buildReportParamSnapshot(ProductionOrderRoutingOperationParam source,
+                                                                          String inputValue,
+                                                                          Long productionProductMainId) {
+        ProductionOrderRoutingOperationParam target = new ProductionOrderRoutingOperationParam();
+        target.setProductionOrderId(source.getProductionOrderId());
+        target.setTechnologyRoutingOperationParamId(source.getTechnologyRoutingOperationParamId());
+        target.setParamCode(source.getParamCode());
+        target.setParamName(source.getParamName());
+        target.setParamType(source.getParamType());
+        target.setParamFormat(source.getParamFormat());
+        target.setUnit(source.getUnit());
+        target.setIsRequired(source.getIsRequired());
+        target.setRemark(source.getRemark());
+        target.setParamId(source.getParamId());
+        target.setTechnologyOperationId(source.getTechnologyOperationId());
+        target.setTechnologyOperationParamId(source.getTechnologyOperationParamId());
+        target.setStandardValue(source.getStandardValue());
+        target.setInputValue(inputValue);
+        target.setProductionOrderRoutingOperationId(source.getProductionOrderRoutingOperationId());
+        target.setProductionProductMainId(productionProductMainId);
+        return target;
+    }
+
     private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) {
-        // 鍒犻櫎鎶ュ伐闇�瑕佸悓姝ュ洖婊氳川妫�銆佸簱瀛樸�佸伐鏃舵牳绠楀拰璁㈠崟/宸ュ崟杩涘害銆�
+        // 鎸夌敓浜т换鍔″洖婊氬苟鍒犻櫎鎶ュ伐涓昏褰�
         List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
                 Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, productionProductMain.getId()));
+        // 鍙傛暟涓庡墠缃潯浠舵牎楠�
         if (qualityInspects.size() > 0) {
             List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList(
                     Wrappers.<QualityUnqualified>lambdaQuery()
+        // 閬嶅巻澶勭悊鏁版嵁骞剁粍瑁呯粨鏋�
                             .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList())));
             if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) {
                 throw new ServiceException("璇ユ潯鎶ュ伐宸茬粡涓嶅悎鏍煎鐞嗕簡锛屼笉鍏佽鍒犻櫎");
@@ -283,8 +646,8 @@
 
         ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(productionProductMain.getProductionOperationTaskId());
         if (productionOperationTask != null && productionProductOutput != null) {
-            BigDecimal validQuantity = defaultDecimal(productionProductOutput.getQuantity()).subtract(defaultDecimal(productionProductOutput.getScrapQty()));
-            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(validQuantity));
+            BigDecimal reportQuantity = defaultDecimal(productionProductOutput.getQuantity());
+            productionOperationTask.setCompleteQuantity(defaultDecimal(productionOperationTask.getCompleteQuantity()).subtract(reportQuantity));
             productionOperationTask.setActualEndTime(null);
             if (defaultDecimal(productionOperationTask.getCompleteQuantity()).compareTo(BigDecimal.ZERO) <= 0) {
                 productionOperationTask.setCompleteQuantity(BigDecimal.ZERO);
@@ -293,19 +656,20 @@
             } 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);
+                    BigDecimal newCompleteQty = defaultDecimal(productionOrder.getCompleteQuantity()).subtract(reportQuantity);
                     productionOrder.setCompleteQuantity(newCompleteQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newCompleteQty);
                     productionOrder.setEndTime(null);
                 }
@@ -330,7 +694,10 @@
                 .eq(ProductionProductOutput::getProductionProductMainId, productionProductMain.getId()));
         productionProductInputMapper.delete(new LambdaQueryWrapper<ProductionProductInput>()
                 .eq(ProductionProductInput::getProductionProductMainId, productionProductMain.getId()));
-        stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode());
+        productionOrderRoutingOperationParamMapper.delete(
+                Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery()
+                        .eq(ProductionOrderRoutingOperationParam::getProductionProductMainId, productionProductMain.getId()));
+        stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode());
         stockUtils.deleteStockInRecord(productionProductMain.getId(), StockInQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_IN.getCode());
         stockUtils.deleteStockOutRecord(productionProductMain.getId(), StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
         productionProductMainMapper.deleteById(productionProductMain.getId());
@@ -338,6 +705,7 @@
     }
 
     private String generateProductNo() {
+        // 鐢熸垚涓嬩竴涓敓浜т骇鍝佺紪鍙�
         String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd"));
         QueryWrapper<ProductionProductMain> queryWrapper = new QueryWrapper<>();
         queryWrapper.select("MAX(product_no) as maxNo").likeRight("product_no", datePrefix);
@@ -360,11 +728,21 @@
     }
 
     private BigDecimal defaultDecimal(BigDecimal value) {
+        // 灏嗙┖鏁伴噺鍏滃簳涓�0锛岄伩鍏嶇┖鎸囬拡寮傚父
         return value == null ? BigDecimal.ZERO : value;
+    }
+
+    private Long resolveTaskId(ProductionProductMainDto dto) {
+        // 浠庡叆鍙備腑瑙f瀽鐢熶骇宸ュ崟ID骞舵牎楠�
+        if (dto == null) {
+            return null;
+        }
+        return dto.getProductionOperationTaskId();
     }
 
     @Override
     public ArrayList<Long> listMain(List<Long> idList) {
+        // 鏌ヨ涓昏〃ID闆嗗悎
         return productionProductMainMapper.listMain(idList);
     }
 }

--
Gitblit v1.9.3