From 757c7e03bee3560bb4d7766fddd03673e2c2e611 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 23 七月 2026 17:46:34 +0800
Subject: [PATCH] feat(mes): 完善MES模块功能并优化日志配置

---
 yudao-server/src/main/resources/logback-spring.xml                                                                                        |   26 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java                           |   20 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java                   |   36 ++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java                   |   44 ++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java                            |    9 +
 .mcp.json                                                                                                                                 |    8 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java                        |   20 +-
 docs/route_process_bom_integration.md                                                                                                     |  134 +++++++++++++++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java                            |   10 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java            |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProProcessBomItemVO.java              |   27 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java |   26 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java                               |   11 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java                        |    5 
 14 files changed, 359 insertions(+), 21 deletions(-)

diff --git a/.mcp.json b/.mcp.json
new file mode 100644
index 0000000..b2068ac
--- /dev/null
+++ b/.mcp.json
@@ -0,0 +1,8 @@
+{
+  "mcpServers": {
+    "idea": {
+      "url": "http://127.0.0.1:64342/stream",
+      "type": "http"
+    }
+  }
+}
\ No newline at end of file
diff --git a/docs/route_process_bom_integration.md b/docs/route_process_bom_integration.md
new file mode 100644
index 0000000..683add4
--- /dev/null
+++ b/docs/route_process_bom_integration.md
@@ -0,0 +1,134 @@
+# 宸ヨ壓璺嚎宸ュ簭鎺ュ彛鏂板鎶曟枡BOM - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 鐢熶骇宸ュ崟鏂板/缂栬緫椤碉紙`src/views/mes/pro/workorder/detail.vue`锛�
+  - 瀛愮粍浠� `modules/process-list.vue`锛堝伐搴忓垪琛級
+  - 瀛愮粍浠� `modules/process-form.vue`锛堝伐搴忔柊澧�/缂栬緫寮圭獥锛�
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 鐢ㄦ埛鏂板缓/缂栬緫宸ュ崟 鈫� 閫夋嫨浜у搧 鈫� `process-list.vue` 璋冪敤鎺ュ彛鑾峰彇宸ュ簭鍒楄〃
+2. 宸ュ簭鍒楄〃杩斿洖鏁版嵁涓柊澧� `bomItems` 瀛楁锛堝伐搴忔姇鏂欐槑缁嗭級
+3. 宸ュ簭灞曞紑琛岃嚜鍔ㄥ睍绀烘姇鏂欐槑缁嗭紙鏃犻渶棰濆璇锋眰锛�
+4. 淇濆瓨宸ュ崟鏃� `bomItems` 闅忓伐搴忔暟鎹竴璧锋彁浜�
+
+## API
+
+### 鎺ュ彛璇存槑
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/mes/pro/route-process/list-by-product?productId={productId}` | 鎸変骇鍝佽幏鍙栧伐鑹鸿矾绾垮伐搴忥紙鍚姇鏂橞OM锛� |
+
+### 鍝嶅簲瀛楁鏂板
+
+`MesProRouteProcessRespVO` 鏂板瀛楁锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| bomItems | ProcessBomItem[] | 宸ュ簭鎶曟枡BOM鍒楄〃锛屾棤鎶曟枡鏃朵负绌烘暟缁勬垨 null |
+
+### ProcessBomItem 缁撴瀯
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| itemId | Long | BOM鐗╂枡缂栧彿 |
+| itemCode | String | BOM鐗╂枡缂栫爜 |
+| itemName | String | BOM鐗╂枡鍚嶇О |
+| quantity | BigDecimal | 鐢ㄦ枡姣斾緥 |
+| remark | String | 澶囨敞 |
+
+### 鍝嶅簲绀轰緥
+
+```json
+{
+  "code": 0,
+  "data": [
+    {
+      "id": 1,
+      "routeId": 10,
+      "processId": 100,
+      "processCode": "P001",
+      "processName": "鐒婃帴",
+      "sort": 1,
+      "nextProcessId": 101,
+      "nextProcessName": "缁勮",
+      "linkType": 3,
+      "keyFlag": true,
+      "checkFlag": false,
+      "backflushFlag": true,
+      "outputItemId": 200,
+      "outputItemCode": "M200",
+      "outputItemName": "鍗婃垚鍝丄",
+      "bomItems": [
+        {
+          "itemId": 1001,
+          "itemCode": "M001",
+          "itemName": "閽㈡澘",
+          "quantity": 2.0,
+          "remark": ""
+        },
+        {
+          "itemId": 1002,
+          "itemCode": "M002",
+          "itemName": "鐒婃潯",
+          "quantity": 0.5,
+          "remark": ""
+        }
+      ]
+    },
+    {
+      "id": 2,
+      "routeId": 10,
+      "processId": 101,
+      "processCode": "P002",
+      "processName": "缁勮",
+      "sort": 2,
+      "nextProcessId": null,
+      "bomItems": []
+    }
+  ]
+}
+```
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| bomItems | 宸ュ簭琛ㄦ牸灞曞紑琛� | 鐐瑰嚮灞曞紑鎸夐挳鍚庝互 Tag 鍒楄〃褰㈠紡灞曠ず姣忎釜鎶曟枡鐗╂枡 |
+| itemName / itemCode | Tag 鏂囨湰 | 浼樺厛鏄剧ず鐗╂枡鍚嶇О锛屾棤鍚嶇О鏃舵樉绀虹紪鐮� |
+| quantity | Tag 鍚庣紑 | 鏄剧ず涓� `脳{quantity}` 鏍煎紡 |
+
+## 鍓嶇瀵规帴纭娓呭崟
+
+### 宸蹭慨鏀规枃浠讹紙褰撳墠鍒嗘敮宸叉湁锛�
+
+| 鏂囦欢 | 淇敼鍐呭 |
+|------|----------|
+| `src/api/mes/pro/route/process/index.ts` | `RouteProcess` 鎺ュ彛鏂板 `bomItems?: ProcessBomItem[]` |
+| `src/api/mes/pro/workorder/process/index.ts` | `WorkOrderProcess` 鎺ュ彛鏂板 `bomItems?: ProcessBomItem[]` |
+| `src/views/mes/pro/workorder/data.ts` | `useProcessGridColumns` 鏂板灞曞紑鍒� `{ type: 'expand', width: 50 }` |
+| `src/views/mes/pro/workorder/modules/process-list.vue` | `getList()` 鏄犲皠 `bomItems`锛屾ā鏉挎柊澧� `#expand` slot |
+
+### 闇�纭
+
+1. `ProcessBomItem` 鎺ュ彛绫诲瀷鍦ㄥ墠绔� API 鏂囦欢涓凡琛ュ厖锛岀‘璁や笌鍚庣杩斿洖缁撴瀯涓�鑷�
+2. 灞曞紑琛屽唴鐨勬姇鏂欐槑缁� Tag 鍒楄〃灞曠ず鎸夊綋鍓嶈璁★細钃濊壊 Tag锛屾樉绀� `鐗╂枡鍚嶇О 脳鐢ㄩ噺`
+3. 鏃犳姇鏂欏伐搴忕殑灞曞紑琛屼负绌猴紙鏃犱换浣曞唴瀹癸級锛屼笉鏄剧ず"鎶曟枡鏄庣粏"鏍囬
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 浜у搧鏈厤缃伐鑹鸿矾绾� | 杩斿洖绌哄垪琛紝涓嶆姤閿� |
+| 宸ヨ壓璺嚎鏈夊伐搴忎絾鏈厤缃姇鏂� | bomItems 涓虹┖锛屽睍寮�琛屾棤鍐呭 |
+| 鎶曟枡鐗╂枡宸茶鍒犻櫎 | 涓嶈繑鍥炶鏉℃姇鏂欒褰曪紙鏁版嵁搴撻�昏緫鍒犻櫎锛� |
+| 宸ュ簭琚垹闄ゅ悗閲嶅缓 | 灞曞紑琛岄噸鏂颁粠鎺ュ彛鑾峰彇鏈�鏂� BOM 鏁版嵁 |
+
+## 娉ㄦ剰浜嬮」
+
+- 鍚庣纭繚鏃犻渶閲嶅惎鍗冲彲楠岃瘉锛堝鏋滄帴鍙e凡鏈夋暟鎹級锛氫娇鐢ㄥ凡閰嶇疆鎶曟枡鐨勪骇鍝両D璋冪敤鎺ュ彛
+- 鎶曟枡 BOM 鏉ヨ嚜 `mes_pro_route_product_bom` 琛紙閰嶇疆鎶曟枡锛夛紝涓嶆槸浜у搧鏍囧噯 BOM
+- BOM 鐗╂枡鍚嶇О閫氳繃 `MesMdItemService` 瀹炴椂鏌ヨ锛岀‘淇濆睍绀烘渶鏂扮墿鏂欏悕绉�
+- Mapper 灞備娇鐢� MyBatis-Plus `@TableLogic`锛屽凡閫昏緫鍒犻櫎鐨勮褰曚笉浼氳繑鍥�
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
index 8c8d2fd..e02cb8a 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
@@ -5,11 +5,13 @@
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProProcessBomItemVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
 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.route.MesProRouteProductBomDO;
 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
 import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
@@ -27,6 +29,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@@ -78,14 +81,41 @@
         return success(buildRouteProcessRespVO(routeProcess));
     }
 
-    // TODO @鑺嬭壙锛氬埌搴曞彨 list-by-item 杩樻槸 list-by-product锛�
     @GetMapping("/list-by-product")
-    @Operation(summary = "鎸変骇鍝佽幏寰楀伐搴忓垪琛�", description = "鏍规嵁浜у搧鏌ユ壘鍏宠仈鐨勫伐鑹鸿矾绾匡紝杩斿洖璇ヨ矾绾跨殑宸ュ簭鍒楄〃")
+    @Operation(summary = "鎸変骇鍝佽幏寰楀伐搴忓垪琛�", description = "鏍规嵁浜у搧鏌ユ壘鍏宠仈鐨勫伐鑹鸿矾绾匡紝杩斿洖璇ヨ矾绾跨殑宸ュ簭鍒楄〃锛堝惈鍚勫伐搴忕殑鎶曟枡BOM锛�")
     @Parameter(name = "productId", description = "浜у搧缂栧彿", required = true, example = "1")
     @PreAuthorize("@ss.hasPermission('mes:pro-route:query')")
     public CommonResult<List<MesProRouteProcessRespVO>> getRouteProcessListByProduct(@RequestParam("productId") Long productId) {
         List<MesProRouteProcessDO> list = routeProcessService.getRouteProcessListByProductId(productId);
-        return success(buildRouteProcessRespVOList(list));
+        List<MesProRouteProcessRespVO> result = buildRouteProcessRespVOList(list);
+
+        // 鏌ヨ閰嶇疆鎶曟枡骞舵寜宸ュ簭濉厖
+        Map<Long, List<MesProRouteProductBomDO>> bomByProcess = routeProcessService.getRouteProcessBomMapByProductId(productId);
+        if (!bomByProcess.isEmpty()) {
+            Set<Long> itemIds = bomByProcess.values().stream()
+                    .flatMap(List::stream)
+                    .map(MesProRouteProductBomDO::getItemId)
+                    .collect(Collectors.toSet());
+            Map<Long, MesMdItemDO> itemMap = mdItemService.getItemMap(itemIds);
+            for (MesProRouteProcessRespVO vo : result) {
+                List<MesProRouteProductBomDO> processBoms = bomByProcess.get(vo.getProcessId());
+                if (CollUtil.isNotEmpty(processBoms)) {
+                    vo.setBomItems(processBoms.stream().map(bom -> {
+                        MesProProcessBomItemVO itemVO = new MesProProcessBomItemVO();
+                        itemVO.setItemId(bom.getItemId());
+                        itemVO.setQuantity(bom.getQuantity());
+                        itemVO.setRemark(bom.getRemark());
+                        MesMdItemDO item = itemMap.get(bom.getItemId());
+                        if (item != null) {
+                            itemVO.setItemCode(item.getCode());
+                            itemVO.setItemName(item.getName());
+                        }
+                        return itemVO;
+                    }).collect(Collectors.toList()));
+                }
+            }
+        }
+        return success(result);
     }
 
     @GetMapping("/list-by-route")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProProcessBomItemVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProProcessBomItemVO.java
new file mode 100644
index 0000000..aa33ee5
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProProcessBomItemVO.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "宸ュ簭鎶曟枡BOM鏄庣粏 VO")
+@Data
+public class MesProProcessBomItemVO {
+
+    @Schema(description = "BOM鐗╂枡缂栧彿", example = "1001")
+    private Long itemId;
+
+    @Schema(description = "BOM鐗╂枡缂栫爜", example = "M001")
+    private String itemCode;
+
+    @Schema(description = "BOM鐗╂枡鍚嶇О", example = "鐗╂枡A")
+    private String itemName;
+
+    @Schema(description = "鐢ㄦ枡姣斾緥", example = "1.0")
+    private BigDecimal quantity;
+
+    @Schema(description = "澶囨敞")
+    private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
index 41fb1f0..76ef7f3 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
@@ -4,6 +4,7 @@
 import lombok.Data;
 
 import java.time.LocalDateTime;
+import java.util.List;
 
 @Schema(description = "绠$悊鍚庡彴 - MES 宸ヨ壓璺嚎宸ュ簭 Response VO")
 @Data
@@ -66,6 +67,9 @@
     @Schema(description = "澶囨敞")
     private String remark;
 
+    @Schema(description = "宸ュ簭鎶曟枡BOM鍒楄〃")
+    private List<MesProProcessBomItemVO> bomItems;
+
     @Schema(description = "鍒涘缓鏃堕棿")
     private LocalDateTime createTime;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
index ebbce72..5b9b2a8 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
@@ -4,6 +4,9 @@
 import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 
+import java.math.BigDecimal;
+import java.util.List;
+
 @Schema(description = "绠$悊鍚庡彴 - MES 鐢熶骇宸ュ崟宸ュ簭鏂板/淇敼 Request VO")
 @Data
 public class MesProWorkOrderProcessSaveReqVO {
@@ -50,4 +53,27 @@
     @Schema(description = "澶囨敞")
     private String remark;
 
+    @Schema(description = "宸ュ簭鎶曟枡BOM鍒楄〃")
+    private List<BomItem> bomItems;
+
+    @Data
+    public static class BomItem {
+
+        @Schema(description = "BOM鐗╂枡缂栧彿", example = "1001")
+        private Long itemId;
+
+        @Schema(description = "BOM鐗╂枡缂栫爜")
+        private String itemCode;
+
+        @Schema(description = "BOM鐗╂枡鍚嶇О")
+        private String itemName;
+
+        @Schema(description = "棰勮浣跨敤閲�")
+        private BigDecimal quantity;
+
+        @Schema(description = "澶囨敞")
+        private String remark;
+
+    }
+
 }
\ No newline at end of file
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 f05fac3..6f36f8c 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,8 +12,8 @@
 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;
@@ -22,8 +22,8 @@
 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;
@@ -64,7 +64,7 @@
     @Resource
     private MesProRouteProcessService routeProcessService;
     @Resource
-    private MesProRouteProductBomService routeProductBomService;
+    private MesProWorkOrderBomService workOrderBomService;
     @Resource
     @Lazy // 閬垮厤寰幆渚濊禆
     private MesProTaskService taskService;
@@ -188,8 +188,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);
             }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java
index 96ccdab..3639a0c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessService.java
@@ -67,6 +67,17 @@
     List<MesProRouteProcessDO> getRouteProcessListByProductId(Long productId);
 
     /**
+     * 鎸変骇鍝佽幏寰楀伐搴忔姇鏂橞OM Map
+     *
+     * key = 宸ュ簭ID锛寁alue = 璇ュ伐搴忎笅鐨勬姇鏂橞OM鏄庣粏鍒楄〃
+     *
+     * @param productId 浜у搧缂栧彿
+     * @return 宸ュ簭 -> 鎶曟枡BOM 鏄犲皠锛屼骇鍝佹棤宸ヨ壓璺嚎鏃惰繑鍥炵┖Map
+     */
+    java.util.Map<Long, java.util.List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO>>
+        getRouteProcessBomMapByProductId(Long productId);
+
+    /**
      * 鎸夊伐搴忚幏寰楀伐鑹鸿矾绾垮伐搴忓垪琛�
      *
      * @param processId 宸ュ簭缂栧彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
index ec5aae1..42417d2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
@@ -4,6 +4,7 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.process.MesProRouteProcessSaveReqVO;
 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.route.MesProRouteProductDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProcessMapper;
 import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
@@ -17,6 +18,8 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -42,6 +45,9 @@
     @Resource
     @Lazy
     private MesProRouteService routeService;
+    @Resource
+    @Lazy
+    private MesProRouteProductBomService routeProductBomService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -198,6 +204,20 @@
     }
 
     @Override
+    public Map<Long, List<MesProRouteProductBomDO>> getRouteProcessBomMapByProductId(Long productId) {
+        MesProRouteProductDO routeProduct = routeProductService.getRouteProductByItemId(productId);
+        if (routeProduct == null) {
+            return Collections.emptyMap();
+        }
+        List<MesProRouteProductBomDO> bomList = routeProductBomService
+                .getRouteProductBomListByRouteIdAndProductId(routeProduct.getRouteId(), productId);
+        if (CollUtil.isEmpty(bomList)) {
+            return Collections.emptyMap();
+        }
+        return bomList.stream().collect(Collectors.groupingBy(MesProRouteProductBomDO::getProcessId));
+    }
+
+    @Override
     public List<MesProRouteProcessDO> getRouteProcessListByProcessId(Long processId) {
         return routeProcessMapper.selectListByProcessId(processId);
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java
index d2781fd..09745db 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomService.java
@@ -53,4 +53,13 @@
      */
     void deleteRouteProductBomByRouteIdAndProductId(Long routeId, Long productId);
 
+    /**
+     * 鎸夊伐鑹鸿矾绾垮拰浜у搧鏌ヨ BOM 鍒楄〃
+     *
+     * @param routeId 宸ヨ壓璺嚎缂栧彿
+     * @param productId 浜у搧鐗╂枡缂栧彿
+     * @return BOM 鍒楄〃
+     */
+    List<MesProRouteProductBomDO> getRouteProductBomListByRouteIdAndProductId(Long routeId, Long productId);
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
index 7b3ec2a..d833683 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductBomServiceImpl.java
@@ -132,4 +132,9 @@
         routeProductBomMapper.deleteByRouteIdAndProductId(routeId, productId);
     }
 
+    @Override
+    public List<MesProRouteProductBomDO> getRouteProductBomListByRouteIdAndProductId(Long routeId, Long productId) {
+        return routeProductBomMapper.selectListByRouteIdAndProductId(routeId, productId);
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
index 0883d5d..e626b2e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
@@ -5,8 +5,11 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.process.MesProWorkOrderProcessSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderProcessMapper;
 import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
 import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
@@ -44,6 +47,11 @@
     private MesProProcessService processService;
     @Resource
     private MesProRouteProcessService routeProcessService;
+    @Resource
+    @Lazy
+    private MesProWorkOrderBomService workOrderBomService;
+    @Resource
+    private MesProWorkOrderBomMapper workOrderBomMapper;
 
     @Override
     public Long createWorkOrderProcess(MesProWorkOrderProcessSaveReqVO createReqVO) {
@@ -175,10 +183,44 @@
             }
         }
 
-        // 4. 閲嶅缓宸ュ簭閾�
+        // 4. 淇濆瓨宸ュ崟 BOM 鎶曟枡锛堝悎骞舵墍鏈夊伐搴忕殑 bomItems锛屾寜 itemId 鍘婚噸锛�
+        saveWorkOrderBomFromProcesses(workOrderId, list);
+
+        // 5. 閲嶅缓宸ュ簭閾�
         rebuildProcessChain(workOrderId);
     }
 
+    /**
+     * 浠庡伐搴忓垪琛ㄤ腑鎻愬彇 BOM 鎶曟枡锛屽悎骞跺幓閲嶅悗淇濆瓨鍒板伐鍗� BOM 琛�
+     */
+    private void saveWorkOrderBomFromProcesses(Long workOrderId, List<MesProWorkOrderProcessSaveReqVO> list) {
+        // 鏀堕泦鎵�鏈夊伐搴忕殑 bomItems锛屾寜 itemId 鍘婚噸锛堜繚鐣欑涓�涓嚭鐜扮殑 quantity锛�
+        java.util.Map<Long, MesProWorkOrderProcessSaveReqVO.BomItem> merged = new java.util.LinkedHashMap<>();
+        for (MesProWorkOrderProcessSaveReqVO item : list) {
+            if (CollUtil.isNotEmpty(item.getBomItems())) {
+                for (MesProWorkOrderProcessSaveReqVO.BomItem bomItem : item.getBomItems()) {
+                    if (bomItem.getItemId() != null) {
+                        merged.putIfAbsent(bomItem.getItemId(), bomItem);
+                    }
+                }
+            }
+        }
+        // 鍒犻櫎鏃� BOM锛屾壒閲忔彃鍏ユ柊 BOM
+        workOrderBomService.deleteWorkOrderBomByWorkOrderId(workOrderId);
+        if (CollUtil.isNotEmpty(merged)) {
+            List<MesProWorkOrderBomDO> bomList = new ArrayList<>(merged.size());
+            for (MesProWorkOrderProcessSaveReqVO.BomItem bomItem : merged.values()) {
+                bomList.add(MesProWorkOrderBomDO.builder()
+                        .workOrderId(workOrderId)
+                        .itemId(bomItem.getItemId())
+                        .quantity(bomItem.getQuantity())
+                        .remark(bomItem.getRemark())
+                        .build());
+            }
+            workOrderBomMapper.insertBatch(bomList);
+        }
+    }
+
     // ==================== 绉佹湁鏂规硶 ====================
 
     private MesProWorkOrderProcessDO validateWorkOrderProcessExists(Long id) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java
index 413d6eb..84452a3 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemconsume/MesWmItemConsumeServiceImpl.java
@@ -5,7 +5,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDetailDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeLineDO;
@@ -19,8 +19,7 @@
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
 import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
-import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
-import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -59,9 +58,7 @@
     @Resource
     private MesWmItemConsumeDetailService itemConsumeDetailService;
     @Resource
-    private MesProRouteProductBomService routeProductBomService;
-    @Resource
-    private MesProRouteService routeService;
+    private MesProWorkOrderBomService workOrderBomService;
     @Resource
     private MesWmTransactionService wmTransactionService;
     @Resource
@@ -77,14 +74,13 @@
 
     @Override
     public MesWmItemConsumeDO generateItemConsume(MesProFeedbackDO feedback) {
-        // 1.1 閬垮厤 routeId 绛変负 null 鏃� getRouteProductBomList 鍥� eqIfPresent 鏀惧鏉′欢瀵艰嚧璇煡
-        if (feedback.getRouteId() == null || feedback.getProcessId() == null || feedback.getItemId() == null) {
+        // 1.1 鏍¢獙宸ュ崟缂栧彿鏈夋晥
+        if (feedback.getWorkOrderId() == null) {
             throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
         }
-        routeService.validateRouteExists(feedback.getRouteId());
-        // 1.2 鏌ヨ褰撳墠宸ュ簭鐨� BOM 鐗╂枡閰嶇疆
-        List<MesProRouteProductBomDO> boms = routeProductBomService.getRouteProductBomList(
-                feedback.getRouteId(), feedback.getProcessId(), feedback.getItemId());
+        // 1.2 鏌ヨ宸ュ崟 BOM 鐗╂枡鍒楄〃锛堝�掑啿鎸夊伐鍗曟姇鏂欙紝闈炲伐鑹鸿矾绾匡級
+        List<MesProWorkOrderBomDO> boms = workOrderBomService.getWorkOrderBomListByWorkOrderId(
+                feedback.getWorkOrderId());
         if (CollUtil.isEmpty(boms)) {
             return null;
         }
diff --git a/yudao-server/src/main/resources/logback-spring.xml b/yudao-server/src/main/resources/logback-spring.xml
index 15b28cf..2ad3a66 100644
--- a/yudao-server/src/main/resources/logback-spring.xml
+++ b/yudao-server/src/main/resources/logback-spring.xml
@@ -4,6 +4,8 @@
     <!-- CONSOLE_LOG_PATTERN 鐩告瘮 FILE_LOG_PATTERN 澶氫簡 highlight銆乧yan 绛夐珮浜� -->
     <property name="CONSOLE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n"/>
     <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n"/>
+    <!-- 鎸佷箙鍖� error 鏃ュ織璺緞锛屾寜澶╂粴鍔紝淇濈暀 15 澶� -->
+    <property name="ERROR_LOG_FILE" value="./logs/error/error.log"/>
 
     <!-- 鎺у埗鍙� Appender -->
     <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">銆�銆�銆�銆�銆�
@@ -34,6 +36,28 @@
         <appender-ref ref="FILE"/>
     </appender>
 
+    <!-- Error 鏃ュ織涓撴湁 Appender锛氭寔涔呭寲 error 鏃ュ織锛屾寜澶╂粴鍔紝淇濈暀 15 澶� -->
+    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <!-- 鍙啓鍏� ERROR 绾у埆 -->
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>ERROR</level>
+        </filter>
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <pattern>${FILE_LOG_PATTERN}</pattern>
+        </encoder>
+        <file>${ERROR_LOG_FILE}</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+            <fileNamePattern>${ERROR_LOG_FILE}.%d{yyyy-MM-dd}.log</fileNamePattern>
+            <maxHistory>15</maxHistory>
+        </rollingPolicy>
+    </appender>
+    <!-- 寮傛鍐欏叆 error 鏃ュ織 -->
+    <appender name="ERROR_ASYNC" class="ch.qos.logback.classic.AsyncAppender">
+        <discardingThreshold>0</discardingThreshold>
+        <queueSize>512</queueSize>
+        <appender-ref ref="ERROR_FILE"/>
+    </appender>
+
     <!-- SkyWalking Appender锛欸RPC 鏃ュ織鏀堕泦锛屽疄鐜版棩蹇椾腑蹇� -->
     <!--
     <appender name="SKYWALKING" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
@@ -51,6 +75,8 @@
         <appender-ref ref="ASYNC"/>
         <!-- 濡傛灉鎯虫帴鍏ャ�怱kyWalking 鏃ュ織鏈嶅姟銆戯紝鍙互鍙栨秷娉ㄩ噴鎺夋湰琛� -->
         <!-- <appender-ref ref="SKYWALKING"/> -->
+        <!-- Error 鏃ュ織鎸佷箙鍖栵細鎸夊ぉ婊氬姩锛屼繚鐣� 15 澶� -->
+        <appender-ref ref="ERROR_ASYNC"/>
     </root>
 
 </configuration>

--
Gitblit v1.9.3