From 3ca1cefd9bed863131ee3f684ff127bd7ac877aa Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 05 八月 2026 11:35:16 +0800
Subject: [PATCH] feat(ai): 优化AI知识文档处理功能

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java |  225 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 195 insertions(+), 30 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 7e1868e..64d5383 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
@@ -6,12 +6,14 @@
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO;
-import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.*;
+import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
+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.route.MesProRouteProcessDO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO;
@@ -20,12 +22,16 @@
 import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
-import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
 import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
 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.mdm.MesMdmItemService;
+import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemBatchConfigApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemBatchConfigRespDTO;
 import cn.iocoder.yudao.module.mes.api.mps.MesProMpsApi;
 import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsRespDTO;
 import cn.iocoder.yudao.module.mes.api.event.ProductionFinishedEvent;
@@ -37,6 +43,8 @@
 import org.springframework.validation.annotation.Validated;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -61,7 +69,7 @@
     @Resource
     private MesProRouteProcessService routeProcessService;
     @Resource
-    private MesProRouteProductBomService routeProductBomService;
+    private MesProWorkOrderBomService workOrderBomService;
     @Resource
     @Lazy // 閬垮厤寰幆渚濊禆
     private MesProTaskService taskService;
@@ -74,7 +82,13 @@
     @Resource
     private MesProMpsApi mpsApi;
     @Resource
+    private MesMdAutoCodeRecordService autoCodeRecordService;
+    @Resource
     private ApplicationEventPublisher eventPublisher;
+    @Resource
+    private MesMdmItemService mdmItemService;
+    @Resource
+    private MdmItemBatchConfigApi itemBatchConfigApi;
 
     @Override
     public Long createFeedback(MesProFeedbackSaveReqVO createReqVO) {
@@ -156,9 +170,9 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean approveFeedback(Long id) {
+    public boolean approveFeedback(MesProFeedbackApproveReqVO reqVO) {
         // 1.1 鏍¢獙瀛樺湪 + 瀹℃壒涓姸鎬�
-        MesProFeedbackDO feedback = validateFeedbackStatusApproving(id);
+        MesProFeedbackDO feedback = validateFeedbackStatusApproving(reqVO.getId());
         // 1.2 鏍¢獙鎶ュ伐鏁伴噺 > 0
         if (feedback.getFeedbackQuantity() == null
                 || feedback.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
@@ -183,8 +197,8 @@
         boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag());
         if (backflushFlag) {
             // 3.1 鏍¢獙鍊掑啿宸ュ簭蹇呴』閰嶇疆 BOM 娑堣�楋紝閬垮厤闈欓粯涓嶆墸鏂�
-            List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList(
-                    feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId());
+            List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderId(
+                    feedback.getWorkOrderId());
             if (CollUtil.isEmpty(boms)) {
                 throw exception(PRO_FEEDBACK_BACKFLUSH_BOM_REQUIRED);
             }
@@ -196,32 +210,32 @@
         }
 
         // 5. 鍏堟洿鏂版姤宸ョ姸鎬佷负宸插畬鎴愶紙纭繚 checkAndFinishMps 妫�鏌ユ椂褰撳墠鎶ュ伐宸叉爣璁板畬鎴愶級
-        feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
+        feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
                 .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())
                 .setUncheckQuantity(BigDecimal.ZERO));
 
-        // 6. 鍏抽敭宸ュ簭锛氱敓鎴愪骇鍑哄崟锛屽苟鏍规嵁鏄惁闇�瑕佹楠屽喅瀹氬叆搴撴柟寮�
-        if (keyFlag) {
+        // 6. 鏍规嵁鏄惁闇�瑕佹楠岋紝鍐冲畾浜у嚭鍜屽叆搴撴柟寮�
+        if (checkFlag) {
             // 6.1 闇�瑕佹楠岋細鐢熸垚浜у嚭鍗曪紙璐ㄩ噺鐘舵��=寰呮楠岋級锛屾洿鏂版姤宸ョ姸鎬佷负寰呮楠岋紝绛夎川妫�瀹屾垚鍥炶皟鍚庡啀鍏ュ簱
-            if (checkFlag) {
-                // 瀹屾垚鏃跺洖璋冭锛歁esQcIpqcServiceImpl#finishIpqc 鈫� splitPendingAndFinishProduce + completeFeedbackFromIpqc
-                productProduceService.generateProductProduce(feedback, true);
-                feedbackMapper.updateById(new MesProFeedbackDO().setId(id)
-                        .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()));
-                return false;
-            }
-            // 6.2 鏃犻渶妫�楠岋細鐢熸垚浜у嚭鍗曪紙鎸夊悎鏍�/涓嶅悎鏍兼媶琛岋級锛岀洿鎺ュ畬鎴愬叆搴擄紝骞舵洿鏂颁换鍔�/宸ュ崟鐨勫凡鐢熶骇鏁伴噺
-            MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false);
-            productProduceService.finishProductProduce(produce.getId());
-            updateTaskAndWorkOrderByFeedback(feedback);
+            // 瀹屾垚鏃跺洖璋冭锛歁esQcIpqcServiceImpl#finishIpqc 鈫� splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish
+            productProduceService.generateProductProduce(feedback, true, reqVO);
+            feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId())
+                    .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()));
+            return false;
         } else {
-            // 6.3 闈炲叧閿伐搴忥細鍙洿鏂颁换鍔$殑宸茬敓浜ф暟閲忥紙宸ュ崟涓嶆洿鏂帮紝宸ュ崟鍙湅鍏抽敭宸ュ簭浜у嚭锛�
-            taskService.updateProducedQuantity(feedback.getTaskId(),
-                    feedback.getFeedbackQuantity(),
-                    ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO),
-                    ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO));
-            // 6.4 闈炲叧閿伐搴忎篃闇�瑕佹鏌� MPS 瀹屾垚鐘舵�侊紙鎵�鏈夊伐搴忔姤宸ュ畬鎴愭墠绠楀畬鎴愶級
-            checkAndPublishProductionFinished(feedback.getWorkOrderId());
+            // 6.2 鏃犻渶妫�楠岋細鐢熸垚浜у嚭鍗曪紙鎸夊悎鏍�/涓嶅悎鏍兼媶琛岋級锛岀洿鎺ュ畬鎴愬叆搴�
+            MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO);
+            productProduceService.finishProductProduce(produce.getId());
+            // 鍏抽敭宸ュ簭锛氭洿鏂颁换鍔� + 宸ュ崟鐨勫凡鐢熶骇鏁伴噺锛涢潪鍏抽敭宸ュ簭锛氬彧鏇存柊浠诲姟鐨勫凡鐢熶骇鏁伴噺锛堝伐鍗曞彧鐪嬪叧閿伐搴忎骇鍑猴級
+            if (keyFlag) {
+                updateTaskAndWorkOrderByFeedback(feedback);
+            } else {
+                taskService.updateProducedQuantity(feedback.getTaskId(),
+                        feedback.getFeedbackQuantity(),
+                        ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO),
+                        ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO));
+                checkAndPublishProductionFinished(feedback.getWorkOrderId());
+            }
         }
 
         return true; // 宸插畬鎴�
@@ -363,6 +377,91 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    public MesProFeedbackQuickCreateRespVO quickCreateFeedback(MesProFeedbackQuickCreateReqVO reqVO) {
+        // 1. 鏍¢獙浠诲姟瀛樺湪涓旈潪缁堟��
+        MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
+        // 2. 鏍¢獙宸ヨ壓璺嚎宸ュ簭閰嶇疆
+        MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
+                task.getRouteId(), task.getProcessId());
+        if (routeProcess == null) {
+            throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
+        }
+        // 3. 鏍¢獙鏁伴噺
+        boolean checkFlag = Boolean.TRUE.equals(routeProcess.getCheckFlag());
+        if (checkFlag) {
+            if (reqVO.getFeedbackQuantity() == null
+                    || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) {
+                throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE);
+            }
+        } else {
+            BigDecimal qualified = ObjectUtil.defaultIfNull(reqVO.getQualifiedQuantity(), BigDecimal.ZERO);
+            BigDecimal unqualified = ObjectUtil.defaultIfNull(reqVO.getUnqualifiedQuantity(), BigDecimal.ZERO);
+            if (qualified.add(unqualified).compareTo(BigDecimal.ZERO) <= 0) {
+                throw exception(PRO_FEEDBACK_QUALIFIED_UNQUALIFIED_REQUIRED);
+            }
+        }
+        // 4. 鏋勫缓鎶ュ伐DO
+        MesProFeedbackDO feedback = new MesProFeedbackDO()
+                .setCode(autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_FEEDBACK_CODE.getCode()))
+                .setType(MesProFeedbackTypeEnum.SELF.getType())
+                .setWorkstationId(task.getWorkstationId())
+                .setRouteId(task.getRouteId())
+                .setProcessId(task.getProcessId())
+                .setWorkOrderId(task.getWorkOrderId())
+                .setTaskId(reqVO.getTaskId())
+                .setItemId(task.getItemId())
+                .setFeedbackQuantity(reqVO.getFeedbackQuantity())
+                .setQualifiedQuantity(reqVO.getQualifiedQuantity())
+                .setUnqualifiedQuantity(reqVO.getUnqualifiedQuantity())
+                .setScheduledQuantity(task.getQuantity())
+                .setFeedbackTime(LocalDateTime.now())
+                .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus())
+                .setRemark(reqVO.getRemark());
+        feedbackMapper.insert(feedback);
+        // 5. 杩斿洖缁撴灉
+        MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(task.getWorkOrderId());
+        return new MesProFeedbackQuickCreateRespVO()
+                .setFeedbackId(feedback.getId())
+                .setTaskStatus(task.getStatus())
+                .setWorkOrderStatus(workOrder != null ? workOrder.getStatus() : null);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public MesProFeedbackBatchCreateRespVO batchCreateFeedback(MesProFeedbackBatchCreateReqVO reqVO) {
+        List<MesProFeedbackBatchCreateRespVO.BatchResult> results = new java.util.ArrayList<>();
+        int successCount = 0;
+        int failCount = 0;
+        for (Long taskId : reqVO.getTaskIds()) {
+            try {
+                MesProFeedbackQuickCreateReqVO singleReq = new MesProFeedbackQuickCreateReqVO();
+                singleReq.setTaskId(taskId);
+                singleReq.setFeedbackQuantity(reqVO.getFeedbackQuantity());
+                singleReq.setQualifiedQuantity(reqVO.getQualifiedQuantity());
+                singleReq.setUnqualifiedQuantity(reqVO.getUnqualifiedQuantity());
+                singleReq.setRemark(reqVO.getRemark());
+                MesProFeedbackQuickCreateRespVO resp = quickCreateFeedback(singleReq);
+                results.add(new MesProFeedbackBatchCreateRespVO.BatchResult()
+                        .setTaskId(taskId)
+                        .setSuccess(true)
+                        .setFeedbackId(resp.getFeedbackId()));
+                successCount++;
+            } catch (Exception e) {
+                results.add(new MesProFeedbackBatchCreateRespVO.BatchResult()
+                        .setTaskId(taskId)
+                        .setSuccess(false)
+                        .setErrorMsg(e.getMessage()));
+                failCount++;
+            }
+        }
+        return new MesProFeedbackBatchCreateRespVO()
+                .setSuccessCount(successCount)
+                .setFailCount(failCount)
+                .setResults(results);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
     public void updateProFeedbackWhenIpqcFinish(Long feedbackId, Long sourceLineId,
                                                 BigDecimal qualifiedQty, BigDecimal unqualifiedQty,
                                                 BigDecimal laborScrapQty, BigDecimal materialScrapQty, BigDecimal otherScrapQty) {
@@ -390,4 +489,70 @@
         updateTaskAndWorkOrderByFeedback(feedback);
     }
 
+    @Override
+    public MesProFeedbackBatchCheckRespVO checkBatchRequirements(Long feedbackId) {
+        MesProFeedbackDO feedback = validateFeedbackExists(feedbackId);
+        MesProFeedbackBatchCheckRespVO respVO = new MesProFeedbackBatchCheckRespVO();
+
+        // 1. 妫�鏌ョ墿鏂欐槸鍚﹀惎鐢ㄦ壒娆$鐞�
+        MesMdmItemDO item = mdmItemService.getItem(feedback.getItemId());
+        if (item == null || Boolean.FALSE.equals(item.getIsBatchManaged())) {
+            respVO.setNeedBatchInput(false);
+            respVO.setIsBatchManaged(false);
+            return respVO;
+        }
+        respVO.setIsBatchManaged(true);
+
+        // 2. 鑾峰彇鎵规閰嶇疆
+        MdmItemBatchConfigRespDTO config = itemBatchConfigApi.getItemBatchConfig(feedback.getItemId());
+        if (config == null) {
+            respVO.setNeedBatchInput(false);
+            return respVO;
+        }
+
+        // 3. 濉厖閰嶇疆鏍囧織
+        respVO.setProduceDateFlag(config.getProduceDateFlag());
+        respVO.setExpireDateFlag(config.getExpireDateFlag());
+        respVO.setReceiptDateFlag(config.getReceiptDateFlag());
+        respVO.setVendorFlag(config.getVendorFlag());
+        respVO.setClientFlag(config.getClientFlag());
+        respVO.setSalesOrderCodeFlag(config.getSalesOrderCodeFlag());
+        respVO.setPurchaseOrderCodeFlag(config.getPurchaseOrderCodeFlag());
+        respVO.setWorkOrderFlag(config.getWorkOrderFlag());
+        respVO.setTaskFlag(config.getTaskFlag());
+        respVO.setWorkstationFlag(config.getWorkstationFlag());
+        respVO.setToolFlag(config.getToolFlag());
+        respVO.setMoldFlag(config.getMoldFlag());
+        respVO.setLotNumberFlag(config.getLotNumberFlag());
+        respVO.setQualityStatusFlag(config.getQualityStatusFlag());
+
+        // 4. 鍒ゆ柇鍝簺瀛楁宸茶嚜鍔ㄥ~鍏咃紙鎶ュ伐鍜岀敓浜ц繃绋嬩腑蹇呯劧鏈夊�肩殑瀛楁锛�
+        List<String> autoFilled = new ArrayList<>();
+        // 鐢熶骇鏃ユ湡锛氬鎵规椂鑷姩鍙栧綋澶�
+        autoFilled.add("produceDate");
+        // 鐢熶骇宸ュ崟锛氫粠鎶ュ伐璁板綍鑾峰彇
+        autoFilled.add("workOrderId");
+        // 鐢熶骇浠诲姟锛氫粠鎶ュ伐璁板綍鑾峰彇
+        autoFilled.add("taskId");
+        // 宸ヤ綔绔欙細浠庢姤宸ヨ褰曡幏鍙�
+        autoFilled.add("workstationId");
+        respVO.setAutoFilledFields(autoFilled);
+
+        // 5. 鍒ゆ柇鏄惁闇�瑕佸脊绐楋細瀛樺湪蹇呭~浣嗘棤娉曡嚜鍔ㄥ~鍏呯殑瀛楁
+        boolean needBatchInput = false;
+        if (Boolean.TRUE.equals(config.getExpireDateFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getReceiptDateFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getVendorFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getClientFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getSalesOrderCodeFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getPurchaseOrderCodeFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getToolFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getMoldFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getLotNumberFlag())) needBatchInput = true;
+        if (Boolean.TRUE.equals(config.getQualityStatusFlag())) needBatchInput = true;
+        respVO.setNeedBatchInput(needBatchInput);
+
+        return respVO;
+    }
+
 }

--
Gitblit v1.9.3