From 74ad846852f42b4e2258bfddd5c2a33cc1ce9847 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 29 八月 2026 18:03:46 +0800
Subject: [PATCH] feat(mes): 新增工序等待时长和报工数量配置功能

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java |   98 ++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 80 insertions(+), 18 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 016e10a..489c6b4 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
@@ -12,6 +12,7 @@
 import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackTypeEnum;
 import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
@@ -24,6 +25,7 @@
 import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
+import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
 import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
@@ -33,6 +35,7 @@
 import cn.iocoder.yudao.module.mes.service.wm.itemconsume.MesWmItemConsumeService;
 import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService;
 import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceService;
+import cn.iocoder.yudao.module.mes.service.wm.productreceipt.MesWmProductReceiptService;
 import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
 import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemBatchConfigApi;
@@ -48,6 +51,7 @@
 import org.springframework.validation.annotation.Validated;
 
 import java.math.BigDecimal;
+import java.time.Duration;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -78,6 +82,8 @@
     @Resource
     private MesProRouteProcessService routeProcessService;
     @Resource
+    private MesProProcessService processService;
+    @Resource
     private MesProWorkOrderBomService workOrderBomService;
     @Resource
     private MesProWorkOrderProcessService workOrderProcessService;
@@ -88,6 +94,9 @@
     private MesWmItemConsumeService itemConsumeService;
     @Resource
     private MesWmProductProduceService productProduceService;
+    @Resource
+    @Lazy // 閬垮厤寰幆渚濊禆锛氫骇鍝佹敹璐у崟鑷姩鐢熸垚閫昏緫渚濊禆鎶ュ伐 Service
+    private MesWmProductReceiptService productReceiptService;
     @Resource
     private MesWmProductProduceLineService produceLineService;
     @Resource
@@ -266,13 +275,17 @@
     public boolean approveFeedback(MesProFeedbackApproveReqVO reqVO) {
         // 1.1 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
         MesProFeedbackDO feedback = validateFeedbackStatusApproving(reqVO.getId());
-        // 1.2 鏍¢獙鎶ュ伐鏁伴噺 > 0
-        if (feedback.getFeedbackQuantity() == null
-                || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
-            throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+        // 1.2 鏍¢獙浠诲姟鏈畬鎴� + 绛夊緟鏃堕暱鍊掕鏃�
+        MesProTaskDO task = taskService.validateTaskNotFinished(feedback.getTaskId());
+        MesProProcessDO process = validateWaitDuration(task);
+        BigDecimal fbQty = feedback.getFeedbackQuantity() == null
+                ? BigDecimal.ZERO : feedback.getFeedbackQuantity();
+        // 1.3 鏍¢獙鎶ュ伐鏁伴噺锛氶粯璁ゅ繀椤� > 0锛涘伐搴忛厤缃�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鏃跺厑璁镐负绌�
+        if (process == null || !Boolean.FALSE.equals(process.getNeedReportQuantity())) {
+            if (fbQty.compareTo(BigDecimal.ZERO) <= 0) {
+                throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+            }
         }
-        // 1.3 鏍¢獙浠诲姟鏈畬鎴�
-        taskService.validateTaskNotFinished(feedback.getTaskId());
 
         // 2.1 鏌ヨ宸ュ簭鐨勫叧閿伐搴忔爣璇� + 妫�楠屾爣璇嗭紙闇�鍦� uncheckQuantity 鏍¢獙涔嬪墠锛屽洜涓鸿川妫�宸ュ簭鍏佽 uncheckQuantity > 0锛�
         MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
@@ -280,13 +293,14 @@
         boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag());
         boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag());
         // 2.2 闈炶川妫�宸ュ簭锛氬悎鏍煎搧/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛涙湰宸ュ簭涓嶇粡杩囪川妫�锛屾姤宸ユ湭濉啓鏃堕粯璁ゅ叏閮ㄤ负鍚堟牸鍝�
+        //     锛堟暟閲忎负绌�/0 鏃舵寜 0 澶勭悊锛岄伩鍏嶅啓鍏� null锛�
         if (!checkFlag
                 && feedback.getQualifiedQuantity() == null
                 && feedback.getUnqualifiedQuantity() == null) {
-            feedback.setQualifiedQuantity(feedback.getFeedbackQuantity());
+            feedback.setQualifiedQuantity(fbQty);
             feedback.setUnqualifiedQuantity(BigDecimal.ZERO);
             feedbackMapper.updateById(new MesProFeedbackDO().setId(feedback.getId())
-                    .setQualifiedQuantity(feedback.getFeedbackQuantity())
+                    .setQualifiedQuantity(fbQty)
                     .setUnqualifiedQuantity(BigDecimal.ZERO));
         }
         // 2.3 闈炶川妫�宸ュ簭锛氫粛鏈夊緟妫�鏁伴噺鏃朵笉鑳藉鎵癸紙璐ㄦ宸ュ簭鐨� uncheckQuantity > 0 鏄甯哥姸鎬侊紝涓嶅仛鎷︽埅锛�
@@ -297,8 +311,9 @@
         }
 
         // 3. 鐗╂枡娑堣�楋細鍊掑啿宸ュ簭鎸� BOM 鑷姩鎵f枡锛涢潪鍊掑啿宸ュ簭璺宠繃锛堢墿鏂欒蛋鎵嬪伐棰嗘枡/鎶曟枡锛�
+        //    鎶ュ伐鏁伴噺涓虹┖/0 鏃惰烦杩囧�掑啿鎵f枡锛岄伩鍏嶇┖鏁伴噺璇墸绾胯竟搴�
         boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag());
-        if (backflushFlag) {
+        if (backflushFlag && fbQty.compareTo(BigDecimal.ZERO) > 0) {
             // 3.1 鏍¢獙鍊掑啿宸ュ簭蹇呴』閰嶇疆 BOM 娑堣�楋紝閬垮厤闈欓粯涓嶆墸鏂�
             List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderId(
                     feedback.getWorkOrderId());
@@ -316,6 +331,14 @@
         feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
                 .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
                 .setUncheckQuantity(BigDecimal.ZERO));
+
+        // 5.1 鎶ュ伐鏁伴噺涓虹┖/0锛�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鐨勫伐搴忥級锛氭棤浜у嚭鍙叆搴擄紝浠呮洿鏂颁换鍔¤繘搴﹀苟缁撴潫
+        if (fbQty.compareTo(BigDecimal.ZERO) <= 0) {
+            taskService.updateProducedQuantity(feedback.getTaskId(),
+                    BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);
+            checkAndPublishProductionFinished(feedback.getWorkOrderId());
+            return true;
+        }
 
         // 6. 鏍规嵁鏄惁闇�瑕佹楠岋紝鍐冲畾浜у嚭鍜屽叆搴撴柟寮�
         // 6.2 璋冩暣 鍙瀹℃壒鎴愬姛鐩存帴鏇存柊
@@ -341,6 +364,8 @@
             // 6.2 鏃犻渶妫�楠岋細鐢熸垚浜у嚭鍗曪紙鎸夊悎鏍�/涓嶅悎鏍兼媶琛岋級锛岀洿鎺ュ畬鎴愬叆搴�
             MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO);
             productProduceService.finishProductProduce(produce.getId());
+            // 6.3 鎶ュ伐浜у嚭瀹氱鍚庤嚜鍔ㄧ敓鎴愪骇鍝佸叆搴撳崟锛堜竴娆℃姤宸�=涓�鏉★紝骞傜瓑锛�
+            productReceiptService.createProductReceiptByFeedback(feedback.getId());
 
         }
 
@@ -477,14 +502,47 @@
         if (routeProcess == null) {
             throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
         }
-        // 4.1 鏍¢獙鏁伴噺锛氭棤璁烘槸鍚﹂渶瑕佹楠岋紝鎶ュ伐鍙渶濉姤宸ユ暟閲忥紝涓斿繀椤� > 0
+        // 4. 鏍¢獙绛夊緟鏃堕暱鍊掕鏃讹細宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀匡紙waitDuration > 0锛夋椂锛岄渶鍏堝紑濮嬪�掕鏃跺苟绛夊緟缁撴潫
+        MesProProcessDO process = validateWaitDuration(task);
+        // 4.1 鏍¢獙鏁伴噺锛氶粯璁ゅ繀椤� > 0锛涘伐搴忛厤缃�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鏃跺厑璁镐负绌�
         // 鍚堟牸鍝�/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛堥潪璐ㄦ宸ュ簭鍦ㄥ鎵规椂榛樿鍏ㄩ儴涓哄悎鏍煎搧锛�
-        if (reqVO.getFeedbackQuantity() == null
-                || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
-            throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+        if (process == null || !Boolean.FALSE.equals(process.getNeedReportQuantity())) {
+            if (reqVO.getFeedbackQuantity() == null
+                    || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+                throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+            }
         }
 
         return task;
+    }
+
+    /**
+     * 鏍¢獙宸ュ簭绛夊緟鏃堕暱鍊掕鏃讹紙鎶ュ伐鍓嶇疆鏉′欢锛�
+     *
+     * <p>璇诲彇浠诲姟鍏宠仈鐨勫伐搴忎富鏁版嵁 waitDuration锛堝皬鏃讹級锛氳嫢鏈厤缃垨涓� 0 鍒欑洿鎺ヨ繑鍥烇紱閰嶇疆浜嗗垯闇�瑕�
+     * 浠诲姟宸茬偣鍑�"寮�濮嬪�掕鏃�"涓斿�掕鏃跺凡缁撴潫锛屽惁鍒欐姏鍑哄搴斿紓甯搞�傝繑鍥炲伐搴忎富鏁版嵁锛屼緵璋冪敤鏂瑰鐢�
+     * needReportQuantity 绛夊瓧娈点��
+     *
+     * @param task 鐢熶骇浠诲姟
+     * @return 宸ュ簭涓绘暟鎹�
+     */
+    private MesProProcessDO validateWaitDuration(MesProTaskDO task) {
+        MesProProcessDO process = processService.getProcess(task.getProcessId());
+        if (process == null || process.getWaitDuration() == null
+                || process.getWaitDuration().compareTo(BigDecimal.ZERO) <= 0) {
+            return process; // 鏈厤缃瓑寰呮椂闀匡紝鏃犻渶鍊掕鏃�
+        }
+        if (task.getWaitStartTime() == null) {
+            throw exception(PRO_TASK_WAIT_NOT_STARTED); // 璇峰厛鐐瑰嚮"寮�濮嬪�掕鏃�"
+        }
+        LocalDateTime readyTime = task.getWaitStartTime()
+                .plusMinutes((long) (process.getWaitDuration().doubleValue() * 60));
+        if (LocalDateTime.now().isBefore(readyTime)) {
+            Duration remaining = Duration.between(LocalDateTime.now(), readyTime);
+            throw exception(PRO_TASK_WAIT_NOT_ELAPSED,
+                    remaining.toHours() + "灏忔椂" + remaining.toMinutesPart() + "鍒嗛挓");
+        }
+        return process;
     }
 
     @Override
@@ -504,12 +562,16 @@
         if (routeProcess == null) {
             throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
         }
-        // 3. 鏍¢獙鏁伴噺锛氬彧濉姤宸ユ暟閲忥紝涓斿繀椤� > 0锛堝悎鏍煎搧/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛�
-        if (reqVO.getFeedbackQuantity() == null
-                || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
-            throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+        // 3. 鏍¢獙绛夊緟鏃堕暱鍊掕鏃讹細宸ュ簭閰嶇疆浜嗙瓑寰呮椂闀匡紙waitDuration > 0锛夋椂锛岄渶鍏堝紑濮嬪�掕鏃跺苟绛夊緟缁撴潫
+        MesProProcessDO process = validateWaitDuration(task);
+        // 4. 鏍¢獙鏁伴噺锛氶粯璁ゅ繀椤� > 0锛涘伐搴忛厤缃�"涓嶉渶瑕佸~鍐欐姤宸ユ暟閲�"鏃跺厑璁镐负绌猴紙鍚堟牸鍝�/涓嶈壇鍝佹暟閲忕敱璐ㄦ纭畾锛�
+        if (process == null || !Boolean.FALSE.equals(process.getNeedReportQuantity())) {
+            if (reqVO.getFeedbackQuantity() == null
+                    || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+                throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+            }
         }
-        // 4. 鏋勫缓鎶ュ伐DO
+        // 5. 鏋勫缓鎶ュ伐DO
         MesProFeedbackDO feedback = new MesProFeedbackDO()
                 .setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_FEEDBACK_CODE.getCode()))
                 .setType(MesProFeedbackTypeEnum.SELF.getType())

--
Gitblit v1.9.3