From ffca46b39d25439a06a27cafda2ec523a3fb67cf Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 27 十月 2026 17:53:02 +0800
Subject: [PATCH] feat(mes): 增加盘号字段支持和优化报工质检流程

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java |  123 +++++++++++++++++++----------------------
 1 files changed, 57 insertions(+), 66 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
index 48db7a0..1c58708 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -71,6 +71,9 @@
 public class MesProFeedbackServiceImpl implements MesProFeedbackService {
 
     @Resource
+    private MesProFeedbackEquipmentService equipmentService;
+
+    @Resource
     private MesProFeedbackMapper feedbackMapper;
 
     @Resource
@@ -114,8 +117,9 @@
         // 1. 鏍¢獙
         MesProTaskDO task = validateFeedbackData(createReqVO);
 
-        // 2. 鎻掑叆锛堣嚜鍔ㄥ~鍏� itemId锛�
+        // 2. 鎻掑叆锛堜富閿敱鏁版嵁搴撹嚜澧烇紝蹇界暐璇锋眰涓彲鑳芥畫鐣欑殑 id锛涜嚜鍔ㄥ~鍏� itemId锛�
         MesProFeedbackDO feedback = BeanUtils.toBean(createReqVO, MesProFeedbackDO.class)
+                .setId(null)
                 .setStatus(MesProFeedbackStatusEnum.PREPARE.getStatus())
                 .setItemId(task.getItemId());
         feedbackMapper.insert(feedback);
@@ -127,6 +131,7 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void updateFeedback(MesProFeedbackSaveReqVO updateReqVO) {
+        equipmentService.lock();
         // 1.1 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
         validateFeedbackStatusPrepare(updateReqVO.getId());
         // 1.2 鏍¢獙涓氬姟鏁版嵁
@@ -136,6 +141,7 @@
         MesProFeedbackDO updateObj = BeanUtils.toBean(updateReqVO, MesProFeedbackDO.class)
                 .setItemId(task.getItemId());
         feedbackMapper.updateById(updateObj);
+        equipmentService.resetSnapshot(updateReqVO.getId());
         // 3. 淇濆瓨鍙傛暟鍊硷紱蹇呭~鍙傛暟鏍¢獙澶辫触鏃舵暣浣撳洖婊�
         saveParamValues(task.getId(), updateReqVO.getParamValues());
     }
@@ -152,7 +158,9 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void deleteFeedback(Long id) {
+        equipmentService.lock();
         // 1. 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
         validateFeedbackStatusPrepare(id);
 
@@ -186,9 +194,11 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void submitFeedback(MesProFeedbackSubmitReqVO reqVO) {
+        equipmentService.lock();
         Long id = reqVO.getId();
         // 1. 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
         MesProFeedbackDO feedback = validateFeedbackStatusPrepare(id);
+        equipmentService.validateSubmission(feedback);
         // 1.1 椤哄簭鎶ュ伐鏍¢獙锛氬悓涓�鐢熶骇宸ュ崟鍐咃紝涓婁竴閬撳伐搴忓繀椤诲凡鎶ュ伐瀹屾垚锛屽惁鍒欎笉鍏佽鎻愪氦
         if (feedback.getTaskId() != null) {
             MesProTaskDO submitTask = taskService.getTask(feedback.getTaskId());
@@ -199,7 +209,9 @@
 
         MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId());
 
-        if (workOrder != null && Boolean.TRUE.equals(workOrder.getFeedbackApprove()) && cn.hutool.core.util.StrUtil.isNotBlank(workOrder.getProcessDefinitionKey())) {
+        if (!processInstanceApi.isGlobalAutoApprove() && workOrder != null
+                && Boolean.TRUE.equals(workOrder.getFeedbackApprove())
+                && cn.hutool.core.util.StrUtil.isNotBlank(workOrder.getProcessDefinitionKey())) {
             // 2. 鏇存柊鐘舵�佷负瀹℃壒涓紝骞跺惎鍔ㄦ祦绋�
             String processInstanceId = processInstanceApi.createProcessInstance(cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId(),
                     new cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO()
@@ -264,7 +276,9 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public void rejectFeedback(Long id) {
+        equipmentService.lock();
         // 1. 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
         validateFeedbackStatusApproving(id);
 
@@ -276,8 +290,16 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public boolean approveFeedback(MesProFeedbackApproveReqVO reqVO) {
+        equipmentService.lock();
         // 1.1 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
         MesProFeedbackDO feedback = validateFeedbackStatusApproving(reqVO.getId());
+        equipmentService.validateApproval(feedback);
+        try (var equipmentScope = equipmentService.beginApproval(feedback)) {
+            return approveValidatedFeedback(reqVO, feedback);
+        }
+    }
+
+    private boolean approveValidatedFeedback(MesProFeedbackApproveReqVO reqVO, MesProFeedbackDO feedback) {
         // 1.2 鏍¢獙鎶ュ伐鏁伴噺 > 0
         if (feedback.getFeedbackQuantity() == null
                 || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
@@ -286,27 +308,10 @@
         // 1.3 鏍¢獙浠诲姟鏈畬鎴�
         taskService.validateTaskNotFinished(feedback.getTaskId());
 
-        // 2.1 鏌ヨ宸ュ簭鐨勫叧閿伐搴忔爣璇� + 妫�楠屾爣璇嗭紙闇�鍦� uncheckQuantity 鏍¢獙涔嬪墠锛屽洜涓鸿川妫�宸ュ簭鍏佽 uncheckQuantity > 0锛�
+        // 宸ュ簭妫�楠屾爣璇嗕笉鍐嶆帶鍒朵骇鍑哄叆搴擄細鎵�鏈夋柊鎶ュ伐浜у嚭鍧囬渶浜哄伐璐ㄦ銆�
         MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
                 feedback.getRouteId(), feedback.getProcessId());
         boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());
-        boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
-        // 2.2 闈炶川妫�宸ュ簭锛氬悎鏍煎搧/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛涙湰宸ュ簭涓嶇粡杩囪川妫�锛屾姤宸ユ湭濉啓鏃堕粯璁ゅ叏閮ㄤ负鍚堟牸鍝�
-        if (!checkFlag
-                && feedback.getQualifiedQuantity() == null
-                && feedback.getUnqualifiedQuantity() == null) {
-            feedback.setQualifiedQuantity(feedback.getFeedbackQuantity());
-            feedback.setUnqualifiedQuantity(BigDecimal.ZERO);
-            feedbackMapper.updateById(new MesProFeedbackDO().setId(feedback.getId())
-                    .setQualifiedQuantity(feedback.getFeedbackQuantity())
-                    .setUnqualifiedQuantity(BigDecimal.ZERO));
-        }
-        // 2.3 闈炶川妫�宸ュ簭锛氫粛鏈夊緟妫�鏁伴噺鏃朵笉鑳藉鎵癸紙璐ㄦ宸ュ簭鐨� uncheckQuantity > 0 鏄甯哥姸鎬侊紝涓嶅仛鎷︽埅锛�
-        if (!checkFlag
-                && feedback.getUncheckQuantity() != null
-                && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) {
-            throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity());
-        }
 
         // 3. 鐗╂枡娑堣�楋細鍊掑啿宸ュ簭鎸夋湰宸ュ簭鎶曟枡 BOM 鑷姩鎵f枡锛涢潪鍊掑啿宸ュ簭璺宠繃锛堢墿鏂欒蛋鎵嬪伐棰嗘枡/鎶曟枡锛�
         boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag());
@@ -325,10 +330,14 @@
             }
         }
 
-        // 5. 鍏堟洿鏂版姤宸ョ姸鎬佷负宸插畬鎴愶紙纭繚 checkAndFinishMps 妫�鏌ユ椂褰撳墠鎶ュ伐宸叉爣璁板畬鎴愶級
+        // 鎶ュ伐涓庤川妫�鍒嗗紑锛氭姤宸ュ鎵瑰畬鎴愬嵆绱浜ч噺锛岃川閲忔暟閲忕敱璐ㄦ鍥炲~銆�
         feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
-                .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
-                .setUncheckQuantity(BigDecimal.ZERO));
+                .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())
+                .setQualifiedQuantity(BigDecimal.ZERO)
+                .setUnqualifiedQuantity(BigDecimal.ZERO)
+                .setUncheckQuantity(feedback.getFeedbackQuantity()));
+        feedback.setQualifiedQuantity(BigDecimal.ZERO).setUnqualifiedQuantity(BigDecimal.ZERO);
+        productProduceService.generateProductProduce(feedback, true, reqVO);
 
         // 6. 鏍规嵁鏄惁闇�瑕佹楠岋紝鍐冲畾浜у嚭鍜屽叆搴撴柟寮�
         // 6.2 璋冩暣 鍙瀹℃壒鎴愬姛鐩存帴鏇存柊
@@ -343,21 +352,8 @@
                 checkAndPublishProductionFinished(feedback.getWorkOrderId());
 
         }
-        if (checkFlag) {
-            // 6.1 闇�瑕佹楠岋細鐢熸垚浜у嚭鍗曪紙璐ㄩ噺鐘舵��=寰呮楠岋級锛屾洿鏂版姤宸ョ姸鎬佷负寰呮楠岋紝绛夎川妫�瀹屾垚鍥炶皟鍚庡啀鍏ュ簱
-            // 瀹屾垚鏃跺洖璋冭锛歁esQcIpqcServiceImpl#finishIpqc 鈫� splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish
-            productProduceService.generateProductProduce(feedback, true, reqVO);
-            feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
-                    .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()));
-            return false;
-        } else {
-            // 6.2 鏃犻渶妫�楠岋細鐢熸垚浜у嚭鍗曪紙鎸夊悎鏍�/涓嶅悎鏍兼媶琛岋級锛岀洿鎺ュ畬鎴愬叆搴�
-            MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO);
-            productProduceService.finishProductProduce(produce.getId());
-
-        }
-
-        return true; // 宸插畬鎴�
+        equipmentService.afterApproved(feedback);
+        return false; // 浜у嚭绛夊緟璐ㄦ锛屾姤宸ヤ骇閲忓凡绱
     }
 
     /**
@@ -373,28 +369,9 @@
      * @param feedback 鎶ュ伐鍗�
      */
     private void updateTaskAndWorkOrderByFeedback(MesProFeedbackDO feedback) {
-        // 1. 鏌ヨ璇ユ姤宸ュ崟鍏宠仈鐨勬墍鏈変骇鍑哄崟琛岋紝鎸夎川閲忕姸鎬佽仛鍚堟暟閲�
-        BigDecimal qualifiedQty = BigDecimal.ZERO;
-        BigDecimal unqualifiedQty = BigDecimal.ZERO;
-        List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByFeedbackId(feedback.getId());
-        for (MesWmProductProduceLineDO line : lines) {
-            if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) {
-                unqualifiedQty = unqualifiedQty.add(line.getQuantity());
-            }
-            if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())) {
-                qualifiedQty = qualifiedQty.add(line.getQuantity());
-            }
-        }
-        // 1.1 鍏滃簳锛氬叧閿潪璐ㄦ宸ュ簭瀹℃壒鏃朵骇鍑哄崟琛屽皻鏈敓鎴愶紝鐩存帴閲囩敤鎶ュ伐鍗曚笂鐨勫悎鏍�/涓嶈壇鏁伴噺
-        //     锛堥潪璐ㄦ宸ュ簭榛樿鍏ㄩ儴涓哄悎鏍煎搧锛涜川妫�宸ュ簭鐨勫悎鏍�/涓嶈壇鍦ㄨ川妫�鍥炶皟鍚庢寜浜у嚭琛岃仛鍚堬級
-        if (CollUtil.isEmpty(lines)) {
-            qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO);
-            unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO);
-        }
-
-        //   2. 鏇存柊浠诲姟鐨勫凡鐢熶骇/鍚堟牸/涓嶅悎鏍兼暟閲�
+        // 鎶ュ伐瀹℃壒鍙疮璁′骇閲忥紱鍚堟牸涓庝笉鍚堟牸鏁伴噺鍦ㄨ川妫�瀹屾垚鏃剁疮璁°��
         taskService.updateProducedQuantity(feedback.getTaskId(),
-                feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty);
+                feedback.getFeedbackQuantity(), BigDecimal.ZERO, BigDecimal.ZERO);
         // 3. 鏇存柊璁㈠崟鐨勫凡鐢熶骇鏁伴噺
         // 鏂板閫昏緫锛氬彧鏈夊湪璇ョ瑪璁㈠崟鏈�鍚庝竴涓伐搴忔墠鏇存柊鐢熶骇璁㈠崟鐨勫凡鐢熶骇鏁伴噺
         MesProWorkOrderProcessDO lastProcess = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId());
@@ -419,12 +396,16 @@
     private void checkAndPublishProductionFinished(Long workOrderId) {
         // 1. 鑾峰彇宸ュ崟鍏宠仈鐨� MPS
         MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(workOrderId);
-        if (mps == null || mps.getSaleOrderId() == null) {
+        if (mps == null || mps.getSaleOrderId() == null
+                || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.FINISHED.getStatus())
+                || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.CANCELED.getStatus())) {
             return; // 闈為攢鍞鍗曟潵婧愮殑宸ュ崟锛岃烦杩�
         }
 
         // 2. 妫�鏌ュ苟鑷姩瀹屾垚 MPS锛堝鏋滄弧瓒虫潯浠讹級
-        mpsApi.checkAndFinishMps(mps.getId());
+        if (!Boolean.TRUE.equals(mpsApi.checkAndFinishMps(mps.getId()))) {
+            return;
+        }
 
         // 3. 妫�鏌ラ攢鍞鍗曟墍鏈夌敓浜ф槸鍚﹀畬鎴�
         if (mpsApi.checkSaleOrderProductionFinished(mps.getSaleOrderId())) {
@@ -491,7 +472,7 @@
             throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
         }
         // 4.1 鏍¢獙鏁伴噺锛氭棤璁烘槸鍚﹂渶瑕佹楠岋紝鎶ュ伐鍙渶濉姤宸ユ暟閲忥紝涓斿繀椤� > 0
-        // 鍚堟牸鍝�/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛堥潪璐ㄦ宸ュ簭鍦ㄥ鎵规椂榛樿鍏ㄩ儴涓哄悎鏍煎搧锛�
+        // 鍚堟牸鍝�/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾銆�
         if (reqVO.getFeedbackQuantity() == null
                 || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
             throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
@@ -568,6 +549,7 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public MesProFeedbackQuickCreateRespVO quickCreateFeedback(MesProFeedbackQuickCreateReqVO reqVO) {
+        equipmentService.lock();
         // 1. 鏍¢獙浠诲姟瀛樺湪涓旈潪缁堟��
         MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
 
@@ -601,9 +583,10 @@
                 .setItemId(task.getItemId())
                 .setFeedbackQuantity(reqVO.getFeedbackQuantity())
                 .setScheduledQuantity(task.getQuantity())
-                .setFeedbackTime(LocalDateTime.now())
+                .setFeedbackTime(reqVO.getFeedbackTime() != null ? reqVO.getFeedbackTime() : LocalDateTime.now())
                 .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus())
                 .setRemark(reqVO.getRemark());
+        equipmentService.validateSubmission(feedback);
         feedbackMapper.insert(feedback);
         saveParamValues(task.getId(), reqVO.getParamValues());
         // 5. 杩斿洖缁撴灉
@@ -613,7 +596,8 @@
         BigDecimal produced = task.getProducedQuantity() == null ? BigDecimal.ZERO : task.getProducedQuantity();
         BigDecimal inTransit = BigDecimal.ZERO;
         for (MesProFeedbackDO fb : getFeedbackListByTaskId(task.getId())) {
-            if (!ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.FINISHED.getStatus())) {
+            if (!ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.FINISHED.getStatus())
+                    && !ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) {
                 inTransit = inTransit.add(fb.getFeedbackQuantity() == null ? BigDecimal.ZERO : fb.getFeedbackQuantity());
             }
         }
@@ -636,6 +620,7 @@
                 MesProFeedbackQuickCreateReqVO singleReq = new MesProFeedbackQuickCreateReqVO();
                 singleReq.setTaskId(taskId);
                 singleReq.setFeedbackQuantity(reqVO.getFeedbackQuantity());
+                singleReq.setFeedbackTime(reqVO.getFeedbackTime());
                 singleReq.setRemark(reqVO.getRemark());
                 MesProFeedbackQuickCreateRespVO resp = quickCreateFeedback(singleReq);
                 results.add(new MesProFeedbackBatchCreateRespVO.BatchResult()
@@ -663,7 +648,10 @@
                                                 BigDecimal qualifiedQty, BigDecimal unqualifiedQty,
                                                 BigDecimal laborScrapQty, BigDecimal materialScrapQty, BigDecimal otherScrapQty) {
         // 1. 鏍¢獙鎶ュ伐鍗曞瓨鍦ㄤ笖涓哄緟妫�楠岀姸鎬�
-        MesProFeedbackDO feedback = validateFeedbackExists(feedbackId);
+        MesProFeedbackDO feedback = feedbackMapper.selectByIdForUpdate(feedbackId);
+        if (feedback == null) {
+            throw exception(PRO_FEEDBACK_NOT_EXISTS);
+        }
         if (ObjUtil.notEqual(feedback.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) {
             throw exception(PRO_FEEDBACK_NOT_UNCHECK);
         }
@@ -681,8 +669,11 @@
                 .setOtherScrapQuantity(otherScrapQty)
                 .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()));
 
-        // 4. 鏇存柊浠诲姟/宸ュ崟鐨勫凡鐢熶骇鏁伴噺
+        // 4. 浜ч噺宸插湪鎶ュ伐瀹℃壒鏃剁疮璁★紝姝ゅ鍙洖濉川閲忔暟閲忋��
         feedback.setQualifiedQuantity(qualifiedQty).setUnqualifiedQuantity(unqualifiedQty);
+        taskService.updateQualityQuantity(feedback.getTaskId(),
+                ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO),
+                ObjectUtil.defaultIfNull(unqualifiedQty, BigDecimal.ZERO));
         //5.妫�鏌ュ苟鍙戝竷鐢熶骇瀹屾垚浜嬩欢
         checkAndPublishProductionFinished(feedback.getWorkOrderId());
     }

--
Gitblit v1.9.3