From c6a2b472cde9dff4d21b95637ee6d99a412c785c Mon Sep 17 00:00:00 2001
From: 高 <gao@gaodeMacBook-Pro.local>
Date: 星期六, 10 十月 2026 17:21:40 +0800
Subject: [PATCH] feat(mes,crm): 新增质检单批量导入、检测结果台账与开票导入

---
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java                          |    8 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/invoice/CrmInvoiceMapper.java                           |    9 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java                   |   28 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java                           |  134 ++
 docs/project-business/mermaid/05-quality-flow.mmd                                                                            |   19 
 docs/sql/qc_inspection_ledger_menu.sql                                                                                       |   94 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerRespVO.java    |  102 ++
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceServiceImpl.java                        |  250 ++++
 yudao-module-mes/src/main/resources/mapper/qc/ledger/MesQcInspectionLedgerMapper.xml                                         |  176 +++
 docs/project-business/data/modules.json                                                                                      |    2 
 docs/crm_invoice_import_integration.md                                                                                       |   92 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcImportExcelVO.java             |   87 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerPageReqVO.java |   41 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/imports/MesQcImportHelper.java                         |  369 +++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/MesQcIpqcController.java                 |   28 
 docs/project-business/index.html                                                                                             |   35 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportRespVO.java   |   26 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/iqc/MesQcIqcMapper.java                              |   18 
 docs/sql/crm_invoice_import_menu.sql                                                                                         |   70 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ipqc/MesQcIpqcMapper.java                            |   15 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerServiceImpl.java           |   75 +
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java                      |    5 
 docs/sql/mes_qc_import_menu.sql                                                                                              |  115 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java                               |   10 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerService.java               |   22 
 docs/project-business/data/flows.json                                                                                        |    4 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceService.java                            |   10 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java                           |  121 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcService.java                             |   10 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportExcelVO.java  |   59 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ledger/MesQcInspectionLedgerMapper.java              |   20 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java                              |   19 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java                         |  154 +++
 docs/mes_qc_import_integration.md                                                                                            |  157 +++
 docs/project-business/data/objects.json                                                                                      |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerExportVO.java  |   70 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java                   |   28 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcService.java                               |   10 
 docs/qc_inspection_ledger_integration.md                                                                                     |  162 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcImportExcelVO.java             |   83 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/MesQcInspectionLedgerController.java   |  113 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/vo/MesQcImportRespVO.java                     |   26 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcImportExcelVO.java           |   91 +
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/CrmInvoiceController.java                |   24 
 44 files changed, 2,979 insertions(+), 20 deletions(-)

diff --git a/docs/crm_invoice_import_integration.md b/docs/crm_invoice_import_integration.md
new file mode 100644
index 0000000..c584562
--- /dev/null
+++ b/docs/crm_invoice_import_integration.md
@@ -0,0 +1,92 @@
+# CRM 寮�绁ㄥ鍏� - 鍓嶇鑱旇皟鏂规
+
+> 闇�姹傦細寮�绁ㄧ鐞嗗垪琛ㄥ鍔犮�屽鍏ャ�嶆寜閽紙浣嶄簬銆屾柊澧炲紑绁ㄧ敵璇枫�嶄笌銆屽鍑恒�嶄箣闂达級锛岀敤浜庢寜妯℃澘鎵归噺瀵煎叆寮�绁ㄨ褰曘��
+> 鍙傝�冨疄鐜帮細鍚堝悓鍒楄〃鐨勫鍏ュ脊绐楋紙`views/crm/contract/modules/import-form.vue`锛夛紝寮�绁ㄥ鍏ヤ笌鍏朵氦浜掍竴鑷淬��
+
+## 娑夊強椤甸潰
+
+- 淇敼锛歚views/crm/invoice/index.vue`锛堝紑绁ㄥ垪琛ㄥ伐鍏锋爮锛�
+- 鏂板锛氬鍏ュ脊绐楃粍浠讹紙寤鸿 `views/crm/invoice/modules/import-form.vue`锛�
+- 淇敼锛歚src/api/crm/invoice/index.ts`锛堟柊澧� 2 涓帴鍙o級
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 鍚堝悓锛堝凡瀹℃牳閫氳繃锛�-> 寮�绁ㄥ鍏ワ細妯℃澘涓~鍐欍�屽悎鍚岀紪鍙枫�嶏紝鍚庣鎸夌紪鍙峰尮閰嶅悎鍚岋紝**瀹㈡埛鑷姩鍙栧悎鍚屾墍灞炲鎴�**锛屾棤闇�鍦ㄦā鏉夸腑濉啓瀹㈡埛
+2. 瀵煎叆鎴愬姛 -> 寮�绁ㄥ垪琛細鏂板鐨勫紑绁ㄤ负銆屾湭鎻愪氦銆嶇姸鎬侊紝鍙湪鍒楄〃涓户缁紪杈戙�佷笂浼犲彂绁ㄩ檮浠躲�佹彁浜ゅ鎵�
+3. 瀵煎叆鐨勮礋璐d汉 = 褰撳墠鐧诲綍鐢ㄦ埛锛堝悓鍚堝悓瀵煎叆锛夛紝瀵煎叆鍚庡綋鍓嶇敤鎴峰嵆鍙湪銆屾垜璐熻矗鐨勩�嶄笅鐪嬪埌
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/crm/invoice/get-import-template` | 涓嬭浇瀵煎叆妯℃澘锛堣繑鍥� xls 鏂囦欢娴侊級 |
+| POST | `/crm/invoice/import` | 瀵煎叆寮�绁紙multipart 涓婁紶 Excel锛� |
+
+**鏉冮檺鐮侊細** 涓や釜鎺ュ彛鍧囪姹� `crm:invoice:import`锛堟寜閽敱 `docs/sql/crm_invoice_import_menu.sql` 鑴氭湰鎸夋潈闄愮爜鑷�傚簲鍒涘缓锛氳惀閿�绠$悊 -> 寮�绁ㄧ鐞� -> 寮�绁ㄥ鍏ワ紝鐖惰彍鍗曡窡闅忓紑绁ㄧ鐞嗐�屽鍑恒�嶆寜閽�佽鑹叉巿鏉冨悓鏍疯窡闅忋�屽鍑恒�嶆寜閽凡鏈夎鑹诧級銆�
+
+**璇锋眰鍙傛暟锛圥OST /crm/invoice/import锛夛細**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| file | MultipartFile | 鏄� | 浠呮敮鎸� xls銆亁lsx |
+
+**妯℃澘鍒楋紙涓嬭浇鐨勬ā鏉垮凡鍚〃澶翠笌涓�琛岀ず渚嬶級锛�**
+
+| 鍒楀悕 | 蹇呭~ | 璇存槑 |
+|------|------|------|
+| 寮�绁ㄧ紪鍙� | 鍚� | 鐣欑┖鍒欑郴缁熻嚜鍔ㄧ敓鎴愶紙KP+鏃ユ湡+6浣嶅簭鍙凤級锛涘~鍐欐椂涓嶅彲涓庣郴缁熶腑宸叉湁寮�绁ㄧ紪鍙烽噸澶� |
+| 鍚堝悓缂栧彿 | 鏄� | 闇�涓庣郴缁熶腑**宸插鏍搁�氳繃**鐨勫悎鍚岀紪鍙蜂竴鑷达紝瀹㈡埛鐢卞悎鍚岃嚜鍔ㄥ甫鍏� |
+| 鍙戠エ绫诲瀷 | 鏄� | 濉啓瀛楀吀 `crm_invoice_type` 鐨勫悕绉帮細澧炲�肩◣涓撶敤鍙戠エ / 澧炲�肩◣鏅�氬彂绁� / 鐢靛瓙鍙戠エ |
+| 鍙戠エ鍙风爜 | 鍚� | 鍙戠エ绁ㄩ潰鍙风爜 |
+| 鍙戠エ鎶ご | 鍚� | 鍙戠エ鎶ご |
+| 寮�绁ㄩ噾棰�(鍏�) | 鏄� | 闇�澶т簬 0锛屼笖涓嶈秴杩囪鍚堝悓鍓╀綑鍙紑绁ㄩ噾棰� |
+| 寮�绁ㄦ棩鏈� | 鍚� | 鏍煎紡 `2026-01-01 10:00:00` 鎴� `2026-01-01`锛涚暀绌哄彇褰撳墠鏃堕棿 |
+| 澶囨敞 | 鍚� | 澶囨敞 |
+
+**鍝嶅簲锛�**
+
+```json
+{
+  "code": 0,
+  "data": {
+    "createList": ["KP20260924000001", "KP20260924000002"],
+    "failureList": {
+      "绗� 4 琛�": "鍚堝悓缂栧彿涓嶅瓨鍦細HT9999銆傝鍏堝湪銆孋RM 鍚堝悓绠$悊銆嶄腑鍒涘缓鎴栧鍏ヨ鍚堝悓锛堢紪鍙烽渶涓庢ā鏉垮畬鍏ㄤ竴鑷达級锛屾垨淇敼妯℃澘涓殑鍚堝悓缂栧彿鍚庨噸鏂板鍏�"
+    }
+  }
+}
+```
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| createList | String[] | 瀵煎叆鎴愬姛鐨勫紑绁ㄧ紪鍙锋暟缁� |
+| failureList | Map | 瀵煎叆澶辫触闆嗗悎锛歬ey 涓恒�屽彂绁ㄥ彿鐮� xxx銆嶆垨銆岀 N 琛屻�嶏紙N 涓� Excel 琛屽彿锛屽惈琛ㄥご锛夛紝value 涓哄け璐ュ師鍥� |
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 瀵煎叆鎸夐挳 | 寮�绁ㄥ垪琛ㄥ伐鍏锋爮 | 浣嶇疆鍦ㄣ�屾柊澧炲紑绁ㄧ敵璇枫�嶄笌銆屽鍑恒�嶄箣闂达紱鏉冮檺鐮� `crm:invoice:import` |
+| 涓嬭浇瀵煎叆妯℃澘鎸夐挳 | 瀵煎叆寮圭獥宸︿笅瑙� | 鍚屽悎鍚屽鍏ュ脊绐楃殑 `prepend-footer` 浣嶇疆 |
+| 鏂板鏉℃暟 | 瀵煎叆寮圭獥缁撴灉鍖� | 鍙� `createList.length`锛屾枃妗堝銆屾柊澧烇細N 鏉°�� |
+| 澶辫触鏄庣粏 | 瀵煎叆寮圭獥缁撴灉鍖� | 閬嶅巻 `failureList`锛岄�愭潯灞曠ず銆宬ey锛歷alue銆嶏紝鍙粴鍔� |
+| 瀵煎叆妯℃澘璇存槑 | 瀵煎叆寮圭獥鍐呯伆鑹叉彁绀哄尯 | 蹇呭~鍒楋細鍚堝悓缂栧彿銆佸彂绁ㄧ被鍨嬨�佸紑绁ㄩ噾棰濓紱鍚堝悓蹇呴』涓恒�屽鏍搁�氳繃銆嶏紱璐熻矗浜� = 褰撳墠鐧诲綍鐢ㄦ埛锛涚姸鎬佷负銆屾湭鎻愪氦銆� |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鍚堝悓鐘舵�� | 鍙湁銆屽鏍搁�氳繃銆嶇殑鍚堝悓鎵嶈兘瀵煎叆寮�绁紱鏈彁浜�/瀹℃壒涓�/涓嶉�氳繃鐨勫悎鍚岃琛屽鍏ュけ璐� |
+| 寮�绁ㄩ噾棰濅笂闄� | 鍗曡閲戦涓嶅緱瓒呰繃銆屽悎鍚岄噾棰� 鈭� 宸插紑绁紙瀹℃壒涓� + 瀹℃牳閫氳繃锛夈�嶇殑鍓╀綑棰濆害锛岃秴鍑哄垯璇ヨ澶辫触锛屾彁绀轰腑缁欏嚭鍓╀綑棰濆害涓庢湰娆¢噾棰� |
+| 閲嶅缂栧彿 | 妯℃澘涓~鍐欑殑寮�绁ㄧ紪鍙峰湪绯荤粺涓凡瀛樺湪鏃惰琛屽け璐� |
+| 閲嶅瀵煎叆鎷︽埅 | 鍚屼竴鍚堝悓涓嬶細濉簡銆屽彂绁ㄥ彿鐮併�嶆椂鎸夈�屽悎鍚� + 鍙戠エ鍙风爜銆嶅垽閲嶏紱鏈~鍙戠エ鍙风爜鏃舵寜銆屽悎鍚� + 寮�绁ㄩ噾棰� + 鍙戠エ鎶ご + 寮�绁ㄦ棩鏈熴�嶅垽閲嶏紙寮�绁ㄦ棩鏈熺暀绌哄垯涓嶆瘮杈冭缁村害锛夈�傚悓涓�鏂囦欢鍐呴噸澶嶃�佹垨涓庣郴缁熷唴宸叉湁寮�绁ㄩ噸澶嶉兘浼氬け璐ュ苟鎸囧嚭閲嶅鐨勫崟鍙� |
+| 閮ㄥ垎鎴愬姛 | 閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛堝悓涓�鏂囦欢鍐呬笉浼氭暣浣撳洖婊氾級 |
+| 鏁版嵁鏉冮檺 | 瀵煎叆鐨勫紑绁ㄨ礋璐d汉 = 褰撳墠鐧诲綍鐢ㄦ埛锛岃嚜鍔ㄥ垱寤烘暟鎹潈闄愶紝鏃犻渶鍓嶇澶勭悊 |
+| 涓庛�屾柊寤哄紑绁ㄧ敵璇枫�嶇殑宸紓 | 瀵煎叆涓嶄骇鐢熼檮浠讹紱瀵煎叆鐨勫紑绁ㄩ粯璁ゃ�屾湭鎻愪氦銆嶏紝闇�鍦ㄥ垪琛ㄤ腑缂栬緫涓婁紶鍙戠エ闄勪欢鍚庡啀鎻愪氦瀹℃壒 |
+
+## 娉ㄦ剰浜嬮」
+
+- 妯℃澘銆屽彂绁ㄧ被鍨嬨�嶅~**鍚嶇О**锛堝 澧炲�肩◣涓撶敤鍙戠エ锛夛紝涔熷吋瀹瑰~鏁板瓧锛�1/2/3锛夛紱濉敊鏃跺け璐ュ師鍥犱細鍒楀嚭鍙�夐」
+- 瀵煎叆寮圭獥鐨勮繘搴︽潯/缁撴灉鍖轰氦浜掑彲鐩存帴澶嶇敤鍚堝悓瀵煎叆鐨勫疄鐜帮紙`import-form.vue`锛�
+- 鍓嶇鎸夐挳鐨� `auth` 闇�浣跨敤鏂扮殑鏉冮檺鐮� `crm:invoice:import`锛涙墽琛� `docs/sql/crm_invoice_import_menu.sql` 鍚庨噸鏂扮櫥褰曚互鍒锋柊鑿滃崟涓庢潈闄愮紦瀛橈紝鑷缓瑙掕壊鑻ヤ笉鍦ㄥ紑绁ㄧ鐞嗐�屽鍑恒�嶆寜閽殑鎺堟潈鑼冨洿鍐咃紝闇�鍦ㄣ�岃鑹茬鐞嗐�嶄腑鍙﹁鍕鹃�夈�屽紑绁ㄥ鍏ャ��
+- 绌鸿浼氳鑷姩璺宠繃锛屼笉绠楀け璐�
diff --git a/docs/mes_qc_import_integration.md b/docs/mes_qc_import_integration.md
new file mode 100644
index 0000000..5ff0d34
--- /dev/null
+++ b/docs/mes_qc_import_integration.md
@@ -0,0 +1,157 @@
+# MES 璐ㄦ鍗曟壒閲忓鍏� - 鍓嶇鑱旇皟鏂规
+
+> 闇�姹傦細鏉ユ枡妫�楠岋紙IQC锛夈�佽繃绋嬫楠岋紙IPQC锛夈�佸嚭璐ф楠岋紙OQC锛変笁涓垪琛ㄥ悇澧炲姞銆屾壒閲忓鍏ャ�嶆寜閽紝浣嶇疆鍦ㄣ�屾柊澧炪�嶄笌銆屽鍑恒�嶄箣闂达紝鐢ㄤ簬鎸夋ā鏉挎壒閲忓鍏ユ楠屽崟銆�
+> 鍙傝�冨疄鐜帮細鐜版湁鍚勬ā鍧楀鍏ュ脊绐楋紙濡� `views/mes/md/item/modules/import-form.vue`銆乣views/crm/contract/modules/import-form.vue`锛夛紝浜や簰涓庣粨鏋滃尯灞曠ず鍙洿鎺ユ部鐢ㄣ��
+
+## 娑夊強椤甸潰
+
+- 淇敼锛歚views/mes/qc/iqc/index.vue`锛堟潵鏂欐楠屽崟鍒楄〃宸ュ叿鏍� + 瀵煎叆寮圭獥锛�
+- 淇敼锛歚views/mes/qc/ipqc/index.vue`锛堣繃绋嬫楠屽崟鍒楄〃宸ュ叿鏍� + 瀵煎叆寮圭獥锛�
+- 淇敼锛歚views/mes/qc/oqc/index.vue`锛堝嚭璐ф楠屽崟鍒楄〃宸ュ叿鏍� + 瀵煎叆寮圭獥锛�
+- 鏂板锛氫笁涓鍏ュ脊绐楃粍浠讹紙寤鸿 `views/mes/qc/{iqc|ipqc|oqc}/modules/import-form.vue`锛�
+- 淇敼锛歚src/api/mes/qc/{iqc|ipqc|oqc}/index.ts`锛堝悇鏂板 2 涓帴鍙o級
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 涓嬭浇妯℃澘 -> 濉啓 -> 瀵煎叆锛�**涓�琛� = 涓�寮犳楠屽崟**銆傜墿鏂欍�佷緵搴斿晢/瀹㈡埛銆佽川妫�鏂规鍧囨寜銆岀紪鐮� / 鍚嶇О銆嶅尮閰嶇郴缁熶腑宸叉湁鏁版嵁锛屾棤闇�濉啓 ID
+2. 浜у搧鐗╂枡 -> 杩囩▼妫�楠屽崟锛圛PQC锛夛細妯℃澘濉啓銆屼骇鍝佺墿鏂欑紪鐮併��+銆屾壒鍙枫�嶏紝浜岃�呴兘鏄繀濉紱杩囩▼妫�楠屽崟鎸� **鐗╂枡 + 鎵瑰彿** 璁板綍锛�**妯℃澘涓嶅~宸ュ崟 / 宸ヤ綅 / 宸ュ簭**
+3. 璐ㄦ鏂规 -> 妫�楠岃锛氬鍏ユ垚鍔熷悗鐢卞悗绔寜鐗╂枡 + 妫�楠岀被鍨嬪尮閰嶈川妫�鏂规锛屽苟鑷姩鐢熸垚妫�楠岃锛堟楠屾寚鏍囷級锛屽墠绔棤闇�棰濆璋冪敤
+4. 瀵煎叆鎴愬姛 -> 鍒楄〃锛氭柊澧炲崟鎹姸鎬佷负銆屽緟妫�楠屻�嶏紙鑽夌锛夛紝鍙湪鍒楄〃涓紪杈戙�佸綍鍏ユ娴嬬粨鏋溿�佸畬鎴愭楠�
+5. 妫�娴嬩汉鍛樼暀绌� = 褰撳墠鐧诲綍鐢ㄦ埛锛涘~鍐欐椂鎸夊鍚嶅尮閰嶇郴缁熺敤鎴�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/mes/qc/iqc/get-import-template` | 涓嬭浇鏉ユ枡妫�楠屽崟瀵煎叆妯℃澘锛堣繑鍥� xls 鏂囦欢娴侊級 |
+| POST | `/mes/qc/iqc/import` | 瀵煎叆鏉ユ枡妫�楠屽崟锛坢ultipart 涓婁紶 Excel锛� |
+| GET | `/mes/qc/ipqc/get-import-template` | 涓嬭浇杩囩▼妫�楠屽崟瀵煎叆妯℃澘锛堣繑鍥� xls 鏂囦欢娴侊級 |
+| POST | `/mes/qc/ipqc/import` | 瀵煎叆杩囩▼妫�楠屽崟锛坢ultipart 涓婁紶 Excel锛� |
+| GET | `/mes/qc/oqc/get-import-template` | 涓嬭浇鍑鸿揣妫�楠屽崟瀵煎叆妯℃澘锛堣繑鍥� xls 鏂囦欢娴侊級 |
+| POST | `/mes/qc/oqc/import` | 瀵煎叆鍑鸿揣妫�楠屽崟锛坢ultipart 涓婁紶 Excel锛� |
+
+**鏉冮檺鐮侊細** `mes:qc-iqc:import` / `mes:qc-ipqc:import` / `mes:qc-oqc:import`锛堣彍鍗曚笌鎸夐挳鐢� `docs/sql/mes_qc_import_menu.sql` 鑴氭湰鎸夋潈闄愮爜鑷�傚簲鍒涘缓锛屽凡璺熼殢鍚勬楠屽崟銆屾煡璇€�嶆寜閽殑瑙掕壊鎺堟潈锛涜嚜寤鸿鑹查渶鍦ㄣ�岃鑹茬鐞嗐�嶄腑鍕鹃�夊搴旀寜閽級銆�
+
+**璇锋眰鍙傛暟锛圥OST .../import锛夛細**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| file | MultipartFile | 鏄� | 浠呮敮鎸� xls銆亁lsx锛涙棤 `updateSupport` 鍙傛暟锛屽鍏ヤ负**绾柊澧�**锛屼笉鍋氭洿鏂拌鐩� |
+
+**鍝嶅簲锛�**
+
+```json
+{
+  "code": 0,
+  "data": {
+    "createList": ["IQC20260924000001", "IQC20260924000002"],
+    "failureList": {
+      "绗� 4 琛�": "鐗╂枡涓嶅瓨鍦細WL9999銆傝鍏堝湪銆岀墿鏂欑鐞嗐�嶄腑缁存姢璇ョ墿鏂欙紙缂栫爜闇�涓庢ā鏉垮畬鍏ㄤ竴鑷达級锛屾垨淇敼妯℃澘涓殑浜у搧鐗╂枡缂栫爜鍚庨噸鏂板鍏�"
+    }
+  }
+}
+```
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| createList | String[] | 瀵煎叆鎴愬姛鐨勬楠屽崟缂栧彿鏁扮粍锛堟ā鏉挎湭濉紪鍙锋椂杩斿洖绯荤粺鑷姩鐢熸垚鐨勭紪鍙凤紝鍙敤浜庢彁绀�/璺宠浆锛� |
+| failureList | Map | 瀵煎叆澶辫触闆嗗悎锛歬ey 涓恒�屾楠屽崟缂栧彿 X銆嶆垨銆岀 N 琛屻�嶏紙N 涓� Excel 琛屽彿锛屽惈琛ㄥご琛岋級锛寁alue 涓哄け璐ュ師鍥� |
+
+## 妯℃澘鍒楋紙涓嬭浇鐨勬ā鏉垮凡鍚〃澶翠笌涓�琛岀ず渚嬶級
+
+### 鏉ユ枡妫�楠屽崟锛圛QC锛�
+
+| 鍒楀悕 | 蹇呭~ | 璇存槑 |
+|------|------|------|
+| 妫�楠屽崟缂栧彿 | 鍚� | 鐣欑┖鍒欑郴缁熻嚜鍔ㄧ敓鎴愶紱濉啓鏃朵笉鍙笌绯荤粺涓凡鏈夌紪鍙烽噸澶� |
+| 妫�楠屽崟鍚嶇О | 鏄� | 妫�楠屽崟鍚嶇О |
+| 浜у搧鐗╂枡缂栫爜 | 鏄� | 闇�涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭墿鏂欑紪鐮佷竴鑷� |
+| 渚涘簲鍟嗗悕绉� | 鍚� | 闇�涓庛�屼緵搴斿晢绠$悊銆嶄腑鐨勫悕绉颁竴鑷达紱鐣欑┖鍒欒鍗曟棤渚涘簲鍟� |
+| 渚涘簲鍟嗘壒娆″彿 | 鍚� | 渚涘簲鍟嗘壒娆″彿 |
+| 璐ㄦ鏂规 | 鍚� | 濉柟妗堝悕绉帮紱鐣欑┖鍒欏悗绔寜鐗╂枡鑷姩鍖归厤 IQC 鏂规 |
+| 鏈鎺ユ敹鏁伴噺 | 鍚� | 鐣欑┖鑷姩 = 鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺锛涘~鍐欏垯蹇呴』涓庝箣鐩哥瓑 |
+| 鍚堟牸鍝佹暟閲� | 鏄� | 涓嶈兘灏忎簬 0 |
+| 涓嶅悎鏍煎搧鏁伴噺 | 鏄� | 涓嶈兘灏忎簬 0 |
+| 妫�娴嬬粨鏋� | 鍚� | 瀛楀吀 `mes_qc_check_result` 鍚嶇О锛氭牎楠岄�氳繃 / 鏍¢獙涓嶉�氳繃 |
+| 妫�娴嬩汉鍛� | 鍚� | 鐢ㄦ埛濮撳悕锛涚暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛 |
+| 妫�娴嬫棩鏈� | 鏄� | 鏍煎紡 `2026-01-01 10:00:00` 鎴� `2026-01-01` |
+| 鏉ユ枡鏃ユ湡 | 鏄� | 鏍煎紡 `2026-01-01 10:00:00` 鎴� `2026-01-01` |
+| 澶囨敞 | 鍚� | 澶囨敞 |
+
+### 杩囩▼妫�楠屽崟锛圛PQC锛�
+
+| 鍒楀悕 | 蹇呭~ | 璇存槑 |
+|------|------|------|
+| 妫�楠屽崟缂栧彿 | 鍚� | 鐣欑┖鍒欑郴缁熻嚜鍔ㄧ敓鎴愶紱濉啓鏃朵笉鍙噸澶� |
+| 妫�楠屽崟鍚嶇О | 鏄� | 妫�楠屽崟鍚嶇О |
+| 妫�楠岀被鍨� | 鏄� | 瀛楀吀 `mes_ipqc_type` 鍚嶇О锛氳嚜鍒跺伐搴忔楠� / 棣栨 / 宸℃ / 鑷 / 鎴愬搧妫�楠� |
+| 浜у搧鐗╂枡缂栫爜 | 鏄� | 闇�涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭墿鏂欑紪鐮佷竴鑷达紱**鏈垎鏀繃绋嬫楠屾寜銆岀墿鏂� + 鎵瑰彿銆嶈褰曪紝涓嶅~宸ュ崟/宸ヤ綅** |
+| 鎵瑰彿 | 鏄� | 杩囩▼妫�楠岀殑鎵瑰彿锛屾渶闀� 100 瀛楃 |
+| 璐ㄦ鏂规 | 鍚� | 濉柟妗堝悕绉帮紱鐣欑┖鍒欏悗绔寜鐗╂枡鑷姩鍖归厤 IPQC 鏂规 |
+| 妫�娴嬫暟閲� | 鍚� | 鐣欑┖鑷姩 = 鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺锛涘~鍐欏垯蹇呴』涓庝箣鐩哥瓑 |
+| 鍚堟牸鍝佹暟閲� | 鏄� | 涓嶈兘灏忎簬 0 |
+| 涓嶅悎鏍煎搧鏁伴噺 | 鏄� | 涓嶈兘灏忎簬 0 |
+| 宸ュ簾鏁伴噺 | 鍚� | 鐣欑┖鎸� 0锛涗笌鏂欏簾銆佸叾浠栧簾鍝佷箣鍜屼笉寰楄秴杩囦笉鍚堟牸鍝佹暟閲� |
+| 鏂欏簾鏁伴噺 | 鍚� | 鐣欑┖鎸� 0 |
+| 鍏朵粬搴熷搧鏁伴噺 | 鍚� | 鐣欑┖鎸� 0 |
+| 妫�娴嬬粨鏋� | 鍚� | 瀛楀吀 `mes_qc_check_result` 鍚嶇О |
+| 妫�娴嬩汉鍛� | 鍚� | 鐢ㄦ埛濮撳悕锛涚暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛 |
+| 妫�娴嬫棩鏈� | 鍚� | 鏍煎紡 `2026-01-01 10:00:00` 鎴� `2026-01-01`锛涚暀绌哄垯涓嶅~ |
+| 澶囨敞 | 鍚� | 澶囨敞 |
+
+### 鍑鸿揣妫�楠屽崟锛圤QC锛�
+
+| 鍒楀悕 | 蹇呭~ | 璇存槑 |
+|------|------|------|
+| 妫�楠屽崟缂栧彿 | 鍚� | 鐣欑┖鍒欑郴缁熻嚜鍔ㄧ敓鎴愶紱濉啓鏃朵笉鍙噸澶� |
+| 妫�楠屽崟鍚嶇О | 鏄� | 妫�楠屽崟鍚嶇О |
+| 浜у搧鐗╂枡缂栫爜 | 鏄� | 闇�涓庛�岀墿鏂欑鐞嗐�嶄腑鐨勭墿鏂欑紪鐮佷竴鑷� |
+| 瀹㈡埛鍚嶇О | 鍚� | 闇�涓庛�孋RM 瀹㈡埛绠$悊銆嶄腑鐨勫悕绉颁竴鑷达紱鐣欑┖琛ㄧず璇ュ崟涓嶅睘浜庝换浣曞鎴枫��**濉啓鏃�**蹇呴』鍦ㄥ綋鍓嶇櫥褰曠敤鎴风殑瀹㈡埛鏁版嵁鏉冮檺鑼冨洿鍐� |
+| 鎵规鍙� | 鍚� | 鎵规鍙� |
+| 璐ㄦ鏂规 | 鍚� | 濉柟妗堝悕绉帮紱鐣欑┖鍒欏悗绔寜鐗╂枡鑷姩鍖归厤 OQC 鏂规 |
+| 鏈鍑鸿揣鏁伴噺 | 鍚� | 濉啓鏃朵笉鑳藉皬浜� 0锛涚暀绌鸿〃绀烘湭濉啓 |
+| 妫�娴嬫暟閲� | 鍚� | 鐣欑┖鑷姩 = 鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺锛涘~鍐欏垯蹇呴』涓庝箣鐩哥瓑 |
+| 鍚堟牸鍝佹暟閲� | 鏄� | 涓嶈兘灏忎簬 0 |
+| 涓嶅悎鏍煎搧鏁伴噺 | 鏄� | 涓嶈兘灏忎簬 0 |
+| 妫�娴嬬粨鏋� | 鍚� | 瀛楀吀 `mes_qc_check_result` 鍚嶇О |
+| 妫�娴嬩汉鍛� | 鍚� | 鐢ㄦ埛濮撳悕锛涚暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛 |
+| 妫�娴嬫棩鏈� | 鏄� | 鏍煎紡 `2026-01-01 10:00:00` 鎴� `2026-01-01` |
+| 鍑鸿揣鏃ユ湡 | 鍚� | 鏍煎紡 `2026-01-01 10:00:00` 鎴� `2026-01-01`锛涚暀绌鸿〃绀烘湭濉啓 |
+| 澶囨敞 | 鍚� | 澶囨敞 |
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鎵归噺瀵煎叆鎸夐挳 | 涓変釜鍒楄〃宸ュ叿鏍� | 浣嶇疆鍦ㄣ�屾柊澧炪�嶄笌銆屽鍑恒�嶄箣闂达紙鍗虫覆鏌撻『搴忥細鏂板 -> 鎵归噺瀵煎叆 -> 瀵煎嚭锛夛紱鏉冮檺鐮� `mes:qc-{iqc|ipqc|oqc}:import` |
+| 涓嬭浇瀵煎叆妯℃澘鎸夐挳 | 瀵煎叆寮圭獥鍐� | 鍚屽叾瀹冩ā鍧楀鍏ュ脊绐楃殑浣嶇疆锛堟ā鏉夸笅杞芥寜閽斁寮圭獥宸︿笅瑙掞級 |
+| 鏂板鏉℃暟 | 瀵煎叆寮圭獥缁撴灉鍖� | 鍙� `createList.length`锛屾枃妗堝銆屾柊澧烇細N 鏉°�� |
+| 澶辫触鏄庣粏 | 瀵煎叆寮圭獥缁撴灉鍖� | 閬嶅巻 `failureList`锛岄�愭潯灞曠ず銆宬ey锛歷alue銆嶏紝鍙粴鍔� |
+| 瀵煎叆璇存槑 | 瀵煎叆寮圭獥鍐呯伆鑹叉彁绀哄尯 | 鎻愮ず蹇呭~鍒椼�佹暟閲忓叧绯伙紙鍚堟牸 + 涓嶅悎鏍� = 妫�娴�/鎺ユ敹鏁伴噺锛夈�佺姸鎬佷负銆屽緟妫�楠屻�嶃�佹娴嬩汉鍛樼暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛 |
+| 鎻愮ず璇ˉ鍏� | 瀵煎叆寮圭獥鎴愬姛鎻愮ず | 瀵煎叆鎴愬姛鍚庡缓璁彁绀恒�屾柊澧炴楠屽崟涓恒�庡緟妫�楠屻�忕姸鎬侊紝璇峰湪鍒楄〃涓紪杈戝苟褰曞叆妫�娴嬬粨鏋溿�� |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 瀵煎叆鏂瑰紡 | 绾柊澧烇紝涓嶆洿鏂板凡鏈夋楠屽崟锛沗updateSupport` 鍙傛暟涓嶅瓨鍦� |
+| 缂栧彿鐢熸垚 | 銆屾楠屽崟缂栧彿銆嶇暀绌虹敱鍚庣鎸夌紪鐮佽鍒欒嚜鍔ㄧ敓鎴愶紙IQC / IPQC / OQC 鍚勮嚜鍓嶇紑锛夛紱濉啓鏃朵笌绯荤粺涓凡鏈夌紪鍙烽噸澶嶅垯璇ヨ澶辫触 |
+| 璐ㄦ鏂规鍖归厤 | 銆岃川妫�鏂规銆嶇暀绌烘椂鍚庣鎸夌墿鏂� + 妫�楠岀被鍨嬭嚜鍔ㄥ尮閰嶏紱鑻ヨ鐗╂枡鏈厤缃搴旂被鍨嬬殑鏂规锛岃琛屽け璐ュ苟鎻愮ず闇�閰嶇疆鏂规鐨勭墿鏂欎笌鍏ュ彛 |
+| 鏁伴噺鍏崇郴 | 鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺 蹇呴』绛変簬銆屾娴嬫暟閲忋�嶏紙IPQC/OQC锛夋垨銆屾湰娆℃帴鏀舵暟閲忋�嶏紙IQC锛夛紱鐣欑┖鏃惰嚜鍔ㄥ彇涓よ�呬箣鍜岋紱濉啓涓嶇浉绛夋椂璇ヨ澶辫触骞剁粰鍑轰袱杈圭殑瀹為檯鍊� |
+| IPQC 搴熷搧鏁伴噺 | 宸ュ簾 + 鏂欏簾 + 鍏朵粬搴熷搧 涔嬪拰涓嶅緱瓒呰繃涓嶅悎鏍煎搧鏁伴噺锛岃秴鍑鸿琛屽け璐ワ紱涓夊垪鐣欑┖鍧囨寜 0 澶勭悊 |
+| IPQC 璁板綍缁村害 | 浠ャ�屼骇鍝佺墿鏂欑紪鐮� + 鎵瑰彿銆嶈瘑鍒竴鏉¤繃绋嬫楠岃褰曪紝鎵瑰彿蹇呭~ |
+| 鐘舵�� | 瀵煎叆鍚庣殑妫�楠屽崟缁熶竴涓恒�屽緟妫�楠屻�嶏紙鑽夌锛夛紝涓嶇洿鎺ュ畬鎴� |
+| 妫�楠岃鐢熸垚 | 鐢卞悗绔寜鍖归厤鍒扮殑璐ㄦ鏂规鑷姩鐢熸垚妫�楠岃锛堟楠屾寚鏍囷級锛屾棤闇�鍓嶇棰濆澶勭悊 |
+| OQC 瀹㈡埛鏁版嵁鏉冮檺 | 銆屽鎴峰悕绉般�嶅~鍐欐椂蹇呴』鍦ㄥ綋鍓嶇櫥褰曠敤鎴风殑鍙瀹㈡埛鑼冨洿鍐咃紝鍚﹀垯璇ヨ澶辫触锛堝惁鍒欏鍏ュ悗鍒楄〃涓篃鐪嬩笉鍒拌鍗曪級锛涚暀绌哄垯涓嶆牎楠岋紝瀵煎叆鍚庝笉鍙楀鎴锋潈闄愰伄鎸� |
+| 閮ㄥ垎鎴愬姛 | 閫愯瀵煎叆锛屽け璐ヨ涓嶅奖鍝嶆垚鍔熻锛堝悓涓�鏂囦欢鍐呬笉浼氭暣浣撳洖婊氾級锛涘叏閮ㄨ涓虹┖鏃惰繑鍥炵┖缁撴灉 |
+| 绌鸿 | 鏁磋涓虹┖鐨勮褰曡嚜鍔ㄨ烦杩囷紝涓嶈涓哄け璐� |
+
+## 娉ㄦ剰浜嬮」
+
+- 銆屾楠岀被鍨嬨�嶃�屾娴嬬粨鏋溿�嶇瓑瀛楀吀鍒楀~**鍚嶇О**锛堝 棣栨銆佹牎楠岄�氳繃锛夛紝涔熷吋瀹瑰~鏁板瓧锛�1/2锛夛紱濉敊鏃跺け璐ュ師鍥犱細鍒楀嚭鍙�夊��
+- 銆岃川妫�鏂规銆嶅垪濉殑鏄�**鏂规鍚嶇О**锛屼笉鏄柟妗堢紪鐮侊紱鐣欑┖鍗充负銆岃嚜鍔ㄥ尮閰嶃�嶏紝鏄帹鑽愮敤娉�
+- IPQC 鐨勩�屾楠岀被鍨嬨�嶃�屼骇鍝佺墿鏂欑紪鐮併�嶃�屾壒鍙枫�嶉兘鏄�**蹇呭~**鍒楋紝涓旀柟妗堢殑妫�楠岀被鍨嬮渶涓庢楠屽崟鐨勭被鍨嬪彲鍖归厤锛屽惁鍒欏彲鑳芥彁绀烘湭閰嶇疆鏂规
+- 銆屾娴嬩汉鍛樸�嶇暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涘~鍐欏鍚嶆椂鑻ュ瓨鍦ㄥ悓鍚嶇敤鎴凤紝浼氬彇鍖归厤鍒扮殑绗竴鏉★紝寤鸿濉鍚嶅叏绉�
+- 涓変釜鍒楄〃鐨勫鍏ヤ簰涓嶅奖鍝嶏紝鏉冮檺鐮佸悇鑷嫭绔嬶紱鑷缓瑙掕壊闇�鍦ㄣ�岃鑹茬鐞嗐�嶄腑鍕鹃�夊搴旂殑銆孹X妫�楠屽鍏ャ�嶆寜閽悗鎵嶅彲瑙佽鎸夐挳
+- 瀵煎叆寮圭獥鐨勮繘搴︽潯 / 缁撴灉鍖� / 妯℃澘涓嬭浇浜や簰鍙洿鎺ュ鐢ㄧ幇鏈� `import-form.vue` 瀹炵幇
+- 鏃ユ湡鍒楁敮鎸� `yyyy-MM-dd HH:mm:ss` 涓� `yyyy-MM-dd` 涓ょ鏍煎紡锛汭QC 鐨勬娴�/鏉ユ枡鏃ユ湡銆丱QC 鐨勬娴嬫棩鏈熶负蹇呭~锛孖PQC 鐨勬娴嬫棩鏈熴�丱QC 鐨勫嚭璐ф棩鏈熷彲鐣欑┖
diff --git a/docs/project-business/data/flows.json b/docs/project-business/data/flows.json
index d66f454..b2bf5ec 100644
--- a/docs/project-business/data/flows.json
+++ b/docs/project-business/data/flows.json
@@ -29,12 +29,14 @@
        {"stage": "鑱斿姩", "action": "瀹℃壒閫氳繃鎸夊伐搴忔爣璁�: 鍊掑啿鈫掕嚜鍔ㄧ墿鏂欐秷鑰楁墸绾胯竟搴�(鍏佽璐熷簱瀛�)锛涜川妫�鈫掍骇鍑哄緟妫�琛岀瓑 IPQC锛涘叧閿潪妫�鈫掔洿鎺ヤ骇鍑哄叆搴�", "roles": ["绯荤粺鑷姩"]},
        {"stage": "瀹屽伐", "action": "浠呮湯閬撳伐搴忓洖鍐欏伐鍗曞凡鐢熶骇鏁伴噺锛岃揪鏍囪嚜鍔ㄥ畬鎴愶紱MPS 绱鍒ゆ柇瀹屾垚骞跺彂浜嬩欢缁欓攢鍞鍗�", "roles": ["绯荤粺鑷姩"]}]},
     {"id": "quality", "name": "璐ㄩ噺娴佺▼", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c",
-     "summary": "鍥涢亾妫�楠岄椄鍙�(IQC/IPQC/OQC/RQC锛屼簩鎬� 0鈫�4) + 鍒ゅ畾鍥涙灉(鍚堟牸/鐗归噰/閫�璐�/鎶ュ簾) + NCR 璇勫澶勭疆鍏抽棴锛汚I 鎻愪緵鍒ゅ畾寤鸿锛汵CR 褰撳墠浠呭彴璐︿笉瑙﹀彂杩斿伐鍗曪紱妫�楠屽崟鍙敱鏅鸿兘璐ㄦ鎶ュ憡骞冲彴涓�閿嚭浠讹紙generate-from-qc锛氭暟鎹粠 MES 鍙栵紝浜旈亾鏍¢獙鍚庢覆鏌撹惤搴擄級銆�",
+     "summary": "鍥涢亾妫�楠岄椄鍙�(IQC/IPQC/OQC/RQC锛屼簩鎬� 0鈫�4) + 鍒ゅ畾鍥涙灉(鍚堟牸/鐗归噰/閫�璐�/鎶ュ簾) + NCR 璇勫澶勭疆鍏抽棴锛汭QC/IPQC/OQC 鏀寔 Excel 鎵归噺瀵煎叆(绾柊澧炪�侀�愯閮ㄥ垎鎴愬姛銆佹寜涓氬姟閿幓閲�)锛屽苟鎻愪緵璺ㄥ洓绫荤殑妫�娴嬬粨鏋滃彴璐︽煡璇笌瀵煎嚭锛汚I 鎻愪緵鍒ゅ畾寤鸿锛汵CR 褰撳墠浠呭彴璐︿笉瑙﹀彂杩斿伐鍗曪紱妫�楠屽崟鍙敱鏅鸿兘璐ㄦ鎶ュ憡骞冲彴涓�閿嚭浠讹紙generate-from-qc锛氭暟鎹粠 MES 鍙栵紝浜旈亾鏍¢獙鍚庢覆鏌撹惤搴擄級銆�",
      "steps": [
        {"stage": "鏉ユ枡", "action": "鍒拌揣閫氱煡/澶栧崗鍏ュ簱鈫扞QC锛岀粨璁哄洖鍐欐帹杩涘緟鍏ュ簱鎴栬Е鍙戦��璐�", "roles": ["IQC 璐ㄦ鍛�"]},
        {"stage": "杩囩▼", "action": "鎶ュ伐璐ㄦ宸ュ簭浜у嚭琛屸啋IPQC锛屽悎鏍兼媶琛屽叆搴撱�佷笉鍚堟牸鐢熸垚 NCR", "roles": ["IPQC 璐ㄦ鍛�"]},
        {"stage": "鍑鸿揣", "action": "閿�鍞嚭搴撴彁浜も啋OQC锛屽畬鎴愬悗鍑哄簱鍗曡繘鍏ュ緟鎷h揣", "roles": ["OQC 璐ㄦ鍛�"]},
        {"stage": "閫�璐�", "action": "閫�鏂�/閿�鍞��璐�/鍞悗閫�璐р啋RQC锛岀粨璁哄洖璋冨崟鎹笌鍞悗", "roles": ["RQC 璐ㄦ鍛�"]},
+       {"stage": "瀵煎叆", "action": "IQC/IPQC/OQC 涓夊垪琛ㄥ悇鏀寔 Excel 鎵归噺瀵煎叆(鎸夐挳鍦ㄣ�屾柊澧炪�嶄笌銆屽鍑恒�嶄箣闂达紝绾柊澧炰笉鍋氭洿鏂拌鐩�)锛氫竴琛屼竴寮犲崟锛岀紪鍙风暀绌鸿嚜鍔ㄧ敓鎴愶紝璐ㄦ鏂规鐣欑┖鎸夌墿鏂�+妫�楠岀被鍨嬭嚜鍔ㄥ尮閰嶏紝妫�娴嬩汉鍛樼暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涚墿鏂�/渚涘簲鍟�/瀹㈡埛鎸夌紪鐮佹垨鍚嶇О绮剧‘鍖归厤(IQC 渚涘簲鍟嗕笌渚涘簲鍟嗘壒娆″彲鐣欑┖銆丱QC 瀹㈡埛鍙暀绌轰笖鐣欑┖涓嶅彈瀹㈡埛鏁版嵁鏉冮檺閬尅)锛屽悎鏍�+涓嶅悎鏍�=妫�娴�/鎺ユ敹鏁伴噺銆両PQC 宸ュ簾+鏂欏簾+鍏朵粬搴熷搧鈮や笉鍚堟牸(涓夊垪鐣欑┖鎸�0)锛孫QC 濉鎴锋椂椤诲湪鏈汉瀹㈡埛鏁版嵁鏉冮檺鍐咃紱閫愯瀵煎叆閮ㄥ垎鎴愬姛锛屽け璐ヨ杩� failureList 甯﹁鍙蜂笌鍙墽琛屽姩浣滐紝鎴愬姛鍗曚竴寰嬪緟妫�楠�0锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏ワ細鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡瀛樺湪鍚屼笟鍔¢敭鍗曟嵁(IQC 鐗╂枡+渚涘簲鍟�+渚涘簲鍟嗘壒娆�+鏉ユ枡鏃ユ湡銆両PQC 鐗╂枡+鎵瑰彿+妫�楠岀被鍨�+妫�娴嬫棩鏈熴�丱QC 鐗╂枡+瀹㈡埛+鎵规鍙�+鍑鸿揣鏃ユ湡)鍧囨嫆璇ヨ锛屾彁绀哄惈缁村害銆佸弻鏂瑰疄闄呭�间笌銆屽厛鍒犻櫎璇ュ崟鎹啀閲嶈瘯銆嶅姩浣�", "roles": ["璐ㄦ鍛�", "IQC 璐ㄦ鍛�", "IPQC 璐ㄦ鍛�", "OQC 璐ㄦ鍛�"]},
+       {"stage": "鍙拌处", "action": "妫�娴嬬粨鏋滃彴璐︼細璺� IQC/IPQC/OQC/RQC 鍥涚被妫�楠屽崟缁熶竴鏌ヨ(鍒嗙被+妫�娴嬫棩鏈熷尯闂�+妫�楠屽崟鍙�+鐗╂枡+鎵规鍙�+妫�娴嬬粨鏋�+妫�娴嬩汉)锛屼竴琛屼竴涓娴嬮」缁撴灉锛屽垽瀹氫緷鎹敱鏍囧噯鍊间笌涓婁笅闄愭嫾瑁咃紱鎵规鍙锋寜绫诲瀷鍙栨潵婧�(IQC 鍙栦緵搴斿晢鎵规鍙凤紝IPQC/OQC/RQC 鍙栨壒鍙�)锛涘悓濂楃瓫閫夋潯浠剁洿鎺ュ鍑� Excel 鍙拌处鐢ㄤ簬鎵撳嵃瀛樻。锛涙湭褰曟娴嬮」鐨勫崟浠嶄繚鐣欎竴琛屼笉婕忓崟", "roles": ["璐ㄦ鍛�", "璐ㄩ噺宸ョ▼甯�"]},
        {"stage": "涓嶅悎鏍�", "action": "NCR: 寰呰瘎瀹♀啋璇勫鈫掑缃�(閫�璐�/鎶ュ簾/杩斿伐/璁╂鎺ユ敹/闄嶇骇)鈫掑叧闂�", "roles": ["璐ㄩ噺宸ョ▼甯�"]},
        {"stage": "鍑轰欢", "action": "涓ゆ潯鍑轰欢鍏ュ彛锛氣憼閫氱敤 generate/preview(璋冪敤鏂硅嚜甯︿笂涓嬫枃) 娓叉煋 HTML 骞跺喕缁撴暟鎹揩鐓э紝缂栧彿 QR+鏃ユ湡+4浣嶆祦姘存寜澶╃嫭绔嬶紝preview 鍙覆鏌撲笉钀藉簱涓嶆秷鑰楃紪鍙凤紝regenerate 鐢ㄥ喕缁撳揩鐓ч噸娓蹭笖鏁版嵁涓庣紪鍙蜂笉鍙樺苟浣滃簾宸插綊妗� PDF锛屽鍑� PDF 鐢卞悗绔皟 Chromium 鎵撳嵃瀹炰緥宸插瓨鐨� HTML 鍚庡綊妗i檮浠朵腑蹇冿紱鈶enerate-from-qc 鐢辫川妫�鍗曠洿鎺ュ嚭浠�(鏁版嵁缁� MesQcReportApi 浠� MES 鍙栵紝鍓嶇涓嶆嫾涓婁笅鏂�)锛屽嚭浠跺墠浜旈亾鏍¢獙鎸変笟鍔′紭鍏堢骇鍙姤绗竴澶勶細绫诲瀷闈炴硶/鍗曟嵁涓嶅瓨鍦�/鏈畬鎴愭楠�/鏈~鍒ゅ畾/妯℃澘绫诲瀷涓嶇/鏃犲彲鐢ㄦ楠岄」锛屾姤鍛婂ご鍙﹀甫鍗曟嵁鍘熷垽瀹� qcResult+qcResultText 涓庡紩鎿庡垽瀹氬苟瀛橈紝鍝嶅簲鍥� undecidableCount 寰呭垽瀹氶」鏁颁笌 warnings(鍒嗙粍鏍囬璺宠繃/鎸囨爣宸插垹/鏈綍瀹炴祴鍊�/鏍峰搧瓒�3)锛屽悓涓�寮犲崟鎹彲鍙嶅鍑哄浠�", "roles": ["璐ㄦ鍛�"]},
        {"stage": "寤烘ā鏉�", "action": "AI 瀵煎叆锛氫笂浼犳棦鏈夋楠屾姤鍛婃枃浠�(鎵弿浠�/鐓х墖銆佺數瀛愮増 PDF銆乄ord/Excel) 鈫� 璇嗗埆鎴愭ā鏉胯崏绋�(涓嶈惤搴�) 鈫� 浜哄伐鍦ㄨ璁″櫒纭璋冩暣鍚庝繚瀛樹负鐗堟湰锛�.doc/.xls 鏃ф牸寮忎笉鏀寔", "roles": ["璐ㄦ鍛�", "璐ㄩ噺宸ョ▼甯�"]}]},
diff --git a/docs/project-business/data/modules.json b/docs/project-business/data/modules.json
index db8b10f..88e0ed1 100644
--- a/docs/project-business/data/modules.json
+++ b/docs/project-business/data/modules.json
@@ -11,7 +11,7 @@
     {"key": "crm", "name": "閿�鍞鐞嗭紙瀹㈡埛鍏崇郴锛�", "color": "#4f9cff", "tag": "CRM", "flow": "sales",
      "positioning": "閿�鍞墠绔細浠庣嚎绱㈠埌鍥炴鐨勫鎴风粡钀�",
      "problem": "瀹㈡埛璧勪骇娌夋穩銆佸晢鏈鸿浆鍖栥�佸悎鍚屽鎵逛笌寮�绁ㄥ洖娆剧鎺�",
-     "features": ["绾跨储杞寲瀹㈡埛", "鍏捣姹犱笌棰嗗彇涓婇檺", "鑱旂郴浜�", "鍟嗘満鑷畾涔夐樁娈�", "璺熻繘璁板綍", "鎶ヤ环鍗�(AI OCR)", "鍚堝悓(BPM瀹℃壒鈫掔敓鎴愰攢鍞鍗�)", "寮�绁�(BPM)鈫掑洖娆�(鍏堝紑绁ㄦ牎楠�)", "鍥炴璁″垝", "閿�鍞洰鏍�", "鏁版嵁鏉冮檺涓夌骇(璐熻矗浜�/鍙/璇诲啓)"],
+     "features": ["绾跨储杞寲瀹㈡埛", "鍏捣姹犱笌棰嗗彇涓婇檺", "鑱旂郴浜�", "鍟嗘満鑷畾涔夐樁娈�", "璺熻繘璁板綍", "鎶ヤ环鍗�(AI OCR)", "鍚堝悓(BPM瀹℃壒鈫掔敓鎴愰攢鍞鍗�)", "寮�绁�(BPM瀹℃壒路鏀寔Excel瀵煎叆路鍚屽悎鍚屽悓鍙戠エ鍙风爜/鍚岄噾棰濇姮澶存棩鏈熶笉鍙噸澶嶅鍏�)鈫掑洖娆�(鍏堝紑绁ㄦ牎楠�)", "鍥炴璁″垝", "閿�鍞洰鏍�", "鏁版嵁鏉冮檺涓夌骇(璐熻矗浜�/鍙/璇诲啓)"],
      "objects": ["绾跨储", "瀹㈡埛", "鑱旂郴浜�", "鍟嗘満", "鎶ヤ环鍗�", "鍚堝悓", "鍙戠エ", "鍥炴", "閿�鍞洰鏍�"],
      "roles": ["閿�鍞唴鍕�", "閿�鍞粡鐞�", "璐㈠姟(寮�绁ㄥ洖娆�)"],
      "upstream": ["涓绘暟鎹�(鐗╂枡)"], "downstream": ["杩涢攢瀛�(閿�鍞鍗�)", "鍞悗(瀹㈡埛)", "AI(鎶ヤ环OCR)"],
diff --git a/docs/project-business/data/objects.json b/docs/project-business/data/objects.json
index ca2a564..4a0b889 100644
--- a/docs/project-business/data/objects.json
+++ b/docs/project-business/data/objects.json
@@ -8,7 +8,7 @@
     {"name": "鍟嗘満", "module": "閿�鍞鐞�", "table": "crm_business", "doc": "閿�鍞満浼氾紝DB 閰嶇疆寮忛樁娈垫帹杩涳紝缁撴潫鎬�: 璧�/杈�/鏃犳晥", "upstream": ["瀹㈡埛"], "downstream": ["鎶ヤ环鍗�", "鍚堝悓"], "keywords": "鍟嗘満 business 璧㈠崟 杈撳崟 闃舵"},
     {"name": "鎶ヤ环鍗�", "module": "閿�鍞鐞�", "table": "crm_sale_quotation", "doc": "QT缂栧彿锛岄�� MDM 鐗╂枡鎶ヤ环锛孉I OCR 褰曞叆锛岃崏绋库啋宸茶浆鍚堝悓", "upstream": ["鍟嗘満"], "downstream": ["鍚堝悓"], "keywords": "鎶ヤ环 quotation QT"},
     {"name": "鍚堝悓", "module": "閿�鍞鐞�", "table": "crm_contract", "doc": "HT缂栧彿锛孊PM 瀹℃壒(0鑽夌/10瀹℃壒涓�/20閫氳繃/30椹冲洖/40鍙栨秷/50浣滃簾)锛岄�氳繃鍚庡彲鐢熸垚閿�鍞鍗�", "upstream": ["鎶ヤ环鍗�"], "downstream": ["閿�鍞鍗�", "鍥炴璁″垝", "寮�绁�"], "keywords": "鍚堝悓 contract 瀹℃壒"},
-    {"name": "鍙戠エ", "module": "閿�鍞鐞�", "table": "crm_invoice", "doc": "KP缂栧彿锛孊PM 瀹℃壒锛岀疮璁″紑绁ㄢ墹鍚堝悓棰濓紝闄勪欢蹇呬紶", "upstream": ["鍚堝悓"], "downstream": ["鍥炴"], "keywords": "寮�绁� 鍙戠エ invoice"},
+    {"name": "鍙戠エ", "module": "閿�鍞鐞�", "table": "crm_invoice", "doc": "KP缂栧彿锛堝彲鐣欑┖鑷姩鐢熸垚銆佺郴缁熷唴鍞竴锛夛紝BPM 瀹℃壒锛岀疮璁″紑绁ㄢ墹鍚堝悓棰濓紝闄勪欢蹇呬紶锛涙敮鎸� Excel 瀵煎叆锛堢函鏂板锛屽悎鍚岄』瀹℃牳閫氳繃涓旀湰娆¢噾棰濃墹鍚堝悓鍓╀綑鍙紑绁ㄩ锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏モ�斺�斿悓鍚堝悓涓嬪彂绁ㄥ彿鐮佺浉鍚屻�佹垨鍙戠エ鍙风爜鐣欑┖鏃跺紑绁ㄩ噾棰�+鍙戠エ鎶ご+寮�绁ㄦ棩鏈熺浉鍚屽嵆鍒ら噸澶嶏紝鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡鏈夊崟閮芥嫆璇ヨ骞跺洖鏄惧弻鏂瑰疄闄呭�硷級", "upstream": ["鍚堝悓", "Excel 瀵煎叆"], "downstream": ["鍥炴"], "keywords": "寮�绁� 鍙戠エ invoice 瀵煎叆 鍘婚噸"},
     {"name": "鍥炴", "module": "閿�鍞鐞�", "table": "crm_receivable", "doc": "HK缂栧彿锛屽垱寤哄嵆瀹℃壒涓�(鎵嬪姩瀹℃牳)锛屽己鍒跺厛寮�绁紝閲戦鍙屾牎楠�", "upstream": ["鍚堝悓", "鍙戠エ"], "downstream": ["鏀舵鍗�(鍏宠仈)"], "keywords": "鍥炴 鏀舵璁″垝 receivable"},
     {"name": "閿�鍞洰鏍�", "module": "閿�鍞鐞�", "table": "crm_sales_target", "doc": "骞�/瀛�/鏈埫椾釜浜�/閮ㄩ棬/浜у搧缁村害鐨勫悎鍚屽洖娆剧瓑鐩爣", "upstream": [], "downstream": ["涓氱哗缁熻"], "keywords": "鐩爣 target 涓氱哗"},
     {"name": "渚涘簲鍟�", "module": "渚涘簲鍟嗙鐞�", "table": "srm_supplier", "doc": "渚涘簲鍟嗘。妗堬細鎵х収/绋庡彿/閾惰/鑱旂郴浜猴紝ERP 涓� MES 鍏辩敤", "upstream": ["鍑嗗叆鐢宠"], "downstream": ["閲囪喘璁㈠崟", "鍒拌揣閫氱煡", "渚涘簲鍟嗛��璐�", "缁╂晥璇勪环"], "keywords": "渚涘簲鍟� supplier vendor"},
@@ -35,9 +35,9 @@
     {"name": "鐩樼偣浠诲姟", "module": "浠撳偍", "table": "mes_wm_stock_taking_task", "doc": "闈欐��/鍔ㄦ�佺洏鐐癸紝琛岀粨鏋�:姝e父/鐩樼泩(ADJUST_IN)/鐩樹簭(ADJUST_OUT)锛屽彲鍐荤粨搴撳瓨銆佺洸鐩�", "upstream": ["搴撳瓨鍙拌处"], "downstream": ["搴撳瓨鍙拌处(璋冩暣)"], "keywords": "鐩樼偣 鐩樼泩 鐩樹簭 stock taking"},
     {"name": "搴撳瓨鍙拌处", "module": "浠撳偍", "table": "mes_wm_material_stock", "doc": "鐗╂枡脳浠撳簱脳搴撳尯脳搴撲綅脳鎵规脳渚涘簲鍟嗗叚鍏冪粍锛屾暟閲�/鍐荤粨/鍗犵敤/鍦ㄩ�斿洓閲�", "upstream": ["鍏ㄩ儴鍏ュ簱鍗�"], "downstream": ["鍏ㄩ儴鍑哄簱鍗�", "浜嬪姟娴佹按"], "keywords": "搴撳瓨 鍙拌处 stock 鍙敤閲�"},
     {"name": "鎵规", "module": "浠撳偍", "table": "mes_wm_batch", "doc": "鍏宠仈鐗╂枡/宸ュ崟/渚涘簲鍟�/瀹㈡埛/璐ㄩ噺鐘舵�侊紱SN 鐮佸惈鎵规 uuid锛涙潯鐮佹敮鎸�18绉嶄笟鍔″璞�", "upstream": ["鍏ュ簱鍗曟嵁"], "downstream": ["搴撳瓨鍙拌处"], "keywords": "鎵规 batch SN 鏉$爜 杩芥函"},
-    {"name": "IQC鏉ユ枡妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_iqc", "doc": "鍒拌揣閫氱煡/澶栧崗鍏ュ簱瑙﹀彂锛岃崏绋�0鈫掑畬鎴�4锛岀粨璁哄洖鍐欐潵婧愬崟鎹紱琛屾搷浣溿�屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙�", "upstream": ["鍒拌揣閫氱煡", "澶栧崗鍏ュ簱"], "downstream": ["閲囪喘鍏ュ簱", "渚涘簲鍟嗛��璐�", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IQC 鏉ユ枡妫�楠� 鍑烘姤鍛�"},
-    {"name": "IPQC杩囩▼妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_ipqc", "doc": "鎶ュ伐璐ㄦ宸ュ簭浜у嚭琛岃Е鍙戯紝瀹屾垚鍥炶皟鎺ㄨ繘鎶ュ伐涓庝骇鍑哄叆搴擄紱琛ㄥ崟鍙暀銆屾潵婧愬崟鎹� + 浜у搧鐗╂枡(鍓嶇鎵嬮�� MDM 鐗╂枡) + 鎵瑰彿(蹇呭~) + 鏁伴噺 + 妫�楠屼俊鎭�嶏紝宸插幓鐢熶骇璁㈠崟/鐢熶骇浠诲姟/宸ヤ綅/宸ュ簭锛屾壒鍙烽殢鎶ュ憡钀芥锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙�", "upstream": ["鎶ュ伐"], "downstream": ["浜у嚭鍏ュ簱", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IPQC 杩囩▼妫�楠� 宸℃ 鍑烘姤鍛� 鎵瑰彿 batchCode MDM鐗╂枡"},
-    {"name": "OQC鍑鸿揣妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_oqc", "doc": "閿�鍞嚭搴撴彁浜よЕ鍙戯紝瀹屾垚鎺ㄨ繘鍑哄簱鍗曞緟鎷h揣锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙�", "upstream": ["閿�鍞嚭搴撳崟"], "downstream": ["閿�鍞嚭搴撳崟(鎺ㄨ繘)", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "OQC 鍑鸿揣妫�楠� 鍑哄巶妫�楠� 鍑烘姤鍛�"},
+    {"name": "IQC鏉ユ枡妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_iqc", "doc": "鍒拌揣閫氱煡/澶栧崗鍏ュ簱瑙﹀彂锛岃崏绋�0鈫掑畬鎴�4锛岀粨璁哄洖鍐欐潵婧愬崟鎹紱琛屾搷浣溿�屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙侊紱鏀寔 Excel 鎵归噺瀵煎叆锛堝伐鍏锋爮銆屾壒閲忓鍏ャ�嶏紝绾柊澧炪�佸鍏ュ崟涓鸿崏绋垮緟妫�楠屻�佺紪鍙峰彲鐣欑┖鑷姩鐢熸垚銆佽川妫�鏂规鐣欑┖鎸夌墿鏂欒嚜鍔ㄥ尮閰嶏級锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏ワ紙鐗╂枡+渚涘簲鍟�+渚涘簲鍟嗘壒娆�+鏉ユ枡鏃ユ湡锛屾枃浠跺唴閲嶅琛屼笌搴撳唴宸叉湁鍗曢兘鎷掕琛屽苟鍥炴樉鍙屾柟瀹為檯鍊硷級", "upstream": ["鍒拌揣閫氱煡", "澶栧崗鍏ュ簱", "Excel 鎵归噺瀵煎叆"], "downstream": ["閲囪喘鍏ュ簱", "渚涘簲鍟嗛��璐�", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IQC 鏉ユ枡妫�楠� 鍑烘姤鍛� 鎵归噺瀵煎叆 瀵煎叆妯℃澘"},
+    {"name": "IPQC杩囩▼妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_ipqc", "doc": "鎶ュ伐璐ㄦ宸ュ簭浜у嚭琛岃Е鍙戯紝瀹屾垚鍥炶皟鎺ㄨ繘鎶ュ伐涓庝骇鍑哄叆搴擄紱琛ㄥ崟鍙暀銆屾潵婧愬崟鎹� + 浜у搧鐗╂枡(鍓嶇鎵嬮�� MDM 鐗╂枡) + 鎵瑰彿(蹇呭~) + 鏁伴噺 + 妫�楠屼俊鎭�嶏紝宸插幓鐢熶骇璁㈠崟/鐢熶骇浠诲姟/宸ヤ綅/宸ュ簭锛屾壒鍙烽殢鎶ュ憡钀芥锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙侊紱鏀寔 Excel 鎵归噺瀵煎叆锛堟ā鏉挎寜銆屼骇鍝佺墿鏂欑紪鐮� + 鎵瑰彿(蹇呭~) + 妫�楠岀被鍨嬨�嶅~鍒楋紝涓嶅~宸ュ崟/宸ヤ綅锛涘伐搴�+鏂欏簾+鍏朵粬搴熷搧涓嶅緱瓒呰繃涓嶅悎鏍煎搧鏁伴噺锛屼笁鍒楃暀绌烘寜 0锛夛紱鍚屼竴鎵规暟鎹笉鍙噸澶嶅鍏ワ紙鐗╂枡+鎵瑰彿+妫�楠岀被鍨�+妫�娴嬫棩鏈燂紝鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡鏈夊崟閮芥嫆璇ヨ锛�", "upstream": ["鎶ュ伐", "Excel 鎵归噺瀵煎叆"], "downstream": ["浜у嚭鍏ュ簱", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IPQC 杩囩▼妫�楠� 宸℃ 鍑烘姤鍛� 鎵瑰彿 batchCode MDM鐗╂枡 鎵归噺瀵煎叆 瀵煎叆妯℃澘"},
+    {"name": "OQC鍑鸿揣妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_oqc", "doc": "閿�鍞嚭搴撴彁浜よЕ鍙戯紝瀹屾垚鎺ㄨ繘鍑哄簱鍗曞緟鎷h揣锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙侊紱鏀寔 Excel 鎵归噺瀵煎叆锛堝鎴峰悕绉�/鏈鍑鸿揣鏁伴噺/鍑鸿揣鏃ユ湡鍧囧彲鐣欑┖锛岀暀绌哄嵆璇ュ崟涓嶅睘浠讳綍瀹㈡埛銆佷笉鍙楀鎴锋暟鎹潈闄愰伄鎸★紱濉鎴锋椂椤诲湪鏈汉瀹㈡埛鏁版嵁鏉冮檺鍐咃級锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏ワ紙鐗╂枡+瀹㈡埛+鎵规鍙�+鍑鸿揣鏃ユ湡锛屾枃浠跺唴閲嶅琛屼笌搴撳唴宸叉湁鍗曢兘鎷掕琛岋級", "upstream": ["閿�鍞嚭搴撳崟", "Excel 鎵归噺瀵煎叆"], "downstream": ["閿�鍞嚭搴撳崟(鎺ㄨ繘)", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "OQC 鍑鸿揣妫�楠� 鍑哄巶妫�楠� 鍑烘姤鍛� 鎵归噺瀵煎叆 瀵煎叆妯℃澘"},
     {"name": "RQC閫�璐ф楠�", "module": "璐ㄩ噺", "table": "mes_qc_rqc", "doc": "鐢熶骇閫�鏂�/閿�鍞��璐�/鍞悗閫�璐цЕ鍙戯紝缁撹鍥炶皟鍞悗锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙併�傛帾杈炲凡缁熶竴涓恒�岄��璐ф楠屻�嶏紙鍘熻彍鍗曠埗椤逛笌 mes_qc_type 瀛楀吀鍐欍�岄��鏂欐楠屻�嶏紝涓庡悗绔灇涓�/琛ㄦ敞閲�/鑿滃崟鏉冮檺瀛愰」涓嶄竴鑷达紱RQC 涓夎�呴兘瑕嗙洊锛屾晠鍙栥�岄��璐ф楠屻�嶏級", "upstream": ["閫�鏂�", "閫�璐�"], "downstream": ["閫�璐у叆搴�", "鍞悗鍥炶皟", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "RQC 閫�璐ф楠� 閫�鏂欐楠� 鍑烘姤鍛�"},
     {"name": "NCR涓嶅悎鏍煎搧", "module": "璐ㄩ噺", "table": "mes_qc_ncr", "doc": "寰呰瘎瀹�0鈫掑凡璇勫6鈫掑凡澶勭疆7鈫掑凡鍏抽棴8锛屽缃�:閫�璐�/鎶ュ簾/杩斿伐/璁╂鎺ユ敹/闄嶇骇", "upstream": ["IQC/IPQC/OQC/RQC"], "downstream": ["閫�璐�/鎶ュ簾/杩斿伐(鍙拌处)"], "keywords": "NCR 涓嶅悎鏍� 澶勭疆 鐗归噰 鎶ュ簾"},
     {"name": "璁惧", "module": "璁惧", "table": "mes_dv_machinery", "doc": "鍙拌处鐘舵��:鍋滄満/鐢熶骇涓�/淇濆吇涓紱鐐规路淇濆吇路缁翠慨宸ュ崟锛汚I 椋庨櫓棰勬祴娑堣垂鍏舵暟鎹�", "upstream": [], "downstream": ["鐐规", "缁翠慨", "鐢熶骇椋庨櫓棰勬祴"], "keywords": "璁惧 鐐规 淇濆吇 缁翠慨 machinery"},
diff --git a/docs/project-business/index.html b/docs/project-business/index.html
index 551ce3d..42891f8 100644
--- a/docs/project-business/index.html
+++ b/docs/project-business/index.html
@@ -341,7 +341,7 @@
     {"key": "crm", "name": "閿�鍞鐞嗭紙瀹㈡埛鍏崇郴锛�", "color": "#4f9cff", "tag": "CRM", "flow": "sales",
      "positioning": "閿�鍞墠绔細浠庣嚎绱㈠埌鍥炴鐨勫鎴风粡钀�",
      "problem": "瀹㈡埛璧勪骇娌夋穩銆佸晢鏈鸿浆鍖栥�佸悎鍚屽鎵逛笌寮�绁ㄥ洖娆剧鎺�",
-     "features": ["绾跨储杞寲瀹㈡埛", "鍏捣姹犱笌棰嗗彇涓婇檺", "鑱旂郴浜�", "鍟嗘満鑷畾涔夐樁娈�", "璺熻繘璁板綍", "鎶ヤ环鍗�(AI OCR)", "鍚堝悓(BPM瀹℃壒鈫掔敓鎴愰攢鍞鍗�)", "寮�绁�(BPM)鈫掑洖娆�(鍏堝紑绁ㄦ牎楠�)", "鍥炴璁″垝", "閿�鍞洰鏍�", "鏁版嵁鏉冮檺涓夌骇(璐熻矗浜�/鍙/璇诲啓)"],
+     "features": ["绾跨储杞寲瀹㈡埛", "鍏捣姹犱笌棰嗗彇涓婇檺", "鑱旂郴浜�", "鍟嗘満鑷畾涔夐樁娈�", "璺熻繘璁板綍", "鎶ヤ环鍗�(AI OCR)", "鍚堝悓(BPM瀹℃壒鈫掔敓鎴愰攢鍞鍗�)", "寮�绁�(BPM瀹℃壒路鏀寔Excel瀵煎叆路鍚屽悎鍚屽悓鍙戠エ鍙风爜/鍚岄噾棰濇姮澶存棩鏈熶笉鍙噸澶嶅鍏�)鈫掑洖娆�(鍏堝紑绁ㄦ牎楠�)", "鍥炴璁″垝", "閿�鍞洰鏍�", "鏁版嵁鏉冮檺涓夌骇(璐熻矗浜�/鍙/璇诲啓)"],
      "objects": ["绾跨储", "瀹㈡埛", "鑱旂郴浜�", "鍟嗘満", "鎶ヤ环鍗�", "鍚堝悓", "鍙戠エ", "鍥炴", "閿�鍞洰鏍�"],
      "roles": ["閿�鍞唴鍕�", "閿�鍞粡鐞�", "璐㈠姟(寮�绁ㄥ洖娆�)"],
      "upstream": ["涓绘暟鎹�(鐗╂枡)"], "downstream": ["杩涢攢瀛�(閿�鍞鍗�)", "鍞悗(瀹㈡埛)", "AI(鎶ヤ环OCR)"],
@@ -451,7 +451,7 @@
     {"name": "鍟嗘満", "module": "閿�鍞鐞�", "table": "crm_business", "doc": "閿�鍞満浼氾紝DB 閰嶇疆寮忛樁娈垫帹杩涳紝缁撴潫鎬�: 璧�/杈�/鏃犳晥", "upstream": ["瀹㈡埛"], "downstream": ["鎶ヤ环鍗�", "鍚堝悓"], "keywords": "鍟嗘満 business 璧㈠崟 杈撳崟 闃舵"},
     {"name": "鎶ヤ环鍗�", "module": "閿�鍞鐞�", "table": "crm_sale_quotation", "doc": "QT缂栧彿锛岄�� MDM 鐗╂枡鎶ヤ环锛孉I OCR 褰曞叆锛岃崏绋库啋宸茶浆鍚堝悓", "upstream": ["鍟嗘満"], "downstream": ["鍚堝悓"], "keywords": "鎶ヤ环 quotation QT"},
     {"name": "鍚堝悓", "module": "閿�鍞鐞�", "table": "crm_contract", "doc": "HT缂栧彿锛孊PM 瀹℃壒(0鑽夌/10瀹℃壒涓�/20閫氳繃/30椹冲洖/40鍙栨秷/50浣滃簾)锛岄�氳繃鍚庡彲鐢熸垚閿�鍞鍗�", "upstream": ["鎶ヤ环鍗�"], "downstream": ["閿�鍞鍗�", "鍥炴璁″垝", "寮�绁�"], "keywords": "鍚堝悓 contract 瀹℃壒"},
-    {"name": "鍙戠エ", "module": "閿�鍞鐞�", "table": "crm_invoice", "doc": "KP缂栧彿锛孊PM 瀹℃壒锛岀疮璁″紑绁ㄢ墹鍚堝悓棰濓紝闄勪欢蹇呬紶", "upstream": ["鍚堝悓"], "downstream": ["鍥炴"], "keywords": "寮�绁� 鍙戠エ invoice"},
+    {"name": "鍙戠エ", "module": "閿�鍞鐞�", "table": "crm_invoice", "doc": "KP缂栧彿锛堝彲鐣欑┖鑷姩鐢熸垚銆佺郴缁熷唴鍞竴锛夛紝BPM 瀹℃壒锛岀疮璁″紑绁ㄢ墹鍚堝悓棰濓紝闄勪欢蹇呬紶锛涙敮鎸� Excel 瀵煎叆锛堢函鏂板锛屽悎鍚岄』瀹℃牳閫氳繃涓旀湰娆¢噾棰濃墹鍚堝悓鍓╀綑鍙紑绁ㄩ锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏モ�斺�斿悓鍚堝悓涓嬪彂绁ㄥ彿鐮佺浉鍚屻�佹垨鍙戠エ鍙风爜鐣欑┖鏃跺紑绁ㄩ噾棰�+鍙戠エ鎶ご+寮�绁ㄦ棩鏈熺浉鍚屽嵆鍒ら噸澶嶏紝鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡鏈夊崟閮芥嫆璇ヨ骞跺洖鏄惧弻鏂瑰疄闄呭�硷級", "upstream": ["鍚堝悓", "Excel 瀵煎叆"], "downstream": ["鍥炴"], "keywords": "寮�绁� 鍙戠エ invoice 瀵煎叆 鍘婚噸"},
     {"name": "鍥炴", "module": "閿�鍞鐞�", "table": "crm_receivable", "doc": "HK缂栧彿锛屽垱寤哄嵆瀹℃壒涓�(鎵嬪姩瀹℃牳)锛屽己鍒跺厛寮�绁紝閲戦鍙屾牎楠�", "upstream": ["鍚堝悓", "鍙戠エ"], "downstream": ["鏀舵鍗�(鍏宠仈)"], "keywords": "鍥炴 鏀舵璁″垝 receivable"},
     {"name": "閿�鍞洰鏍�", "module": "閿�鍞鐞�", "table": "crm_sales_target", "doc": "骞�/瀛�/鏈埫椾釜浜�/閮ㄩ棬/浜у搧缁村害鐨勫悎鍚屽洖娆剧瓑鐩爣", "upstream": [], "downstream": ["涓氱哗缁熻"], "keywords": "鐩爣 target 涓氱哗"},
     {"name": "渚涘簲鍟�", "module": "渚涘簲鍟嗙鐞�", "table": "srm_supplier", "doc": "渚涘簲鍟嗘。妗堬細鎵х収/绋庡彿/閾惰/鑱旂郴浜猴紝ERP 涓� MES 鍏辩敤", "upstream": ["鍑嗗叆鐢宠"], "downstream": ["閲囪喘璁㈠崟", "鍒拌揣閫氱煡", "渚涘簲鍟嗛��璐�", "缁╂晥璇勪环"], "keywords": "渚涘簲鍟� supplier vendor"},
@@ -478,9 +478,9 @@
     {"name": "鐩樼偣浠诲姟", "module": "浠撳偍", "table": "mes_wm_stock_taking_task", "doc": "闈欐��/鍔ㄦ�佺洏鐐癸紝琛岀粨鏋�:姝e父/鐩樼泩(ADJUST_IN)/鐩樹簭(ADJUST_OUT)锛屽彲鍐荤粨搴撳瓨銆佺洸鐩�", "upstream": ["搴撳瓨鍙拌处"], "downstream": ["搴撳瓨鍙拌处(璋冩暣)"], "keywords": "鐩樼偣 鐩樼泩 鐩樹簭 stock taking"},
     {"name": "搴撳瓨鍙拌处", "module": "浠撳偍", "table": "mes_wm_material_stock", "doc": "鐗╂枡脳浠撳簱脳搴撳尯脳搴撲綅脳鎵规脳渚涘簲鍟嗗叚鍏冪粍锛屾暟閲�/鍐荤粨/鍗犵敤/鍦ㄩ�斿洓閲�", "upstream": ["鍏ㄩ儴鍏ュ簱鍗�"], "downstream": ["鍏ㄩ儴鍑哄簱鍗�", "浜嬪姟娴佹按"], "keywords": "搴撳瓨 鍙拌处 stock 鍙敤閲�"},
     {"name": "鎵规", "module": "浠撳偍", "table": "mes_wm_batch", "doc": "鍏宠仈鐗╂枡/宸ュ崟/渚涘簲鍟�/瀹㈡埛/璐ㄩ噺鐘舵�侊紱SN 鐮佸惈鎵规 uuid锛涙潯鐮佹敮鎸�18绉嶄笟鍔″璞�", "upstream": ["鍏ュ簱鍗曟嵁"], "downstream": ["搴撳瓨鍙拌处"], "keywords": "鎵规 batch SN 鏉$爜 杩芥函"},
-    {"name": "IQC鏉ユ枡妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_iqc", "doc": "鍒拌揣閫氱煡/澶栧崗鍏ュ簱瑙﹀彂锛岃崏绋�0鈫掑畬鎴�4锛岀粨璁哄洖鍐欐潵婧愬崟鎹紱琛屾搷浣溿�屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙�", "upstream": ["鍒拌揣閫氱煡", "澶栧崗鍏ュ簱"], "downstream": ["閲囪喘鍏ュ簱", "渚涘簲鍟嗛��璐�", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IQC 鏉ユ枡妫�楠� 鍑烘姤鍛�"},
-    {"name": "IPQC杩囩▼妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_ipqc", "doc": "鎶ュ伐璐ㄦ宸ュ簭浜у嚭琛岃Е鍙戯紝瀹屾垚鍥炶皟鎺ㄨ繘鎶ュ伐涓庝骇鍑哄叆搴擄紱琛ㄥ崟鍙暀銆屾潵婧愬崟鎹� + 浜у搧鐗╂枡(鍓嶇鎵嬮�� MDM 鐗╂枡) + 鎵瑰彿(蹇呭~) + 鏁伴噺 + 妫�楠屼俊鎭�嶏紝宸插幓鐢熶骇璁㈠崟/鐢熶骇浠诲姟/宸ヤ綅/宸ュ簭锛屾壒鍙烽殢鎶ュ憡钀芥锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙�", "upstream": ["鎶ュ伐"], "downstream": ["浜у嚭鍏ュ簱", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IPQC 杩囩▼妫�楠� 宸℃ 鍑烘姤鍛� 鎵瑰彿 batchCode MDM鐗╂枡"},
-    {"name": "OQC鍑鸿揣妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_oqc", "doc": "閿�鍞嚭搴撴彁浜よЕ鍙戯紝瀹屾垚鎺ㄨ繘鍑哄簱鍗曞緟鎷h揣锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙�", "upstream": ["閿�鍞嚭搴撳崟"], "downstream": ["閿�鍞嚭搴撳崟(鎺ㄨ繘)", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "OQC 鍑鸿揣妫�楠� 鍑哄巶妫�楠� 鍑烘姤鍛�"},
+    {"name": "IQC鏉ユ枡妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_iqc", "doc": "鍒拌揣閫氱煡/澶栧崗鍏ュ簱瑙﹀彂锛岃崏绋�0鈫掑畬鎴�4锛岀粨璁哄洖鍐欐潵婧愬崟鎹紱琛屾搷浣溿�屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙侊紱鏀寔 Excel 鎵归噺瀵煎叆锛堝伐鍏锋爮銆屾壒閲忓鍏ャ�嶏紝绾柊澧炪�佸鍏ュ崟涓鸿崏绋垮緟妫�楠屻�佺紪鍙峰彲鐣欑┖鑷姩鐢熸垚銆佽川妫�鏂规鐣欑┖鎸夌墿鏂欒嚜鍔ㄥ尮閰嶏級锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏ワ紙鐗╂枡+渚涘簲鍟�+渚涘簲鍟嗘壒娆�+鏉ユ枡鏃ユ湡锛屾枃浠跺唴閲嶅琛屼笌搴撳唴宸叉湁鍗曢兘鎷掕琛屽苟鍥炴樉鍙屾柟瀹為檯鍊硷級", "upstream": ["鍒拌揣閫氱煡", "澶栧崗鍏ュ簱", "Excel 鎵归噺瀵煎叆"], "downstream": ["閲囪喘鍏ュ簱", "渚涘簲鍟嗛��璐�", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IQC 鏉ユ枡妫�楠� 鍑烘姤鍛� 鎵归噺瀵煎叆 瀵煎叆妯℃澘"},
+    {"name": "IPQC杩囩▼妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_ipqc", "doc": "鎶ュ伐璐ㄦ宸ュ簭浜у嚭琛岃Е鍙戯紝瀹屾垚鍥炶皟鎺ㄨ繘鎶ュ伐涓庝骇鍑哄叆搴擄紱琛ㄥ崟鍙暀銆屾潵婧愬崟鎹� + 浜у搧鐗╂枡(鍓嶇鎵嬮�� MDM 鐗╂枡) + 鎵瑰彿(蹇呭~) + 鏁伴噺 + 妫�楠屼俊鎭�嶏紝宸插幓鐢熶骇璁㈠崟/鐢熶骇浠诲姟/宸ヤ綅/宸ュ簭锛屾壒鍙烽殢鎶ュ憡钀芥锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙侊紱鏀寔 Excel 鎵归噺瀵煎叆锛堟ā鏉挎寜銆屼骇鍝佺墿鏂欑紪鐮� + 鎵瑰彿(蹇呭~) + 妫�楠岀被鍨嬨�嶅~鍒楋紝涓嶅~宸ュ崟/宸ヤ綅锛涘伐搴�+鏂欏簾+鍏朵粬搴熷搧涓嶅緱瓒呰繃涓嶅悎鏍煎搧鏁伴噺锛屼笁鍒楃暀绌烘寜 0锛夛紱鍚屼竴鎵规暟鎹笉鍙噸澶嶅鍏ワ紙鐗╂枡+鎵瑰彿+妫�楠岀被鍨�+妫�娴嬫棩鏈燂紝鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡鏈夊崟閮芥嫆璇ヨ锛�", "upstream": ["鎶ュ伐", "Excel 鎵归噺瀵煎叆"], "downstream": ["浜у嚭鍏ュ簱", "NCR", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "IPQC 杩囩▼妫�楠� 宸℃ 鍑烘姤鍛� 鎵瑰彿 batchCode MDM鐗╂枡 鎵归噺瀵煎叆 瀵煎叆妯℃澘"},
+    {"name": "OQC鍑鸿揣妫�楠�", "module": "璐ㄩ噺", "table": "mes_qc_oqc", "doc": "閿�鍞嚭搴撴彁浜よЕ鍙戯紝瀹屾垚鎺ㄨ繘鍑哄簱鍗曞緟鎷h揣锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙侊紱鏀寔 Excel 鎵归噺瀵煎叆锛堝鎴峰悕绉�/鏈鍑鸿揣鏁伴噺/鍑鸿揣鏃ユ湡鍧囧彲鐣欑┖锛岀暀绌哄嵆璇ュ崟涓嶅睘浠讳綍瀹㈡埛銆佷笉鍙楀鎴锋暟鎹潈闄愰伄鎸★紱濉鎴锋椂椤诲湪鏈汉瀹㈡埛鏁版嵁鏉冮檺鍐咃級锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏ワ紙鐗╂枡+瀹㈡埛+鎵规鍙�+鍑鸿揣鏃ユ湡锛屾枃浠跺唴閲嶅琛屼笌搴撳唴宸叉湁鍗曢兘鎷掕琛岋級", "upstream": ["閿�鍞嚭搴撳崟", "Excel 鎵归噺瀵煎叆"], "downstream": ["閿�鍞嚭搴撳崟(鎺ㄨ繘)", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "OQC 鍑鸿揣妫�楠� 鍑哄巶妫�楠� 鍑烘姤鍛� 鎵归噺瀵煎叆 瀵煎叆妯℃澘"},
     {"name": "RQC閫�璐ф楠�", "module": "璐ㄩ噺", "table": "mes_qc_rqc", "doc": "鐢熶骇閫�鏂�/閿�鍞��璐�/鍞悗閫�璐цЕ鍙戯紝缁撹鍥炶皟鍞悗锛涜鎿嶄綔銆屽嚭鎶ュ憡銆嶄粎鍦ㄥ凡瀹屾垚鏃跺彲瑙併�傛帾杈炲凡缁熶竴涓恒�岄��璐ф楠屻�嶏紙鍘熻彍鍗曠埗椤逛笌 mes_qc_type 瀛楀吀鍐欍�岄��鏂欐楠屻�嶏紝涓庡悗绔灇涓�/琛ㄦ敞閲�/鑿滃崟鏉冮檺瀛愰」涓嶄竴鑷达紱RQC 涓夎�呴兘瑕嗙洊锛屾晠鍙栥�岄��璐ф楠屻�嶏級", "upstream": ["閫�鏂�", "閫�璐�"], "downstream": ["閫�璐у叆搴�", "鍞悗鍥炶皟", "鎶ュ憡瀹炰緥(鍑烘姤鍛�)"], "keywords": "RQC 閫�璐ф楠� 閫�鏂欐楠� 鍑烘姤鍛�"},
     {"name": "NCR涓嶅悎鏍煎搧", "module": "璐ㄩ噺", "table": "mes_qc_ncr", "doc": "寰呰瘎瀹�0鈫掑凡璇勫6鈫掑凡澶勭疆7鈫掑凡鍏抽棴8锛屽缃�:閫�璐�/鎶ュ簾/杩斿伐/璁╂鎺ユ敹/闄嶇骇", "upstream": ["IQC/IPQC/OQC/RQC"], "downstream": ["閫�璐�/鎶ュ簾/杩斿伐(鍙拌处)"], "keywords": "NCR 涓嶅悎鏍� 澶勭疆 鐗归噰 鎶ュ簾"},
     {"name": "璁惧", "module": "璁惧", "table": "mes_dv_machinery", "doc": "鍙拌处鐘舵��:鍋滄満/鐢熶骇涓�/淇濆吇涓紱鐐规路淇濆吇路缁翠慨宸ュ崟锛汚I 椋庨櫓棰勬祴娑堣垂鍏舵暟鎹�", "upstream": [], "downstream": ["鐐规", "缁翠慨", "鐢熶骇椋庨櫓棰勬祴"], "keywords": "璁惧 鐐规 淇濆吇 缁翠慨 machinery"},
@@ -524,12 +524,14 @@
        {"stage": "鑱斿姩", "action": "瀹℃壒閫氳繃鎸夊伐搴忔爣璁�: 鍊掑啿鈫掕嚜鍔ㄧ墿鏂欐秷鑰楁墸绾胯竟搴�(鍏佽璐熷簱瀛�)锛涜川妫�鈫掍骇鍑哄緟妫�琛岀瓑 IPQC锛涘叧閿潪妫�鈫掔洿鎺ヤ骇鍑哄叆搴�", "roles": ["绯荤粺鑷姩"]},
        {"stage": "瀹屽伐", "action": "浠呮湯閬撳伐搴忓洖鍐欏伐鍗曞凡鐢熶骇鏁伴噺锛岃揪鏍囪嚜鍔ㄥ畬鎴愶紱MPS 绱鍒ゆ柇瀹屾垚骞跺彂浜嬩欢缁欓攢鍞鍗�", "roles": ["绯荤粺鑷姩"]}]},
     {"id": "quality", "name": "璐ㄩ噺娴佺▼", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c",
-     "summary": "鍥涢亾妫�楠岄椄鍙�(IQC/IPQC/OQC/RQC锛屼簩鎬� 0鈫�4) + 鍒ゅ畾鍥涙灉(鍚堟牸/鐗归噰/閫�璐�/鎶ュ簾) + NCR 璇勫澶勭疆鍏抽棴锛汚I 鎻愪緵鍒ゅ畾寤鸿锛汵CR 褰撳墠浠呭彴璐︿笉瑙﹀彂杩斿伐鍗曪紱妫�楠屽崟鍙敱鏅鸿兘璐ㄦ鎶ュ憡骞冲彴涓�閿嚭浠讹紙generate-from-qc锛氭暟鎹粠 MES 鍙栵紝浜旈亾鏍¢獙鍚庢覆鏌撹惤搴擄級銆�",
+     "summary": "鍥涢亾妫�楠岄椄鍙�(IQC/IPQC/OQC/RQC锛屼簩鎬� 0鈫�4) + 鍒ゅ畾鍥涙灉(鍚堟牸/鐗归噰/閫�璐�/鎶ュ簾) + NCR 璇勫澶勭疆鍏抽棴锛汭QC/IPQC/OQC 鏀寔 Excel 鎵归噺瀵煎叆(绾柊澧炪�侀�愯閮ㄥ垎鎴愬姛銆佹寜涓氬姟閿幓閲�)锛屽苟鎻愪緵璺ㄥ洓绫荤殑妫�娴嬬粨鏋滃彴璐︽煡璇笌瀵煎嚭锛汚I 鎻愪緵鍒ゅ畾寤鸿锛汵CR 褰撳墠浠呭彴璐︿笉瑙﹀彂杩斿伐鍗曪紱妫�楠屽崟鍙敱鏅鸿兘璐ㄦ鎶ュ憡骞冲彴涓�閿嚭浠讹紙generate-from-qc锛氭暟鎹粠 MES 鍙栵紝浜旈亾鏍¢獙鍚庢覆鏌撹惤搴擄級銆�",
      "steps": [
        {"stage": "鏉ユ枡", "action": "鍒拌揣閫氱煡/澶栧崗鍏ュ簱鈫扞QC锛岀粨璁哄洖鍐欐帹杩涘緟鍏ュ簱鎴栬Е鍙戦��璐�", "roles": ["IQC 璐ㄦ鍛�"]},
        {"stage": "杩囩▼", "action": "鎶ュ伐璐ㄦ宸ュ簭浜у嚭琛屸啋IPQC锛屽悎鏍兼媶琛屽叆搴撱�佷笉鍚堟牸鐢熸垚 NCR", "roles": ["IPQC 璐ㄦ鍛�"]},
        {"stage": "鍑鸿揣", "action": "閿�鍞嚭搴撴彁浜も啋OQC锛屽畬鎴愬悗鍑哄簱鍗曡繘鍏ュ緟鎷h揣", "roles": ["OQC 璐ㄦ鍛�"]},
        {"stage": "閫�璐�", "action": "閫�鏂�/閿�鍞��璐�/鍞悗閫�璐р啋RQC锛岀粨璁哄洖璋冨崟鎹笌鍞悗", "roles": ["RQC 璐ㄦ鍛�"]},
+       {"stage": "瀵煎叆", "action": "IQC/IPQC/OQC 涓夊垪琛ㄥ悇鏀寔 Excel 鎵归噺瀵煎叆(鎸夐挳鍦ㄣ�屾柊澧炪�嶄笌銆屽鍑恒�嶄箣闂达紝绾柊澧炰笉鍋氭洿鏂拌鐩�)锛氫竴琛屼竴寮犲崟锛岀紪鍙风暀绌鸿嚜鍔ㄧ敓鎴愶紝璐ㄦ鏂规鐣欑┖鎸夌墿鏂�+妫�楠岀被鍨嬭嚜鍔ㄥ尮閰嶏紝妫�娴嬩汉鍛樼暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涚墿鏂�/渚涘簲鍟�/瀹㈡埛鎸夌紪鐮佹垨鍚嶇О绮剧‘鍖归厤(IQC 渚涘簲鍟嗕笌渚涘簲鍟嗘壒娆″彲鐣欑┖銆丱QC 瀹㈡埛鍙暀绌轰笖鐣欑┖涓嶅彈瀹㈡埛鏁版嵁鏉冮檺閬尅)锛屽悎鏍�+涓嶅悎鏍�=妫�娴�/鎺ユ敹鏁伴噺銆両PQC 宸ュ簾+鏂欏簾+鍏朵粬搴熷搧鈮や笉鍚堟牸(涓夊垪鐣欑┖鎸�0)锛孫QC 濉鎴锋椂椤诲湪鏈汉瀹㈡埛鏁版嵁鏉冮檺鍐咃紱閫愯瀵煎叆閮ㄥ垎鎴愬姛锛屽け璐ヨ杩� failureList 甯﹁鍙蜂笌鍙墽琛屽姩浣滐紝鎴愬姛鍗曚竴寰嬪緟妫�楠�0锛涘悓涓�鎵规暟鎹笉鍙噸澶嶅鍏ワ細鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡瀛樺湪鍚屼笟鍔¢敭鍗曟嵁(IQC 鐗╂枡+渚涘簲鍟�+渚涘簲鍟嗘壒娆�+鏉ユ枡鏃ユ湡銆両PQC 鐗╂枡+鎵瑰彿+妫�楠岀被鍨�+妫�娴嬫棩鏈熴�丱QC 鐗╂枡+瀹㈡埛+鎵规鍙�+鍑鸿揣鏃ユ湡)鍧囨嫆璇ヨ锛屾彁绀哄惈缁村害銆佸弻鏂瑰疄闄呭�间笌銆屽厛鍒犻櫎璇ュ崟鎹啀閲嶈瘯銆嶅姩浣�", "roles": ["璐ㄦ鍛�", "IQC 璐ㄦ鍛�", "IPQC 璐ㄦ鍛�", "OQC 璐ㄦ鍛�"]},
+       {"stage": "鍙拌处", "action": "妫�娴嬬粨鏋滃彴璐︼細璺� IQC/IPQC/OQC/RQC 鍥涚被妫�楠屽崟缁熶竴鏌ヨ(鍒嗙被+妫�娴嬫棩鏈熷尯闂�+妫�楠屽崟鍙�+鐗╂枡+鎵规鍙�+妫�娴嬬粨鏋�+妫�娴嬩汉)锛屼竴琛屼竴涓娴嬮」缁撴灉锛屽垽瀹氫緷鎹敱鏍囧噯鍊间笌涓婁笅闄愭嫾瑁咃紱鎵规鍙锋寜绫诲瀷鍙栨潵婧�(IQC 鍙栦緵搴斿晢鎵规鍙凤紝IPQC/OQC/RQC 鍙栨壒鍙�)锛涘悓濂楃瓫閫夋潯浠剁洿鎺ュ鍑� Excel 鍙拌处鐢ㄤ簬鎵撳嵃瀛樻。锛涙湭褰曟娴嬮」鐨勫崟浠嶄繚鐣欎竴琛屼笉婕忓崟", "roles": ["璐ㄦ鍛�", "璐ㄩ噺宸ョ▼甯�"]},
        {"stage": "涓嶅悎鏍�", "action": "NCR: 寰呰瘎瀹♀啋璇勫鈫掑缃�(閫�璐�/鎶ュ簾/杩斿伐/璁╂鎺ユ敹/闄嶇骇)鈫掑叧闂�", "roles": ["璐ㄩ噺宸ョ▼甯�"]},
        {"stage": "鍑轰欢", "action": "涓ゆ潯鍑轰欢鍏ュ彛锛氣憼閫氱敤 generate/preview(璋冪敤鏂硅嚜甯︿笂涓嬫枃) 娓叉煋 HTML 骞跺喕缁撴暟鎹揩鐓э紝缂栧彿 QR+鏃ユ湡+4浣嶆祦姘存寜澶╃嫭绔嬶紝preview 鍙覆鏌撲笉钀藉簱涓嶆秷鑰楃紪鍙凤紝regenerate 鐢ㄥ喕缁撳揩鐓ч噸娓蹭笖鏁版嵁涓庣紪鍙蜂笉鍙樺苟浣滃簾宸插綊妗� PDF锛屽鍑� PDF 鐢卞悗绔皟 Chromium 鎵撳嵃瀹炰緥宸插瓨鐨� HTML 鍚庡綊妗i檮浠朵腑蹇冿紱鈶enerate-from-qc 鐢辫川妫�鍗曠洿鎺ュ嚭浠�(鏁版嵁缁� MesQcReportApi 浠� MES 鍙栵紝鍓嶇涓嶆嫾涓婁笅鏂�)锛屽嚭浠跺墠浜旈亾鏍¢獙鎸変笟鍔′紭鍏堢骇鍙姤绗竴澶勶細绫诲瀷闈炴硶/鍗曟嵁涓嶅瓨鍦�/鏈畬鎴愭楠�/鏈~鍒ゅ畾/妯℃澘绫诲瀷涓嶇/鏃犲彲鐢ㄦ楠岄」锛屾姤鍛婂ご鍙﹀甫鍗曟嵁鍘熷垽瀹� qcResult+qcResultText 涓庡紩鎿庡垽瀹氬苟瀛橈紝鍝嶅簲鍥� undecidableCount 寰呭垽瀹氶」鏁颁笌 warnings(鍒嗙粍鏍囬璺宠繃/鎸囨爣宸插垹/鏈綍瀹炴祴鍊�/鏍峰搧瓒�3)锛屽悓涓�寮犲崟鎹彲鍙嶅鍑哄浠�", "roles": ["璐ㄦ鍛�"]},
        {"stage": "寤烘ā鏉�", "action": "AI 瀵煎叆锛氫笂浼犳棦鏈夋楠屾姤鍛婃枃浠�(鎵弿浠�/鐓х墖銆佺數瀛愮増 PDF銆乄ord/Excel) 鈫� 璇嗗埆鎴愭ā鏉胯崏绋�(涓嶈惤搴�) 鈫� 浜哄伐鍦ㄨ璁″櫒纭璋冩暣鍚庝繚瀛樹负鐗堟湰锛�.doc/.xls 鏃ф牸寮忎笉鏀寔", "roles": ["璐ㄦ鍛�", "璐ㄩ噺宸ョ▼甯�"]}]},
@@ -754,6 +756,25 @@
   T2[鎶ュ伐璐ㄦ宸ュ簭閫氳繃鍚庝骇鍑鸿] --> A2[IPQC 杩囩▼妫�楠屄疯崏绋�0鈫掑畬鎴�4]
   T3[閿�鍞嚭搴撳崟鎻愪氦] --> A3[OQC 鍑鸿揣妫�楠屄疯崏绋�0鈫掑畬鎴�4]
   T4[鐢熶骇閫�鏂欏崟 鎴� 閿�鍞��璐у崟 鎴� 鍞悗閫�璐 --> A4[RQC 閫�璐ф楠屄疯崏绋�0鈫掑畬鎴�4]
+  T5[Excel 鎵归噺瀵煎叆路IQC IPQC OQC 涓夊垪琛ㄥ悇涓�涓叆鍙B锋寜閽湪鏂板涓庡鍑轰箣闂碷 --> IMP{瀵煎叆闂搁棬路閫愯瑙f瀽路澶辫触琛屼簰涓嶅奖鍝峿
+  IMP -->|缂栧彿鐣欑┖鑷姩鐢熸垚路缂栧彿閲嶅鎴栧悓涓�鎵规暟鎹噸澶嶅垯鎷掕琛寍 A1
+  IMP --> A2
+  IMP --> A3
+  IMP -.->|鍚屼竴鎵规暟鎹笉鍙噸澶嶅鍏ヂ稩QC 鐗╂枡+渚涘簲鍟�+渚涘簲鍟嗘壒娆�+鏉ユ枡鏃ユ湡 / IPQC 鐗╂枡+鎵瑰彿+妫�楠岀被鍨�+妫�娴嬫棩鏈� / OQC 鐗╂枡+瀹㈡埛+鎵规鍙�+鍑鸿揣鏃ユ湡路鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡鏈夊崟閮芥嫆骞跺洖鏄惧弻鏂瑰疄闄呭�紎 IMPDUP[瀵煎叆鍘婚噸路鎸変笟鍔¢敭]
+  IMP -.->|鐗╂枡鎸夌紪鐮� 渚涘簲鍟嗕笌瀹㈡埛鎸夊悕绉� 绮剧‘鍖归厤路鏂规鐣欑┖鎸夌墿鏂�+妫�楠岀被鍨嬭嚜鍔ㄥ尮閰峾 IMPR[寮曠敤瑙f瀽路QC_NO_TEMPLATE 琛ョ墿鏂欎笌鍙墽琛屽姩浣淽
+  IMP -.->|鍚堟牸+涓嶅悎鏍� 蹇呴』绛変簬 妫�娴嬫垨鎺ユ敹鏁伴噺路涓嶇瓑绛夊垯鎷掕琛寍 IMPQ[鏁伴噺鍏崇郴鏍¢獙路涓や晶瀹為檯鍊奸兘鍥炴樉]
+  IMP -.->|IPQC 宸ュ簾+鏂欏簾+鍏朵粬搴熷搧 涓嶅緱瓒呰繃涓嶅悎鏍煎搧鏁伴噺路涓夊垪鐣欑┖鎸�0| IMPQ
+  IMP -.->|OQC 濉鎴锋椂鏍¢獙鍦ㄦ湰浜烘暟鎹潈闄愬唴路鐣欑┖鍒欎笉灞炰簬浠讳綍瀹㈡埛路涓嶅彈閬尅| IMPP[鏁版嵁鏉冮檺鍓嶇疆鏍¢獙]
+  IMP -.->|妫�娴嬩汉鍛樼暀绌�=褰撳墠鐧诲綍鐢ㄦ埛路IQC 妫�娴嬩笌鏉ユ枡鏃ユ湡銆丱QC 妫�娴嬫棩鏈熷繀濉稩PQC 妫�娴嬫棩鏈熶笌 OQC 鍑鸿揣鏃ユ湡鍙暀绌簗 IMPD[浜哄憳涓庢棩鏈焆
+  IMP --> IMR[鍝嶅簲 createList 鎴愬姛缂栧彿 + failureList 琛屽彿鈫掑師鍥犅锋暣琛岀┖鑷姩璺宠繃]
+  IMR -.->|瀵煎叆鍗曚竴寰嬪緟妫�楠�0路涓嶈蛋鏉ユ簮鍗曟嵁鍥炲啓路涓嶇敓鎴怤CR| IMRNOTE[涓庤Е鍙戝紡寤哄崟鐨勫尯鍒穎inish 鎵嶅垽瀹氫笌鍥炲啓]
+  A1 -->|鍙拌处 UNION ALL| LG[妫�娴嬬粨鏋滃彴璐β疯法 IQC IPQC OQC RQC 缁熶竴鏌ヨ涓庡鍑篯
+  A2 -->|鍙拌处 UNION ALL| LG
+  A3 -->|鍙拌处 UNION ALL| LG
+  A4 -->|鍙拌处 UNION ALL| LG
+  LG -->|涓�琛屼竴涓娴嬮」路鏈綍缁撴灉涔熶繚鐣欎竴琛屼笉婕忓崟| LGF[鎵规鍙锋寜绫诲瀷鍙栨潵婧惵稩QC 渚涘簲鍟嗘壒娆″彿 / IPQC OQC RQC 鎵瑰彿]
+  LG -->|鍒ゅ畾渚濇嵁鐢辨爣鍑嗗�间笌涓婁笅闄愭嫾瑁厊 LGB[鍚屼竴濂楃瓫閫夋潯浠跺鍑� Excel 鍙拌处路鍙椤典笉鎻愪緵澧炲垹鏀筣
+  LG -->|鏉冮檺鐮亅 LGPERM["mes:qc-inspection-ledger:query 鏌ヨ路:export 瀵煎嚭路鑿滃崟鎸傝川閲忕鐞嗙洰褰�"]
   A1 --> R{妫�楠屽垽瀹� MesQcCheckResultEnum}
   A2 --> R
   A3 --> R
@@ -808,7 +829,7 @@
   RCTX --> RCITEM[妫�楠岄」=鏍峰搧脳鎸囨爣浜ゅ弶灞曞紑路鍏ㄦ寚鏍囪繘涓嶈繃婊ぢ峰鏍峰搧鏃舵牱鍝佸彿鎶樿繘 remark]
   RCTX --> RCQC[鎼哄甫鍗曟嵁鍘熷垽瀹� qcResult 鏁奥穛cResultText 涓枃路涓庡紩鎿庡垽瀹氬苟瀛樹簰涓嶈鐩朷
   MESMAP -->|鍒嗙粍椤� item_type=2 涓庡畠鐨勫瓙椤规寜 parent_id 杩樺師涓ゅ眰路褰掑睘涓嶇湅 type 涔熶笉鐪嬭搴弢 RCTXG
-  RCTXG["鍒嗙粍杩樺師路缁勫ご琛岃儗缁勫悕涓� formula_text 鍏紡路缁勫唴绗竴琛岃儗 span=璇ョ粍鎬昏鏁奥峰悓缁勫叾浣欒 span=1 涓� hidden 璁╀綅路鍙悎骞舵楠岄」鐩竴鍒�"] --- RCTX
+  RCTXG["鍒嗙粍杩樺師路缁勫ご琛岃儗缁勫悕涓� formula_text 鍏紡路缁勫唴绗竴琛岃儗 span=璇ョ粍鎬昏鏁奥峰悓缁勫叾浣欒 span=1 涓� hidden 璁╀綅路鍚堝苟鐨勬槸妯℃澘 columns 閲屽甫 # 鐨勯偅涓�鍒楋紙榛樿鍒楀嵆妫�楠岄」鐩級路鍚堝苟琛屾暟鏄湰鍗曡缁勫疄闄呰鏁拌�岄潪涓绘暟鎹瓙椤规暟路璇ョ粍鏈崟鏃犲瓙椤硅鍒欎笉鍚堝苟"] --- RCTX
   MESMAP -.->|鍒嗙粍椤规湰鍗曟病鏈夊睘浜庡畠鐨勫瓙椤� / 缁勫ご鏈崟娌℃湁琛岀敱鍚庣琛ヤ竴琛寍 RCWARN
   MESMAP -.->|鎸囨爣宸插垹 / 鏈綍瀹炴祴鍊硷紙缁勫ご閿氱偣琛屾寜瀹氫箟鏃犲�悸蜂笉鎶ワ級 / 鏍峰搧瓒� 3 涓疯繘 warnings| RCWARN
   RCWARN[warnings 杞彁绀郝蜂笉钀藉簱路浜嬪悗鐪嬩笉鍒伴渶鑷鐣欑棔] --- RCTX
diff --git a/docs/project-business/mermaid/05-quality-flow.mmd b/docs/project-business/mermaid/05-quality-flow.mmd
index ea108dc..2aa8329 100644
--- a/docs/project-business/mermaid/05-quality-flow.mmd
+++ b/docs/project-business/mermaid/05-quality-flow.mmd
@@ -3,6 +3,25 @@
   T2[鎶ュ伐璐ㄦ宸ュ簭閫氳繃鍚庝骇鍑鸿] --> A2[IPQC 杩囩▼妫�楠屄疯崏绋�0鈫掑畬鎴�4]
   T3[閿�鍞嚭搴撳崟鎻愪氦] --> A3[OQC 鍑鸿揣妫�楠屄疯崏绋�0鈫掑畬鎴�4]
   T4[鐢熶骇閫�鏂欏崟 鎴� 閿�鍞��璐у崟 鎴� 鍞悗閫�璐 --> A4[RQC 閫�璐ф楠屄疯崏绋�0鈫掑畬鎴�4]
+  T5[Excel 鎵归噺瀵煎叆路IQC IPQC OQC 涓夊垪琛ㄥ悇涓�涓叆鍙B锋寜閽湪鏂板涓庡鍑轰箣闂碷 --> IMP{瀵煎叆闂搁棬路閫愯瑙f瀽路澶辫触琛屼簰涓嶅奖鍝峿
+  IMP -->|缂栧彿鐣欑┖鑷姩鐢熸垚路缂栧彿閲嶅鎴栧悓涓�鎵规暟鎹噸澶嶅垯鎷掕琛寍 A1
+  IMP --> A2
+  IMP --> A3
+  IMP -.->|鍚屼竴鎵规暟鎹笉鍙噸澶嶅鍏ヂ稩QC 鐗╂枡+渚涘簲鍟�+渚涘簲鍟嗘壒娆�+鏉ユ枡鏃ユ湡 / IPQC 鐗╂枡+鎵瑰彿+妫�楠岀被鍨�+妫�娴嬫棩鏈� / OQC 鐗╂枡+瀹㈡埛+鎵规鍙�+鍑鸿揣鏃ユ湡路鏂囦欢鍐呴噸澶嶈涓庡簱鍐呭凡鏈夊崟閮芥嫆骞跺洖鏄惧弻鏂瑰疄闄呭�紎 IMPDUP[瀵煎叆鍘婚噸路鎸変笟鍔¢敭]
+  IMP -.->|鐗╂枡鎸夌紪鐮� 渚涘簲鍟嗕笌瀹㈡埛鎸夊悕绉� 绮剧‘鍖归厤路鏂规鐣欑┖鎸夌墿鏂�+妫�楠岀被鍨嬭嚜鍔ㄥ尮閰峾 IMPR[寮曠敤瑙f瀽路QC_NO_TEMPLATE 琛ョ墿鏂欎笌鍙墽琛屽姩浣淽
+  IMP -.->|鍚堟牸+涓嶅悎鏍� 蹇呴』绛変簬 妫�娴嬫垨鎺ユ敹鏁伴噺路涓嶇瓑绛夊垯鎷掕琛寍 IMPQ[鏁伴噺鍏崇郴鏍¢獙路涓や晶瀹為檯鍊奸兘鍥炴樉]
+  IMP -.->|IPQC 宸ュ簾+鏂欏簾+鍏朵粬搴熷搧 涓嶅緱瓒呰繃涓嶅悎鏍煎搧鏁伴噺路涓夊垪鐣欑┖鎸�0| IMPQ
+  IMP -.->|OQC 濉鎴锋椂鏍¢獙鍦ㄦ湰浜烘暟鎹潈闄愬唴路鐣欑┖鍒欎笉灞炰簬浠讳綍瀹㈡埛路涓嶅彈閬尅| IMPP[鏁版嵁鏉冮檺鍓嶇疆鏍¢獙]
+  IMP -.->|妫�娴嬩汉鍛樼暀绌�=褰撳墠鐧诲綍鐢ㄦ埛路IQC 妫�娴嬩笌鏉ユ枡鏃ユ湡銆丱QC 妫�娴嬫棩鏈熷繀濉稩PQC 妫�娴嬫棩鏈熶笌 OQC 鍑鸿揣鏃ユ湡鍙暀绌簗 IMPD[浜哄憳涓庢棩鏈焆
+  IMP --> IMR[鍝嶅簲 createList 鎴愬姛缂栧彿 + failureList 琛屽彿鈫掑師鍥犅锋暣琛岀┖鑷姩璺宠繃]
+  IMR -.->|瀵煎叆鍗曚竴寰嬪緟妫�楠�0路涓嶈蛋鏉ユ簮鍗曟嵁鍥炲啓路涓嶇敓鎴怤CR| IMRNOTE[涓庤Е鍙戝紡寤哄崟鐨勫尯鍒穎inish 鎵嶅垽瀹氫笌鍥炲啓]
+  A1 -->|鍙拌处 UNION ALL| LG[妫�娴嬬粨鏋滃彴璐β疯法 IQC IPQC OQC RQC 缁熶竴鏌ヨ涓庡鍑篯
+  A2 -->|鍙拌处 UNION ALL| LG
+  A3 -->|鍙拌处 UNION ALL| LG
+  A4 -->|鍙拌处 UNION ALL| LG
+  LG -->|涓�琛屼竴涓娴嬮」路鏈綍缁撴灉涔熶繚鐣欎竴琛屼笉婕忓崟| LGF[鎵规鍙锋寜绫诲瀷鍙栨潵婧惵稩QC 渚涘簲鍟嗘壒娆″彿 / IPQC OQC RQC 鎵瑰彿]
+  LG -->|鍒ゅ畾渚濇嵁鐢辨爣鍑嗗�间笌涓婁笅闄愭嫾瑁厊 LGB[鍚屼竴濂楃瓫閫夋潯浠跺鍑� Excel 鍙拌处路鍙椤典笉鎻愪緵澧炲垹鏀筣
+  LG -->|鏉冮檺鐮亅 LGPERM["mes:qc-inspection-ledger:query 鏌ヨ路:export 瀵煎嚭路鑿滃崟鎸傝川閲忕鐞嗙洰褰�"]
   A1 --> R{妫�楠屽垽瀹� MesQcCheckResultEnum}
   A2 --> R
   A3 --> R
diff --git a/docs/qc_inspection_ledger_integration.md b/docs/qc_inspection_ledger_integration.md
new file mode 100644
index 0000000..a4ee7c4
--- /dev/null
+++ b/docs/qc_inspection_ledger_integration.md
@@ -0,0 +1,162 @@
+# 璐ㄩ噺绠$悊 - 妫�娴嬬粨鏋滃彴璐︼紙鍒嗙被 / 鏃堕棿鍖洪棿绛涢�� + 瀵煎嚭锛� - 鍓嶇鑱旇皟鏂规
+
+> 鏈瑙e喅涓や欢浜嬶細
+> 1. **鐩存帴灞曠ず**锛氬洓绫绘楠屽崟锛圛QC/IPQC/OQC/RQC锛夊垪琛ㄦ帴鍙�**宸茬粡杩斿洖**妫�楠岄」瀹炴祴缁撴灉姹囨�诲瓧娈� `indicatorResultSummary`锛屼絾鍓嶇鍒楄〃鏈睍绀� 鈫� 鍓嶇鍔犱竴鍒楀嵆鍙紝**鍚庣鏃犻渶鏀瑰姩**銆�
+> 2. **绛涢�夊睍绀� + 瀵煎嚭鍙拌处**锛氭柊澧炶法鍥涚被鐨勩�屾娴嬬粨鏋滃彴璐︺�嶆煡璇笌瀵煎嚭鎺ュ彛锛堝垎绫� + 妫�娴嬫棩鏈熷尯闂� + 鐗╂枡/鎵规/妫�娴嬬粨鏋�/妫�娴嬩汉绛涢�夛級锛屼竴琛屼竴涓娴嬮」缁撴灉锛岀敤浜庢煡璇€�佹墦鍗般�佸瓨妗c��
+
+## 娑夊強椤甸潰
+
+- **鏂板**锛氭娴嬬粨鏋滃彴璐﹂〉 `views/mes/qc/ledger/index.vue`锛堣彍鍗曠敱 `docs/sql/qc_inspection_ledger_menu.sql` 鑴氭湰鎸夋潈闄愮爜鑷�傚簲鍒涘缓锛氳川閲忕鐞� 鈫� 妫�娴嬬粨鏋滃彴璐︼紝path `inspection-ledger`锛宑omponent `mes/qc/ledger/index`锛�
+- **鍙樻洿锛堢敤浜庛�岀洿鎺ュ睍绀恒�嶏級**锛氭潵鏂欐楠� / 杩囩▼妫�楠� / 鍑鸿揣妫�楠� / 閫�璐ф楠� 鍥涗釜鍒楄〃椤垫柊澧炪�屾娴嬬粨鏋滐紙妫�楠岄」锛夈�嶅垪
+
+## 闇�姹傜偣涓庤惤鐐瑰鐓�
+
+| 闇�姹� | 钀界偣 |
+|------|------|
+| 妫�娴嬬粨鏋滃湪鐣岄潰涓婄洿鎺ュ睍绀� | 鍥涚被妫�楠屽崟鍒楄〃 API 宸茶繑鍥� `indicatorResultSummary`锛屽墠绔垪琛ㄥ姞鍒楋紙鏃犻渶鍚庣鏀瑰姩锛� |
+| 鏀寔绛涢�夊睍绀� | 鏂板鍙拌处椤碉細鍒嗙被 + 妫�娴嬫棩鏈熷尯闂� + 妫�楠屽崟鍙� + 鐗╂枡 + 鎵规鍙� + 妫�娴嬬粨鏋� + 妫�娴嬩汉 |
+| 鎸夊垎绫荤瓫閫� | 鍙傛暟 `qcType`锛�1 鏉ユ枡妫�楠� / 2 杩囩▼妫�楠� / 3 鍑鸿揣妫�楠� / 4 閫�璐ф楠岋級锛屼笉浼犱负鍏ㄩ儴 |
+| 鎸夋椂闂村尯闂寸瓫閫� | 鍙傛暟 `inspectDate`锛堟寜銆屾娴嬫棩鏈熴�嶈繃婊わ級 |
+| 瀵煎嚭涓哄彴璐� | `GET /mes/qc/inspection-ledger/export-excel`锛屼笌鍒楄〃鍚屼竴濂楃瓫閫夊弬鏁� |
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 鐢ㄦ埛鍦ㄥ彴璐﹂〉璁剧疆绛涢�夋潯浠讹紙鍒嗙被銆佹娴嬫棩鏈熷尯闂寸瓑锛夆啋 鍒楄〃鍒嗛〉灞曠ず锛屼竴琛屼竴涓娴嬮」缁撴灉銆�
+2. 鍚屼竴濂楃瓫閫夊弬鏁扮洿鎺ヤ紶缁欏鍑烘帴鍙� 鈫� 鐢熸垚 Excel 鍙拌处锛堟枃浠跺悕銆屾娴嬬粨鏋滃彴璐�.xls銆嶏級锛岀敤浜庢墦鍗颁笌瀛樻。銆�
+3. 妫�楠屽崟鏈綍鍏ユ楠岀粨鏋滄椂锛岃鍗曚粛浼氬嚭鐜板湪鍙拌处涓紙妫�娴嬮」鐩稿叧鍒椾负绌猴級锛屼笉浼氭紡鍗曘��
+4. 鍙拌处鐨勩�屾壒娆″彿銆嶅垪鎸夋楠岀被鍨嬪彇涓嶅悓鏉ユ簮锛氭潵鏂欐楠屽彇**渚涘簲鍟嗘壒娆″彿**锛涜繃绋嬫楠屻�佸嚭璐ф楠屻�侀��璐ф楠屽彇鍚勮嚜鐨�**鎵规鍙�**銆�
+
+## API
+
+### 1. 鍙拌处鍒嗛〉
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/admin-api/mes/qc/inspection-ledger/page` | 妫�娴嬬粨鏋滃彴璐﹀垎椤碉紙鍥涚被妫�楠屽崟缁熶竴鏌ヨ锛� |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| qcType | Integer | 鍚� | 鍒嗙被锛堟楠岀被鍨嬶級锛�1 鏉ユ枡妫�楠� / 2 杩囩▼妫�楠� / 3 鍑鸿揣妫�楠� / 4 閫�璐ф楠岋紱涓嶄紶涓哄叏閮� |
+| inspectDate[0] | String | 鍚� | 妫�娴嬫棩鏈熷紑濮嬶紝鏍煎紡 yyyy-MM-dd HH:mm:ss |
+| inspectDate[1] | String | 鍚� | 妫�娴嬫棩鏈熺粨鏉燂紝鏍煎紡 yyyy-MM-dd HH:mm:ss |
+| code | String | 鍚� | 妫�楠屽崟缂栧彿锛屾ā绯婂尮閰� |
+| itemId | Long | 鍚� | 鐗╂枡缂栧彿 |
+| batchNo | String | 鍚� | 鎵规鍙凤紝妯$硦鍖归厤锛堟潵鏂欐楠屽彇渚涘簲鍟嗘壒娆″彿锛涜繃绋�/鍑鸿揣/閫�璐ф楠屽彇鎵规鍙凤級 |
+| checkResult | Integer | 鍚� | 妫�娴嬬粨鏋滐紝瀛楀吀 `mes_qc_check_result` |
+| inspectorUserId | Long | 鍚� | 妫�娴嬩汉鍛樼紪鍙� |
+| pageNo / pageSize | Integer | 鍚� | 鍒嗛〉鍙傛暟 |
+
+> `inspectDate` 涓庣幇鏈夊洓绫绘楠屽崟鍒楄〃鐨勪紶鍙傛柟寮忎竴鑷达紙鍚屼竴濂� RangePicker 鍙傛暟瀵硅薄鍙洿鎺ュ鐢級銆�
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| qcType | Integer | 鍒嗙被锛堟楠岀被鍨嬶級锛屽瓧鍏� `mes_qc_type` |
+| qcId | Long | 妫�楠屽崟 ID锛堝彲璺宠浆鍒板搴旀楠屽崟璇︽儏锛� |
+| code | String | 妫�楠屽崟缂栧彿 |
+| name | String | 妫�楠屽崟鍚嶇О |
+| status | Integer | 鍗曟嵁鐘舵�侊紝瀛楀吀 `mes_order_status` |
+| itemId / itemCode / itemName / itemSpecification / unitName | - | 鐗╂枡淇℃伅锛堢紪鐮併�佸悕绉般�佽鏍煎瀷鍙枫�佽閲忓崟浣嶏紱鍧囧彇鑷� MDM 鐗╂枡锛� |
+| batchNo | String | 鎵规鍙� |
+| checkQuantity / qualifiedQuantity / unqualifiedQuantity | BigDecimal | 妫�娴嬫暟閲� / 鍚堟牸鏁伴噺 / 涓嶅悎鏍兼暟閲� |
+| checkResult | Integer | 妫�娴嬬粨鏋滐紝瀛楀吀 `mes_qc_check_result` |
+| inspectDate | String (LocalDateTime) | 妫�娴嬫棩鏈� |
+| inspectorUserId / inspectorName | - | 妫�娴嬩汉鍛� |
+| indicatorId / indicatorName | - | 妫�娴嬮」缂栧彿 / 鍚嶇О |
+| indicatorValue | String | 妫�娴嬪�硷紙瀹炴祴鍊硷紝鍘熸牱瀛楃涓诧級 |
+| indicatorRemark | String | 妫�娴嬮」澶囨敞 |
+| judgementBasis | String | 鍒ゅ畾渚濇嵁锛屽 `10~20`銆乣鈮�20`銆乣鈮�10`銆乣鏍囧噯鍊� 15`锛涙棤娉曟嫾瑁呮椂涓虹┖ |
+| standardValue / minThreshold / maxThreshold | BigDecimal | 鍒ゅ畾渚濇嵁鍘熷鍊硷紙鍓嶇灞曠ず鐢� `judgementBasis` 鍗冲彲锛� |
+| remark | String | 妫�楠屽崟澶囨敞 |
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+  "code": 0,
+  "data": {
+    "list": [
+      {
+        "qcType": 1,
+        "qcId": 123,
+        "code": "IQC1789019053513",
+        "name": "AN1789019053466-鐗╂枡妫�楠�",
+        "status": 0,
+        "itemId": 28,
+        "itemCode": "WL0001",
+        "itemName": "鐗╂枡A",
+        "itemSpecification": "100mm*50mm",
+        "unitName": "涓�",
+        "batchNo": "VB20260101",
+        "checkQuantity": 99,
+        "qualifiedQuantity": 99,
+        "unqualifiedQuantity": 0,
+        "checkResult": 1,
+        "inspectDate": "2026-09-10 00:00:00",
+        "inspectorUserId": 111,
+        "inspectorName": "寮犱笁",
+        "indicatorId": 2,
+        "indicatorName": "娓╁害",
+        "indicatorValue": "15",
+        "indicatorRemark": null,
+        "judgementBasis": "10~20",
+        "remark": null
+      }
+    ],
+    "total": 5
+  }
+}
+```
+
+### 2. 鍙拌处瀵煎嚭
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/admin-api/mes/qc/inspection-ledger/export-excel` | 瀵煎嚭妫�娴嬬粨鏋滃彴璐� Excel锛屽弬鏁颁笌鍒嗛〉鎺ュ彛瀹屽叏涓�鑷� |
+
+**瀵煎嚭鍒楋細** 鍒嗙被 / 妫�楠屽崟缂栧彿 / 妫�楠屽崟鍚嶇О / 鐗╂枡缂栫爜 / 鐗╂枡鍚嶇О / 瑙勬牸鍨嬪彿 / 鎵规鍙� / 妫�娴嬮」 / 妫�娴嬪�� / 鍒ゅ畾渚濇嵁 / 妫�娴嬬粨鏋� / 妫�娴嬫暟閲� / 鍚堟牸鏁伴噺 / 涓嶅悎鏍兼暟閲� / 妫�娴嬩汉鍛� / 妫�娴嬫棩鏈� / 鍗曟嵁鐘舵�� / 澶囨敞銆�
+
+## 鏉冮檺涓庤彍鍗�
+
+| 鑿滃崟 | 鏉冮檺鐮� | 璇存槑 |
+|------|--------|------|
+| 璐ㄩ噺绠$悊 鈫� 妫�娴嬬粨鏋滃彴璐� | 鈥� | type=2锛宑omponent `mes/qc/ledger/index` |
+| 妫�娴嬬粨鏋滃彴璐︽煡璇� | `mes:qc-inspection-ledger:query` | 椤甸潰鍔犺浇銆佸垎椤垫煡璇� |
+| 妫�娴嬬粨鏋滃彴璐﹀鍑� | `mes:qc-inspection-ledger:export` | 瀵煎嚭鎸夐挳 |
+
+鑿滃崟涓庢寜閽敱 `docs/sql/qc_inspection_ledger_menu.sql` 鍒涘缓锛�**鑿滃崟 ID 鑷�傚簲**锛屼笉鍐欐 ID锛涚埗鑿滃崟璺熼殢銆屾潵鏂欐楠屾煡璇€�嶆寜閽墍鍦ㄧ殑妫�楠屽崟鑿滃崟锛岃鑹叉巿鏉冭窡闅忋�屾潵鏂欐楠屾煡璇€�嶆寜閽凡鏈夌殑瑙掕壊锛夈�傛墽琛岃剼鏈悗闇�閲嶆柊鐧诲綍浠ュ埛鏂拌彍鍗曚笌鏉冮檺缂撳瓨銆傚墠绔缓濂� `views/mes/qc/ledger/index.vue` 鍚庤彍鍗曞嵆鍙敤銆�
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鍒嗙被 | 鍙拌处鍒楄〃鍒椼�佺瓫閫夐」 | 鐢� `CellDict` + `DICT_TYPE.MES_QC_TYPE` 娓叉煋 |
+| 妫�娴嬬粨鏋� | 鍙拌处鍒楄〃鍒椼�佺瓫閫夐」 | 鐢� `CellDict` + `DICT_TYPE.MES_QC_CHECK_RESULT` |
+| 妫�娴嬮」 / 妫�娴嬪�� / 鍒ゅ畾渚濇嵁 | 鍙拌处鍒楄〃鍒� | 涓�琛屼竴涓娴嬮」锛涙楠屽崟鏈綍鍏ョ粨鏋滄椂涓虹┖ |
+| 鍗曟嵁鐘舵�� | 鍙拌处鍒楄〃鍒� | `DICT_TYPE.MES_ORDER_STATUS` |
+| 鎵规鍙� | 鍙拌处鍒楄〃鍒� | 鏉ユ枡妫�楠屼负渚涘簲鍟嗘壒娆″彿锛涜繃绋�/鍑鸿揣/閫�璐ф楠屼负鎵规鍙� |
+| 妫�娴嬬粨鏋滐紙妫�楠岄」锛� | 鍥涚被妫�楠屽崟鍒楄〃鍒� | 鐩存帴娓叉煋 `indicatorResultSummary`锛堝 `娓╁害:15锛岀數闃�:1`锛夛紝鍒楀寤鸿 200+锛屽唴瀹硅繃闀垮彲鎹㈣鎴栫渷鐣ュ彿鎮诞 |
+| 妫�娴嬫棩鏈熷尯闂� | 鍙拌处椤电瓫閫夊尯 | RangePicker锛寁alueFormat `YYYY-MM-DD HH:mm:ss` |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鍒嗙被涓嶉�� | 鍥涚被妫�楠屽崟涓�璧锋煡璇紝鎸夋娴嬫棩鏈熷�掑簭銆佸悓鏃ユ湡鎸夋楠屽崟 ID 鍊掑簭 |
+| 鏃堕棿鍖洪棿 | 鎸夈�屾娴嬫棩鏈熴�嶉棴鍖洪棿杩囨护锛涘彧浼犲紑濮嬫垨鍙紶缁撴潫鍧囧彲 |
+| 涓�琛屼竴妫�娴嬮」 | 涓�寮犳楠屽崟褰曞叆澶氫釜妫�娴嬮」缁撴灉鏃跺睍寮�涓哄琛岋紙鍏朵綑瀛楁閲嶅锛� |
+| 妫�楠屽崟鏈綍鍏ユ娴嬮」 | 璇ュ崟淇濈暀涓�琛岋紝妫�娴嬮」 / 妫�娴嬪�� / 鍒ゅ畾渚濇嵁涓虹┖ |
+| 鍒ゅ畾渚濇嵁 | 鐢辨楠屽崟琛岀殑鏍囧噯鍊间笌璇樊涓婁笅闄愭嫾瑁咃細涓婁笅闄愰兘鏈� 鈫� `涓嬮檺~涓婇檺`锛涗粎涓婇檺 鈫� `鈮や笂闄恅锛涗粎涓嬮檺 鈫� `鈮ヤ笅闄恅锛涗粎鏍囧噯鍊� 鈫� `鏍囧噯鍊� 鍊糮锛涢兘娌℃湁鍒欎负绌� |
+| 閫昏緫鍒犻櫎 | 宸插垹闄ょ殑妫�楠屽崟涓嶅嚭鐜板湪鍙拌处锛涘凡鍒犻櫎鐨勬娴嬮」浠嶅睍绀哄叾鍘嗗彶鍚嶇О锛屼繚璇佸巻鍙插彴璐﹀彲璇� |
+| 瀵煎嚭鑼冨洿 | 瀵煎嚭閬靛惊褰撳墠绛涢�夋潯浠讹紱涓嶉�夋椂闂村垯瀵煎嚭鍏ㄩ儴绗﹀悎鍏跺畠鏉′欢鐨勮褰� |
+
+## 娉ㄦ剰浜嬮」
+
+- 鏈鍚庣鏂板浜嗛〉闈㈡帴鍙d笌鑿滃崟锛�**闇�閲嶅惎鍚庣鏈嶅姟**鍚庢柊鎺ュ彛鎵嶅彲鐢紱骞舵墽琛� `docs/sql/qc_inspection_ledger_menu.sql` 鍚庨噸鏂扮櫥褰曚互鍒锋柊鑿滃崟涓庢潈闄愮紦瀛樸��
+- 鍙拌处鎺ュ彛鐨� `qcId` + `qcType` 鍙畾浣嶅埌鍘熷妫�楠屽崟锛岃嫢闇�瑕佺偣鍑昏烦杞紝璇锋寜鍒嗙被璺冲埌瀵瑰簲椤甸潰锛堟潵鏂欐楠� / 杩囩▼妫�楠� / 鍑鸿揣妫�楠� / 閫�璐ф楠岋級銆�
+- 鍙拌处鐨勩�屾娴嬬粨鏋溿�嶅垪涓庡洓绫绘楠屽崟鍒楄〃鐨勩�屾娴嬬粨鏋溿�嶅垪鍙e緞涓�鑷达紝鍧囧彇妫�楠屽崟澶寸殑妫�娴嬬粨璁猴紙瀛楀吀 `mes_qc_check_result`锛夈��
+- 鍥涚被妫�楠屽崟鍒楄〃鏂板鐨勩�屾娴嬬粨鏋滐紙妫�楠岄」锛夈�嶅垪鐩存帴浣跨敤宸叉湁瀛楁 `indicatorResultSummary`锛屼笉闇�瑕佹柊鎺ュ彛锛涘鍑烘椂璇ュ唴瀹瑰凡鍖呭惈鍦ㄣ�屾楠屾寚鏍囥�嶅垪涓��
+- 鍙拌处涓哄彧璇婚〉闈紝涓嶆彁渚涙柊澧� / 淇敼 / 鍒犻櫎鎿嶄綔銆�
diff --git a/docs/sql/crm_invoice_import_menu.sql b/docs/sql/crm_invoice_import_menu.sql
new file mode 100644
index 0000000..ab69b3e
--- /dev/null
+++ b/docs/sql/crm_invoice_import_menu.sql
@@ -0,0 +1,70 @@
+-- ============================================================
+-- CRM 寮�绁ㄥ鍏� 鑿滃崟涓庢寜閽潈闄�
+--
+-- 鑳屾櫙锛氬紑绁ㄧ鐞嗘柊澧� Excel 鎵归噺瀵煎叆鑳藉姏
+--       锛堝悗绔帴鍙� GET /crm/invoice/get-import-template 涓� POST /crm/invoice/import锛夛紝
+--       闇�瑕佸湪寮�绁ㄥ垪琛ㄩ〉鏆撮湶銆屽鍏ャ�嶆寜閽潈闄愶紝鍚﹀垯鍓嶇鎸夐挳浼氳 @PreAuthorize 鎷︽埅銆�
+--
+-- 鏂板鎸夐挳鏉冮檺锛歝rm:invoice:import锛堝紑绁ㄥ垪琛ㄣ�屽鍏ャ�嶆寜閽級
+--
+-- ID 鑷�傚簲锛氭湰鑴氭湰涓嶅啓姝昏彍鍗� ID
+--   鈶� 鑿滃崟 ID 鐢� system_menu 鑷鍒嗛厤锛堜笉鎸囧畾 id 鍒楋級锛�
+--   鈶� 鐖惰彍鍗曟寜鏉冮檺鐮佸弽鏌ュ紑绁ㄧ鐞嗚彍鍗曪紙crm:invoice:export -> 鍏� parent_id锛夛紝
+--      鏌ヤ笉鍒版椂渚濇鍥為�� crm:invoice:query銆佸悕绉般�屽紑绁ㄧ鐞嗐�嶇殑 type=2 鑿滃崟锛�
+--   鈶� 瑙掕壊鎺堟潈璺熼殢寮�绁ㄧ鐞嗐�屽鍑恒�嶆寜閽凡鏈夌殑瑙掕壊锛屼笉鍐欐 role_id銆�
+--
+-- 骞傜瓑锛氭寜鏉冮檺鐮佸厛鍒犲悗鎻掞紝鍙噸澶嶆墽琛屻��
+--
+-- 鎵ц鏂瑰紡锛圥owerShell锛岄伩鍏嶄腑鏂囦贡鐮侊級锛�
+--   mysql -uroot -p1234 --default-character-set=utf8mb4 ruoyi-vue-pro < docs/sql/crm_invoice_import_menu.sql
+-- ============================================================
+
+SET NAMES utf8mb4;
+
+-- ------------------------------------------------------------
+-- 0. 骞傜瓑娓呯悊
+-- ------------------------------------------------------------
+DELETE rm FROM `system_role_menu` rm
+    JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+WHERE m.`permission` = 'crm:invoice:import';
+
+DELETE FROM `system_menu` WHERE `permission` = 'crm:invoice:import';
+
+-- ------------------------------------------------------------
+-- 1. 鐖惰彍鍗曪細寮�绁ㄧ鐞嗚彍鍗曪紙type=2锛�
+-- ------------------------------------------------------------
+SET @invoice_menu_id = COALESCE(
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'crm:invoice:export' AND `deleted` = b'0' LIMIT 1),
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'crm:invoice:query'  AND `deleted` = b'0' LIMIT 1),
+    (SELECT `id`        FROM `system_menu` WHERE `name` = '寮�绁ㄧ鐞�' AND `type` = 2 AND `deleted` = b'0' LIMIT 1));
+
+-- ------------------------------------------------------------
+-- 2. 鏂板鎸夐挳锛堣彍鍗� ID 鑷锛�
+-- ------------------------------------------------------------
+INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '寮�绁ㄥ鍏�', 'crm:invoice:import', 3, 5, @invoice_menu_id, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+WHERE @invoice_menu_id IS NOT NULL;
+SET @menu_invoice_import = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), NULL);
+
+-- ------------------------------------------------------------
+-- 3. 鎺堟潈锛氳窡闅忓紑绁ㄧ鐞嗐�屽鍑恒�嶆寜閽凡鏈夌殑瑙掕壊锛堝厹搴曘�屾煡璇€�嶆寜閽級
+-- ------------------------------------------------------------
+INSERT INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT DISTINCT rm.`role_id`, @menu_invoice_import, '1', NOW(), '1', NOW(), b'0'
+FROM `system_role_menu` rm
+WHERE rm.`menu_id` = COALESCE(
+        (SELECT `id` FROM `system_menu` WHERE `permission` = 'crm:invoice:export' AND `deleted` = b'0' LIMIT 1),
+        (SELECT `id` FROM `system_menu` WHERE `permission` = 'crm:invoice:query'  AND `deleted` = b'0' LIMIT 1))
+  AND rm.`deleted` = b'0'
+  AND @menu_invoice_import IS NOT NULL;
+
+-- ------------------------------------------------------------
+-- 4. 鏍¢獙锛氳彍鍗曞簲杩斿洖 1 琛岋紱鎺堟潈琛屾暟涓庛�屽紑绁ㄧ鐞嗗鍑恒�嶆寜閽竴鑷�
+-- ------------------------------------------------------------
+SELECT '鑿滃崟 crm:invoice:import  ' AS item, COUNT(*) AS cnt
+    FROM `system_menu` WHERE `permission` = 'crm:invoice:import' AND `deleted` = b'0'
+UNION ALL SELECT '鎺堟潈 / 寮�绁ㄥ鍑烘巿鏉�     ', CONCAT(
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'crm:invoice:import' AND rm.`deleted` = b'0'), ' / ',
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'crm:invoice:export' AND rm.`deleted` = b'0'));
diff --git a/docs/sql/mes_qc_import_menu.sql b/docs/sql/mes_qc_import_menu.sql
new file mode 100644
index 0000000..4536085
--- /dev/null
+++ b/docs/sql/mes_qc_import_menu.sql
@@ -0,0 +1,115 @@
+-- ============================================================
+-- MES 璐ㄦ鍗曟壒閲忓鍏� 鑿滃崟涓庢寜閽潈闄�
+--
+-- 鑳屾櫙锛氭潵鏂欐楠岋紙IQC锛夈�佽繃绋嬫楠岋紙IPQC锛夈�佸嚭璐ф楠岋紙OQC锛夊悇鑷柊澧炪�屾壒閲忓鍏ャ�嶈兘鍔�
+--       锛堝悗绔帴鍙� GET /mes/qc/{iqc|ipqc|oqc}/get-import-template 涓� POST .../import锛夛紝
+--       闇�瑕佸湪瀵瑰簲鍒楄〃椤垫毚闇叉寜閽潈闄愶紝鍚﹀垯鍓嶇鎸夐挳浼氳 @PreAuthorize 鎷︽埅銆�
+--
+-- 鏂板鎸夐挳鏉冮檺锛�
+--   mes:qc-iqc:import   鏉ユ枡妫�楠屽垪琛ㄣ�屾壒閲忓鍏ャ�嶆寜閽�
+--   mes:qc-ipqc:import  杩囩▼妫�楠屽垪琛ㄣ�屾壒閲忓鍏ャ�嶆寜閽�
+--   mes:qc-oqc:import   鍑鸿揣妫�楠屽垪琛ㄣ�屾壒閲忓鍏ャ�嶆寜閽�
+--
+-- ID 鑷�傚簲锛氭湰鑴氭湰涓嶅啓姝昏彍鍗� ID
+--   鈶� 鑿滃崟 ID 鐢� system_menu 鑷鍒嗛厤锛堜笉鎸囧畾 id 鍒楋級锛�
+--   鈶� 鐖惰彍鍗曟寜鏉冮檺鐮佸弽鏌ヨ妯″潡銆屾煡璇€�嶆寜閽墍鍦ㄨ彍鍗曪紙mes:qc-iqc:query 绛夛級锛�
+--      鏌ヤ笉鍒版椂渚濇鍥為��鍒般�屽鍑恒�嶆寜閽墍鍦ㄨ彍鍗曘�佸悓鍚� type=2 鑿滃崟锛�
+--   鈶� 瑙掕壊鎺堟潈璺熼殢鍚屾ā鍧椼�屾煡璇€�嶆寜閽凡鏈夌殑瑙掕壊锛屼笉鍐欐 role_id銆�
+--   鍥犳鑴氭湰鍙湪浠讳綍搴撶粨鏋勪竴鑷寸殑瀹炰緥涓婄洿鎺ユ墽琛屻��
+--
+-- 骞傜瓑锛氭寜鏉冮檺鐮佸厛鍒犲悗鎻掞紝鍙噸澶嶆墽琛屻��
+--
+-- 鎵ц鏂瑰紡锛圥owerShell锛岄伩鍏嶄腑鏂囪彍鍗曞悕涔辩爜锛夛細
+--   mysql -uroot -p1234 --default-character-set=utf8mb4 ruoyi-vue-pro < docs/sql/mes_qc_import_menu.sql
+-- ============================================================
+
+SET NAMES utf8mb4;
+
+-- ------------------------------------------------------------
+-- 0. 骞傜瓑娓呯悊锛氬垹闄ゆ棦鏈夊鍏ユ寜閽強鍏惰鑹叉巿鏉�
+-- ------------------------------------------------------------
+DELETE rm FROM `system_role_menu` rm
+    JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+WHERE m.`permission` IN ('mes:qc-iqc:import', 'mes:qc-ipqc:import', 'mes:qc-oqc:import');
+
+DELETE FROM `system_menu`
+WHERE `permission` IN ('mes:qc-iqc:import', 'mes:qc-ipqc:import', 'mes:qc-oqc:import');
+
+-- ------------------------------------------------------------
+-- 1. 鐖惰彍鍗曪細鍚勬楠屽崟鍒楄〃椤垫墍鍦ㄨ彍鍗曪紙type=2锛�
+-- ------------------------------------------------------------
+SET @iqc_parent = COALESCE(
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'mes:qc-iqc:query'  AND `deleted` = b'0' LIMIT 1),
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'mes:qc-iqc:export' AND `deleted` = b'0' LIMIT 1));
+
+SET @ipqc_parent = COALESCE(
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'mes:qc-ipqc:query'  AND `deleted` = b'0' LIMIT 1),
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'mes:qc-ipqc:export' AND `deleted` = b'0' LIMIT 1));
+
+SET @oqc_parent = COALESCE(
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'mes:qc-oqc:query'  AND `deleted` = b'0' LIMIT 1),
+    (SELECT `parent_id` FROM `system_menu` WHERE `permission` = 'mes:qc-oqc:export' AND `deleted` = b'0' LIMIT 1));
+
+-- ------------------------------------------------------------
+-- 2. 鏂板鎸夐挳锛堣彍鍗� ID 鑷锛�
+-- ------------------------------------------------------------
+INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '鏉ユ枡妫�楠屽鍏�', 'mes:qc-iqc:import', 3, 6, @iqc_parent, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+WHERE @iqc_parent IS NOT NULL;
+SET @menu_iqc_import = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), NULL);
+
+INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '杩囩▼妫�楠屽鍏�', 'mes:qc-ipqc:import', 3, 6, @ipqc_parent, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+WHERE @ipqc_parent IS NOT NULL;
+SET @menu_ipqc_import = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), NULL);
+
+INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '鍑鸿揣妫�楠屽鍏�', 'mes:qc-oqc:import', 3, 6, @oqc_parent, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+WHERE @oqc_parent IS NOT NULL;
+SET @menu_oqc_import = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), NULL);
+
+-- ------------------------------------------------------------
+-- 3. 鎺堟潈锛氳窡闅忓悓妯″潡銆屾煡璇€�嶆寜閽凡鏈夌殑瑙掕壊锛堝悓鏃跺厹搴曡秴绾х鐞嗗憳瑙掕壊 1锛�
+-- ------------------------------------------------------------
+INSERT INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT DISTINCT rm.`role_id`, @menu_iqc_import, '1', NOW(), '1', NOW(), b'0'
+FROM `system_role_menu` rm
+WHERE rm.`menu_id` = (SELECT `id` FROM `system_menu` WHERE `permission` = 'mes:qc-iqc:query' AND `deleted` = b'0' LIMIT 1)
+  AND rm.`deleted` = b'0'
+  AND @menu_iqc_import IS NOT NULL;
+
+INSERT INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT DISTINCT rm.`role_id`, @menu_ipqc_import, '1', NOW(), '1', NOW(), b'0'
+FROM `system_role_menu` rm
+WHERE rm.`menu_id` = (SELECT `id` FROM `system_menu` WHERE `permission` = 'mes:qc-ipqc:query' AND `deleted` = b'0' LIMIT 1)
+  AND rm.`deleted` = b'0'
+  AND @menu_ipqc_import IS NOT NULL;
+
+INSERT INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT DISTINCT rm.`role_id`, @menu_oqc_import, '1', NOW(), '1', NOW(), b'0'
+FROM `system_role_menu` rm
+WHERE rm.`menu_id` = (SELECT `id` FROM `system_menu` WHERE `permission` = 'mes:qc-oqc:query' AND `deleted` = b'0' LIMIT 1)
+  AND rm.`deleted` = b'0'
+  AND @menu_oqc_import IS NOT NULL;
+
+-- ------------------------------------------------------------
+-- 4. 鏍¢獙锛氬簲鍚勮嚜杩斿洖 1 琛岃彍鍗曪紱瑙掕壊鎺堟潈琛屾暟涓庡悓妯″潡銆屾煡璇€�嶆寜閽竴鑷�
+-- ------------------------------------------------------------
+SELECT '鑿滃崟 mes:qc-iqc:import ' AS item, COUNT(*) AS cnt FROM `system_menu` WHERE `permission` = 'mes:qc-iqc:import'   AND `deleted` = b'0'
+UNION ALL SELECT '鑿滃崟 mes:qc-ipqc:import', COUNT(*) FROM `system_menu` WHERE `permission` = 'mes:qc-ipqc:import'  AND `deleted` = b'0'
+UNION ALL SELECT '鑿滃崟 mes:qc-oqc:import ', COUNT(*) FROM `system_menu` WHERE `permission` = 'mes:qc-oqc:import'   AND `deleted` = b'0'
+UNION ALL SELECT 'IQC 鎺堟潈 / 鏌ヨ鎺堟潈  ', CONCAT(
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'mes:qc-iqc:import' AND rm.`deleted` = b'0'), ' / ',
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'mes:qc-iqc:query' AND rm.`deleted` = b'0'))
+UNION ALL SELECT 'IPQC 鎺堟潈 / 鏌ヨ鎺堟潈 ', CONCAT(
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'mes:qc-ipqc:import' AND rm.`deleted` = b'0'), ' / ',
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'mes:qc-ipqc:query' AND rm.`deleted` = b'0'))
+UNION ALL SELECT 'OQC 鎺堟潈 / 鏌ヨ鎺堟潈  ', CONCAT(
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'mes:qc-oqc:import' AND rm.`deleted` = b'0'), ' / ',
+    (SELECT COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+      WHERE m.`permission` = 'mes:qc-oqc:query' AND rm.`deleted` = b'0'));
diff --git a/docs/sql/qc_inspection_ledger_menu.sql b/docs/sql/qc_inspection_ledger_menu.sql
new file mode 100644
index 0000000..114cee9
--- /dev/null
+++ b/docs/sql/qc_inspection_ledger_menu.sql
@@ -0,0 +1,94 @@
+-- ============================================================
+-- 璐ㄩ噺绠$悊 - 妫�娴嬬粨鏋滃彴璐� 鑿滃崟涓庢潈闄�
+--
+-- 鑳屾櫙锛氳川妫�鍚勬ā鍧楋紙IQC/IPQC/OQC/RQC锛夌殑妫�娴嬬粨鏋滃師鍏堝垎鏁e湪鍚勮嚜鍒楄〃鍐咃紝
+--       缂哄皯璺ㄧ被鍨嬬殑缁熶竴鏌ヨ涓庡彴璐﹀鍑恒�傛湰娆℃柊澧炪�屾娴嬬粨鏋滃彴璐︺�嶉〉闈細
+--       鎸夋楠岀被鍨� + 妫�娴嬫棩鏈熷尯闂� + 鐗╂枡/鎵规/妫�娴嬬粨鏋�/妫�娴嬩汉绛涢�夛紝
+--       鍒楄〃鐩存帴灞曠ず妫�娴嬮」瀹炴祴缁撴灉锛屽苟鏀寔瀵煎嚭 Excel 鍙拌处鐢ㄤ簬鏌ヨ銆佹墦鍗般�佸瓨妗c��
+--
+-- 鏂板鑿滃崟涓庢寜閽細
+--   鈶� 鑿滃崟銆屾娴嬬粨鏋滃彴璐︺��   type=2锛宑omponent=mes/qc/ledger/index
+--   鈶� 鎸夐挳銆屾娴嬬粨鏋滃彴璐︽煡璇€��= mes:qc-inspection-ledger:query
+--   鈶� 鎸夐挳銆屾娴嬬粨鏋滃彴璐﹀鍑恒��= mes:qc-inspection-ledger:export
+--
+-- ID 鑷�傚簲锛氭湰鑴氭湰涓嶅啓姝昏彍鍗� ID
+--   鈶� 鑿滃崟 ID 鐢� system_menu 鑷鍒嗛厤锛堜笉鎸囧畾 id 鍒楋級锛�
+--   鈶� 鐖惰彍鍗� = 銆屾潵鏂欐楠屾煡璇€��(mes:qc-iqc:query) 鎵�鍦ㄦ楠屽崟鑿滃崟鐨勪笂绾э紙鍗宠川閲忕鐞嗙洰褰曪級锛�
+--      鏌ヤ笉鍒版椂鍥為��涓哄悕绉般�岃川閲忕鐞嗐�嶃�乼ype=1 鐨勭洰褰曪紱
+--   鈶� 瑙掕壊鎺堟潈璺熼殢銆屾潵鏂欐楠屾煡璇€�嶆寜閽凡鏈夌殑瑙掕壊锛屼笉鍐欐 role_id銆�
+--
+-- 骞傜瓑锛氭寜鏉冮檺鐮� / 鍚嶇О鍏堝垹鍚庢彃锛屽彲閲嶅鎵ц銆�
+--
+-- 鎵ц鏂瑰紡锛圥owerShell锛岄伩鍏嶄腑鏂囦贡鐮侊級锛�
+--   mysql -uroot -p1234 --default-character-set=utf8mb4 ruoyi-vue-pro < docs/sql/qc_inspection_ledger_menu.sql
+-- ============================================================
+
+SET NAMES utf8mb4;
+
+-- ------------------------------------------------------------
+-- 0. 骞傜瓑娓呯悊
+-- ------------------------------------------------------------
+DELETE rm FROM `system_role_menu` rm
+    JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+WHERE m.`permission` IN ('mes:qc-inspection-ledger:query', 'mes:qc-inspection-ledger:export')
+   OR (m.`name` = '妫�娴嬬粨鏋滃彴璐�' AND m.`type` = 2);
+
+DELETE FROM `system_menu`
+WHERE `permission` IN ('mes:qc-inspection-ledger:query', 'mes:qc-inspection-ledger:export')
+   OR (`name` = '妫�娴嬬粨鏋滃彴璐�' AND `type` = 2);
+
+-- ------------------------------------------------------------
+-- 1. 鐖惰彍鍗曪細璐ㄩ噺绠$悊鐩綍
+--    鏉ユ枡妫�楠屾煡璇㈡寜閽� -> 鏉ユ枡妫�楠岃彍鍗�(type=2) -> 璐ㄩ噺绠$悊鐩綍(type=1)
+-- ------------------------------------------------------------
+SET @iqc_menu_id = (SELECT `parent_id` FROM `system_menu`
+                     WHERE `permission` = 'mes:qc-iqc:query' AND `deleted` = b'0' LIMIT 1);
+SET @qc_dir_id = COALESCE(
+    (SELECT `parent_id` FROM `system_menu` WHERE `id` = @iqc_menu_id),
+    (SELECT `id` FROM `system_menu` WHERE `name` = '璐ㄩ噺绠$悊' AND `type` = 1 AND `deleted` = b'0' LIMIT 1));
+
+-- ------------------------------------------------------------
+-- 2. 鑿滃崟锛氭娴嬬粨鏋滃彴璐︼紙type=2锛�
+-- ------------------------------------------------------------
+INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '妫�娴嬬粨鏋滃彴璐�', '', 2, 14, @qc_dir_id, 'inspection-ledger', 'ep:document', 'mes/qc/ledger/index', 'MesQcInspectionLedger', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+WHERE @qc_dir_id IS NOT NULL;
+SET @menu_ledger = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), NULL);
+
+-- ------------------------------------------------------------
+-- 3. 鎸夐挳锛氭煡璇� / 瀵煎嚭
+-- ------------------------------------------------------------
+INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '妫�娴嬬粨鏋滃彴璐︽煡璇�', 'mes:qc-inspection-ledger:query', 3, 1, @menu_ledger, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+WHERE @menu_ledger IS NOT NULL;
+SET @menu_ledger_query = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), NULL);
+
+INSERT INTO `system_menu` (`name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '妫�娴嬬粨鏋滃彴璐﹀鍑�', 'mes:qc-inspection-ledger:export', 3, 2, @menu_ledger, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+WHERE @menu_ledger IS NOT NULL;
+SET @menu_ledger_export = IF(ROW_COUNT() > 0, LAST_INSERT_ID(), NULL);
+
+-- ------------------------------------------------------------
+-- 4. 鎺堟潈锛氳窡闅忋�屾潵鏂欐楠屾煡璇€�嶆寜閽凡鏈夌殑瑙掕壊
+-- ------------------------------------------------------------
+INSERT INTO `system_role_menu` (`role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT DISTINCT rm.`role_id`, t.`menu_id`, '1', NOW(), '1', NOW(), b'0'
+FROM `system_role_menu` rm
+    JOIN (SELECT @menu_ledger AS `menu_id`
+          UNION ALL SELECT @menu_ledger_query
+          UNION ALL SELECT @menu_ledger_export) t
+WHERE rm.`menu_id` = @iqc_menu_id
+  AND rm.`deleted` = b'0'
+  AND t.`menu_id` IS NOT NULL;
+
+-- ------------------------------------------------------------
+-- 5. 鏍¢獙锛氳彍鍗� 3 琛岋紱鎺堟潈琛屾暟 = 3 脳 銆屾潵鏂欐楠屾煡璇€�嶇殑瑙掕壊鏁�
+-- ------------------------------------------------------------
+SELECT '鑿滃崟 妫�娴嬬粨鏋滃彴璐�        ' AS item, COUNT(*) AS cnt FROM `system_menu`
+    WHERE `name` = '妫�娴嬬粨鏋滃彴璐�' AND `type` = 2 AND `deleted` = b'0'
+UNION ALL SELECT '鎸夐挳 鏌ヨ/瀵煎嚭            ', COUNT(*) FROM `system_menu`
+    WHERE `permission` IN ('mes:qc-inspection-ledger:query', 'mes:qc-inspection-ledger:export') AND `deleted` = b'0'
+UNION ALL SELECT '鎺堟潈 3 涓彍鍗�             ', COUNT(*) FROM `system_role_menu`
+    WHERE `menu_id` IN (@menu_ledger, @menu_ledger_query, @menu_ledger_export) AND `deleted` = b'0'
+UNION ALL SELECT '瀵规瘮 鏉ユ枡妫�楠屾煡璇㈡巿鏉冭鏁� ', COUNT(*) FROM `system_role_menu` rm JOIN `system_menu` m ON rm.`menu_id` = m.`id`
+    WHERE m.`permission` = 'mes:qc-iqc:query' AND rm.`deleted` = b'0';
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/CrmInvoiceController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/CrmInvoiceController.java
index 8397ad9..3ef2373 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/CrmInvoiceController.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/CrmInvoiceController.java
@@ -10,6 +10,8 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
+import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceImportExcelVO;
+import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceImportRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoicePageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceSaveReqVO;
@@ -36,6 +38,7 @@
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
 import java.math.BigDecimal;
@@ -144,6 +147,27 @@
                 buildInvoiceDetailList(list));
     }
 
+    @GetMapping("/get-import-template")
+    @Operation(summary = "鑾峰緱瀵煎叆寮�绁ㄦā鏉�")
+    @PreAuthorize("@ss.hasPermission('crm:invoice:import')")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        List<CrmInvoiceImportExcelVO> list = Collections.singletonList(
+                CrmInvoiceImportExcelVO.builder()
+                        .contractNo("HT20260101000001").invoiceType("澧炲�肩◣涓撶敤鍙戠エ")
+                        .invoiceNo("02564178").invoiceTitle("绀轰緥瀹㈡埛鏈夐檺鍏徃").price(new BigDecimal("10000.00"))
+                        .invoiceTime("2026-01-01 10:00:00").remark("绀轰緥鏁版嵁锛屽鍏ュ墠璇锋浛鎹�")
+                        .build());
+        ExcelUtils.write(response, "寮�绁ㄥ鍏ユā鏉�.xls", "寮�绁�", CrmInvoiceImportExcelVO.class, list);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "瀵煎叆寮�绁�")
+    @PreAuthorize("@ss.hasPermission('crm:invoice:import')")
+    public CommonResult<CrmInvoiceImportRespVO> importInvoiceExcel(@RequestParam("file") MultipartFile file) throws Exception {
+        List<CrmInvoiceImportExcelVO> list = ExcelUtils.read(file, CrmInvoiceImportExcelVO.class);
+        return success(invoiceService.importInvoiceList(list));
+    }
+
     private List<CrmInvoiceRespVO> buildInvoiceDetailList(List<CrmInvoiceDO> invoiceList) {
         if (CollUtil.isEmpty(invoiceList)) {
             return Collections.emptyList();
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportExcelVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportExcelVO.java
new file mode 100644
index 0000000..016067b
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportExcelVO.java
@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 绠$悊鍚庡彴 - 寮�绁ㄥ鍏� Excel VO
+ *
+ * 璇存槑锛氫竴琛� = 涓�寮犲紑绁ㄨ褰曘�傗�滃悎鍚岀紪鍙封�濅负蹇呭~锛岄渶涓庣郴缁熶腑宸插鏍搁�氳繃鐨勫悎鍚岀紪鍙蜂竴鑷淬��
+ * 瀹㈡埛銆佽礋璐d汉銆佸鎵圭姸鎬佺敱绯荤粺澶勭悊锛氬鎴峰彇鍚堝悓鎵�灞炲鎴凤紱璐熻矗浜� = 褰撳墠鐧诲綍鐢ㄦ埛锛涘鎵圭姸鎬� = 鏈彁浜ゃ��
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Schema(description = "绠$悊鍚庡彴 - 寮�绁ㄥ鍏� Excel VO")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class CrmInvoiceImportExcelVO {
+
+    @ExcelProperty("寮�绁ㄧ紪鍙�")
+    @Schema(description = "寮�绁ㄧ紪鍙凤紝鐣欑┖鍒欒嚜鍔ㄧ敓鎴�", example = "KP20260101000001")
+    private String no;
+
+    @ExcelProperty("鍚堝悓缂栧彿")
+    @Schema(description = "鍚堝悓缂栧彿锛岄渶涓庣郴缁熶腑宸插鏍搁�氳繃鐨勫悎鍚岀紪鍙蜂竴鑷�", requiredMode = Schema.RequiredMode.REQUIRED, example = "HT20260101000001")
+    private String contractNo;
+
+    @ExcelProperty("鍙戠エ绫诲瀷")
+    @Schema(description = "鍙戠エ绫诲瀷锛屽~鍐欏瓧鍏� crm_invoice_type 鐨勫悕绉帮紝濡� 澧炲�肩◣涓撶敤鍙戠エ", requiredMode = Schema.RequiredMode.REQUIRED, example = "澧炲�肩◣涓撶敤鍙戠エ")
+    private String invoiceType;
+
+    @ExcelProperty("鍙戠エ鍙风爜")
+    @Schema(description = "鍙戠エ鍙风爜", example = "02564178")
+    private String invoiceNo;
+
+    @ExcelProperty("鍙戠エ鎶ご")
+    @Schema(description = "鍙戠エ鎶ご", example = "XX绉戞妧鏈夐檺鍏徃")
+    private String invoiceTitle;
+
+    @ExcelProperty("寮�绁ㄩ噾棰�(鍏�)")
+    @Schema(description = "寮�绁ㄩ噾棰濓紝闇�澶т簬 0 涓斾笉瓒呰繃鍚堝悓鍓╀綑鍙紑绁ㄩ噾棰�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000.00")
+    private BigDecimal price;
+
+    @ExcelProperty("寮�绁ㄦ棩鏈�")
+    @Schema(description = "寮�绁ㄦ棩鏈燂紝鏍煎紡濡� 2026-01-01 10:00:00锛涚暀绌哄彇褰撳墠鏃堕棿", example = "2026-01-01 10:00:00")
+    private String invoiceTime;
+
+    @ExcelProperty("澶囨敞")
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    private String remark;
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportRespVO.java
new file mode 100644
index 0000000..cb97d96
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/invoice/vo/invoice/CrmInvoiceImportRespVO.java
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 绠$悊鍚庡彴 - 寮�绁ㄥ鍏� Response VO
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Schema(description = "绠$悊鍚庡彴 - 寮�绁ㄥ鍏� Response VO")
+@Data
+@Builder
+public class CrmInvoiceImportRespVO {
+
+    @Schema(description = "鍒涘缓鎴愬姛鐨勫紑绁ㄧ紪鍙锋暟缁�", example = "KP20260101000001")
+    private List<String> createList;
+
+    @Schema(description = "瀵煎叆澶辫触闆嗗悎锛宬ey 涓鸿鍙锋垨鍙戠エ鍙风爜锛寁alue 涓哄け璐ュ師鍥�")
+    private Map<String, String> failureList;
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/invoice/CrmInvoiceMapper.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/invoice/CrmInvoiceMapper.java
index 9dcbca0..66daa01 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/invoice/CrmInvoiceMapper.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/invoice/CrmInvoiceMapper.java
@@ -91,4 +91,13 @@
         return selectCount(CrmInvoiceDO::getContractId, contractId);
     }
 
+    /**
+     * 鏌ヨ鍚堝悓涓嬬殑鍏ㄩ儴寮�绁紙瀵煎叆鍘婚噸鐢級
+     */
+    default List<CrmInvoiceDO> selectListByContractId(Long contractId) {
+        return selectList(new LambdaQueryWrapperX<CrmInvoiceDO>()
+                .eq(CrmInvoiceDO::getContractId, contractId)
+                .orderByAsc(CrmInvoiceDO::getId));
+    }
+
 }
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
index b1390b6..e4a9837 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
@@ -107,6 +107,14 @@
     CrmContractDO getContract(Long id);
 
     /**
+     * 鑾峰緱鍚堝悓锛屽熀浜庡悎鍚岀紪鍙�
+     *
+     * @param no 鍚堝悓缂栧彿
+     * @return 鍚堝悓
+     */
+    CrmContractDO getContractByNo(String no);
+
+    /**
      * 鏍¢獙鍚堝悓鏄惁鍚堟硶
      *
      * @param id 缂栧彿
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
index 448dae2..2bfb7fa 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -644,6 +644,11 @@
     }
 
     @Override
+    public CrmContractDO getContractByNo(String no) {
+        return contractMapper.selectByNo(no);
+    }
+
+    @Override
     public CrmContractDO validateContract(Long id) {
         return validateContractExists(id);
     }
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceService.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceService.java
index 0390851..53109b1 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceService.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceService.java
@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.crm.service.invoice;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceImportExcelVO;
+import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceImportRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoicePageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceSaveReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.invoice.CrmInvoiceDO;
@@ -29,6 +31,14 @@
     Long createInvoice(@Valid CrmInvoiceSaveReqVO createReqVO);
 
     /**
+     * 瀵煎叆寮�绁�
+     *
+     * @param importList 瀵煎叆鐨勫紑绁ㄥ垪琛�
+     * @return 瀵煎叆缁撴灉
+     */
+    CrmInvoiceImportRespVO importInvoiceList(List<CrmInvoiceImportExcelVO> importList);
+
+    /**
      * 鏇存柊寮�绁�
      *
      * @param updateReqVO 鏇存柊淇℃伅
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceServiceImpl.java
index 4d0fd42..a59c979 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceServiceImpl.java
@@ -5,12 +5,16 @@
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
+import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceImportExcelVO;
+import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceImportRespVO;
 import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoicePageReqVO;
 import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceSaveReqVO;
 import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
@@ -36,11 +40,18 @@
 import org.springframework.validation.annotation.Validated;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_INVOICE_TYPE;
 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
 import static cn.iocoder.yudao.module.crm.util.CrmAuditStatusUtils.convertBpmResultToAuditStatus;
@@ -118,22 +129,247 @@
         return invoice.getId();
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public CrmInvoiceImportRespVO importInvoiceList(List<CrmInvoiceImportExcelVO> importList) {
+        CrmInvoiceImportRespVO respVO = CrmInvoiceImportRespVO.builder()
+                .createList(new ArrayList<>())
+                .failureList(new LinkedHashMap<>())
+                .build();
+        if (CollUtil.isEmpty(importList)) {
+            return respVO;
+        }
+
+        Map<String, Integer> firstRowNoByKey = new HashMap<>();
+        for (int i = 0; i < importList.size(); i++) {
+            CrmInvoiceImportExcelVO row = importList.get(i);
+            if (isImportRowEmpty(row)) {
+                continue; // 璺宠繃绌鸿
+            }
+            String label = StrUtil.isNotBlank(row.getInvoiceNo()) ? "鍙戠エ鍙风爜 " + row.getInvoiceNo().trim()
+                    : "绗� " + (i + 2) + " 琛�";
+            String importKey = buildInvoiceImportKey(row);
+            try {
+                Integer firstRowNo = firstRowNoByKey.get(importKey);
+                if (firstRowNo != null) {
+                    throw duplicateRowException(firstRowNo, buildInvoiceKeyDesc(row));
+                }
+                respVO.getCreateList().add(createInvoiceByImport(row));
+                firstRowNoByKey.putIfAbsent(importKey, i + 2);
+            } catch (Exception ex) {
+                respVO.getFailureList().put(label, resolveImportErrorMessage(ex));
+            }
+        }
+        return respVO;
+    }
+
+    private boolean isImportRowEmpty(CrmInvoiceImportExcelVO row) {
+        return row == null || (StrUtil.isBlank(row.getNo()) && StrUtil.isBlank(row.getContractNo())
+                && StrUtil.isBlank(row.getInvoiceType()) && StrUtil.isBlank(row.getInvoiceNo())
+                && StrUtil.isBlank(row.getInvoiceTitle()) && row.getPrice() == null
+                && StrUtil.isBlank(row.getInvoiceTime()) && StrUtil.isBlank(row.getRemark()));
+    }
+
+    /**
+     * 渚濇嵁瀵煎叆鏁版嵁鍒涘缓涓�寮犲紑绁ㄣ�傚鎴峰彇鍚堝悓鎵�灞炲鎴凤紱璐熻矗浜�=褰撳墠鐧诲綍鐢ㄦ埛锛涘鎵圭姸鎬�=鏈彁浜ゃ��
+     */
+    private String createInvoiceByImport(CrmInvoiceImportExcelVO row) {
+        // 1. 鍚堝悓鏍¢獙
+        if (StrUtil.isBlank(row.getContractNo())) {
+            throw new IllegalArgumentException("鍚堝悓缂栧彿涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆屽悎鍚岀紪鍙枫�嶅垪");
+        }
+        CrmContractDO contract = contractService.getContractByNo(row.getContractNo().trim());
+        if (contract == null) {
+            throw new IllegalArgumentException("鍚堝悓缂栧彿涓嶅瓨鍦細" + row.getContractNo().trim()
+                    + "銆傝鍏堝湪銆孋RM 鍚堝悓绠$悊銆嶄腑鍒涘缓鎴栧鍏ヨ鍚堝悓锛堢紪鍙烽渶涓庢ā鏉垮畬鍏ㄤ竴鑷达級锛屾垨淇敼妯℃澘涓殑鍚堝悓缂栧彿鍚庨噸鏂板鍏�");
+        }
+        if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) {
+            throw new IllegalArgumentException("鍚堝悓[" + contract.getNo() + "]褰撳墠鐘舵�佷负銆�"
+                    + CrmAuditStatusEnum.getNameByStatus(contract.getAuditStatus()) + "銆嶏紝鍙湁銆屽鏍搁�氳繃銆嶇殑鍚堝悓鎵嶈兘寮�绁ㄣ��"
+                    + "璇峰厛鍦ㄣ�孋RM 鍚堝悓绠$悊銆嶄腑瀹屾垚璇ュ悎鍚屽鎵癸紝鎴栨敼鐢ㄥ叾浠栧凡瀹℃牳閫氳繃鐨勫悎鍚岀紪鍙峰悗閲嶆柊瀵煎叆");
+        }
+
+        // 2. 寮�绁ㄩ噾棰濇牎楠�
+        if (row.getPrice() == null) {
+            throw new IllegalArgumentException("寮�绁ㄩ噾棰濅笉鑳戒负绌猴細璇峰~鍐欐ā鏉夸腑璇ヨ鐨勩�屽紑绁ㄩ噾棰�(鍏�)銆嶅垪");
+        }
+        if (row.getPrice().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new IllegalArgumentException("寮�绁ㄩ噾棰濆繀椤诲ぇ浜� 0锛屽綋鍓嶄负 "
+                    + row.getPrice().stripTrailingZeros().toPlainString() + "銆傝淇敼妯℃澘涓殑銆屽紑绁ㄩ噾棰�(鍏�)銆嶅悗閲嶆柊瀵煎叆");
+        }
+        BigDecimal remainingInvoicePrice = getRemainingInvoicePrice(contract, null);
+        if (row.getPrice().compareTo(remainingInvoicePrice) > 0) {
+            throw new IllegalArgumentException("寮�绁ㄩ噾棰濊秴杩囧悎鍚屽墿浣欏彲寮�绁ㄩ噾棰濓細鍚堝悓[" + contract.getNo()
+                    + "]鍓╀綑鍙紑绁ㄩ噾棰� " + remainingInvoicePrice.stripTrailingZeros().toPlainString() + " 鍏冿紝鏈瀵煎叆閲戦 "
+                    + row.getPrice().stripTrailingZeros().toPlainString()
+                    + " 鍏冦�傝璋冩暣妯℃澘涓殑銆屽紑绁ㄩ噾棰�(鍏�)銆嶏紝鎴栧厛鍦ㄧ郴缁熶腑鏍稿宸插紑绁ㄩ噾棰濆悗閲嶆柊瀵煎叆");
+        }
+
+        // 3. 鍙戠エ绫诲瀷鏍¢獙
+        Integer invoiceType = resolveInvoiceType(row.getInvoiceType());
+
+        // 4. 寮�绁ㄧ紪鍙凤細濉啓鍒欎娇鐢紝鐣欑┖鍒欒嚜鍔ㄧ敓鎴�
+        String no = StrUtil.isNotBlank(row.getNo()) ? row.getNo().trim()
+                : noRedisDAO.generate(CrmNoRedisDAO.INVOICE_PREFIX);
+        if (invoiceMapper.selectByNo(no) != null) {
+            throw new IllegalArgumentException("寮�绁ㄧ紪鍙峰凡瀛樺湪锛�" + no
+                    + "銆傝淇敼妯℃澘涓殑寮�绁ㄧ紪鍙凤紝鎴栧厛鍦ㄧ郴缁熶腑鍒犻櫎璇ョ紪鍙风殑寮�绁ㄥ悗閲嶆柊瀵煎叆");
+        }
+
+        // 5. 鍘婚噸锛氬悓涓�鍚堝悓涓嬶紝鍙戠エ鍙风爜锛堢暀绌烘椂鎸夊紑绁ㄩ噾棰濄�佸彂绁ㄦ姮澶淬�佸紑绁ㄦ棩鏈燂級鐩稿悓鐨勫紑绁ㄤ笉鍙噸澶嶅鍏�
+        // 寮�绁ㄦ棩鏈熺暀绌烘椂瑙嗕綔涓嶆瘮杈冭缁村害锛屾晠鎸夆�滄槸鍚﹀~鍐欌�濅紶 null锛屼笉鑳界敤 parseImportDateTime 鐨� now() 鍏滃簳鍊�
+        LocalDateTime invoiceTime = parseImportDateTime(row.getInvoiceTime());
+        CrmInvoiceDO duplicate = findDuplicateInvoice(contract.getId(), row,
+                StrUtil.isNotBlank(row.getInvoiceTime()) ? invoiceTime : null);
+        if (duplicate != null) {
+            throw duplicateExistingException(buildInvoiceKeyDesc(row), duplicate.getNo());
+        }
+
+        // 6. 淇濆瓨寮�绁�
+        CrmInvoiceDO invoice = new CrmInvoiceDO().setNo(no).setCustomerId(contract.getCustomerId())
+                .setContractId(contract.getId()).setOwnerUserId(getLoginUserId()).setInvoiceType(invoiceType)
+                .setInvoiceNo(row.getInvoiceNo()).setInvoiceTitle(row.getInvoiceTitle()).setPrice(row.getPrice())
+                .setInvoiceTime(invoiceTime).setRemark(row.getRemark())
+                .setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus());
+        invoiceMapper.insert(invoice);
+
+        // 7. 鍒涘缓鏁版嵁鏉冮檺锛堝惁鍒欏綋鍓嶇敤鎴风湅涓嶅埌璇ュ紑绁級
+        permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_INVOICE.getType())
+                .setBizId(invoice.getId()).setUserId(invoice.getOwnerUserId())
+                .setLevel(CrmPermissionLevelEnum.OWNER.getLevel()));
+        return invoice.getNo();
+    }
+
+    private Integer resolveInvoiceType(String text) {
+        List<String> labelList = DictFrameworkUtils.getDictDataLabelList(CRM_INVOICE_TYPE);
+        if (StrUtil.isBlank(text)) {
+            throw new IllegalArgumentException("鍙戠エ绫诲瀷涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆屽彂绁ㄧ被鍨嬨�嶅垪锛屽彲閫夊�硷細"
+                    + String.join("銆�", labelList));
+        }
+        String value = text.trim();
+        if (value.matches("\\d+") && DictFrameworkUtils.getDictDataValueList(CRM_INVOICE_TYPE).contains(value)) {
+            return Integer.valueOf(value);
+        }
+        String parsedValue = DictFrameworkUtils.parseDictDataValue(CRM_INVOICE_TYPE, value);
+        if (parsedValue == null) {
+            throw new IllegalArgumentException("鍙戠エ绫诲瀷涓嶅瓨鍦細" + value + "銆傛ā鏉夸腑銆屽彂绁ㄧ被鍨嬨�嶈濉啓浠ヤ笅涔嬩竴锛�"
+                    + String.join("銆�", labelList));
+        }
+        return Integer.valueOf(parsedValue);
+    }
+
+    private LocalDateTime parseImportDateTime(String text) {
+        if (StrUtil.isBlank(text)) {
+            return LocalDateTime.now();
+        }
+        String value = text.trim();
+        for (String pattern : new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss"}) {
+            try {
+                return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(pattern));
+            } catch (Exception ignored) {
+                // 缁х画灏濊瘯涓嬩竴涓牸寮�
+            }
+        }
+        for (String pattern : new String[]{"yyyy-MM-dd", "yyyy/M/d"}) {
+            try {
+                return java.time.LocalDate.parse(value, DateTimeFormatter.ofPattern(pattern)).atStartOfDay();
+            } catch (Exception ignored) {
+                // 缁х画灏濊瘯涓嬩竴涓牸寮�
+            }
+        }
+        throw new IllegalArgumentException("寮�绁ㄦ棩鏈熸牸寮忎笉姝g‘锛�" + text + "锛岀ず渚� 2026-01-01 10:00:00");
+    }
+
+    private String resolveImportErrorMessage(Exception ex) {
+        if (ex instanceof cn.iocoder.yudao.framework.common.exception.ServiceException serviceException) {
+            return serviceException.getMessage();
+        }
+        return StrUtil.blankToDefault(ex.getMessage(), "瀵煎叆澶辫触");
+    }
+
+    /**
+     * 瀵煎叆鍘婚噸閿細鍙戠エ鍙风爜濉啓鏃朵互 鍚堝悓 + 鍙戠エ鍙风爜 涓哄噯锛涚暀绌烘椂姣旇緝 鍚堝悓 + 寮�绁ㄩ噾棰� + 鍙戠エ鎶ご + 寮�绁ㄦ棩鏈�
+     */
+    private String buildInvoiceImportKey(CrmInvoiceImportExcelVO row) {
+        if (StrUtil.isNotBlank(row.getInvoiceNo())) {
+            return "no|" + StrUtil.trimToEmpty(row.getContractNo()) + "|" + row.getInvoiceNo().trim();
+        }
+        return "amt|" + StrUtil.trimToEmpty(row.getContractNo()) + "|"
+                + (row.getPrice() == null ? "" : row.getPrice().stripTrailingZeros().toPlainString()) + "|"
+                + StrUtil.trimToEmpty(row.getInvoiceTitle()) + "|" + StrUtil.trimToEmpty(row.getInvoiceTime());
+    }
+
+    private String buildInvoiceKeyDesc(CrmInvoiceImportExcelVO row) {
+        if (StrUtil.isNotBlank(row.getInvoiceNo())) {
+            return "鍚堝悓缂栧彿[" + StrUtil.trimToEmpty(row.getContractNo()) + "]銆佸彂绁ㄥ彿鐮乕" + row.getInvoiceNo().trim() + "]";
+        }
+        return "鍚堝悓缂栧彿[" + StrUtil.trimToEmpty(row.getContractNo()) + "]銆佸紑绁ㄩ噾棰漑"
+                + (row.getPrice() == null ? "鏈~鍐�" : row.getPrice().stripTrailingZeros().toPlainString() + " 鍏�")
+                + "]銆佸彂绁ㄦ姮澶碵" + StrUtil.blankToDefault(StrUtil.trimToNull(row.getInvoiceTitle()), "鏈~鍐�") + "]"
+                + (StrUtil.isBlank(row.getInvoiceTime()) ? "" : "銆佸紑绁ㄦ棩鏈焄" + row.getInvoiceTime().trim() + "]");
+    }
+
+    /**
+     * 鏌ヨ鍚屽悎鍚屼笅涓氬姟鏁版嵁瀹屽叏鐩稿悓鐨勫紑绁�
+     */
+    private CrmInvoiceDO findDuplicateInvoice(Long contractId, CrmInvoiceImportExcelVO row, LocalDateTime invoiceTime) {
+        for (CrmInvoiceDO old : invoiceMapper.selectListByContractId(contractId)) {
+            if (StrUtil.isNotBlank(row.getInvoiceNo())) {
+                if (StrUtil.equals(StrUtil.trimToEmpty(old.getInvoiceNo()), row.getInvoiceNo().trim())) {
+                    return old;
+                }
+                continue;
+            }
+            // 鍙戠エ鍙风爜鐣欑┖鏃讹紝姣旇緝寮�绁ㄩ噾棰濄�佸彂绁ㄦ姮澶达紱寮�绁ㄦ棩鏈熺暀绌哄垯涓嶆瘮杈冭缁村害
+            if (old.getPrice().compareTo(row.getPrice()) != 0
+                    || !StrUtil.equals(StrUtil.trimToEmpty(old.getInvoiceTitle()),
+                            StrUtil.trimToEmpty(row.getInvoiceTitle()))) {
+                continue;
+            }
+            if (invoiceTime != null && !invoiceTime.equals(old.getInvoiceTime())) {
+                continue;
+            }
+            return old;
+        }
+        return null;
+    }
+
+    private IllegalArgumentException duplicateRowException(int firstRowNo, String keyDesc) {
+        return new IllegalArgumentException("妫�娴嬪埌閲嶅瀵煎叆锛氭湰娆″鍏ョ殑绗� " + firstRowNo
+                + " 琛屼笌褰撳墠琛岀殑鏁版嵁瀹屽叏鐩稿悓锛�" + keyDesc + "锛夈�傝鍒犻櫎妯℃澘涓噸澶嶇殑琛屽悗閲嶆柊瀵煎叆");
+    }
+
+    private IllegalArgumentException duplicateExistingException(String keyDesc, String existingNo) {
+        return new IllegalArgumentException("妫�娴嬪埌閲嶅瀵煎叆锛�" + keyDesc + " 涓庡凡鏈夊紑绁ㄥ崟銆�" + existingNo
+                + "銆嶅畬鍏ㄧ浉鍚岋紙鍙兘鏄湰娆″鍏ュ墠闈㈢殑琛岋紝涔熷彲鑳芥槸绯荤粺涓凡鏈夊崟鎹級銆傝鍕块噸澶嶅鍏ワ紱濡傜‘闇�閲嶆柊瀵煎叆锛岃鍏堝湪銆岃惀閿�绠$悊-寮�绁ㄧ鐞嗐�嶄腑鍒犻櫎璇ュ紑绁ㄥ崟鍚庨噸璇�");
+    }
+
     private void validateInvoicePriceExceedsLimit(CrmInvoiceSaveReqVO reqVO) {
         // 1. 璁$畻鍓╀綑鍙紑绁ㄩ噾棰濓紝涓嶅寘鎷� reqVO 鑷韩锛堝鎵逛腑涔熷崰鐢ㄩ搴︼紝闃叉绱寮�绁ㄨ秴杩囧悎鍚岄噾棰濓級
         CrmContractDO contract = contractService.validateContract(reqVO.getContractId());
-        List<CrmInvoiceDO> invoices = invoiceMapper.selectListByContractIdAndStatus(reqVO.getContractId(),
-                List.of(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
-        if (reqVO.getId() != null) {
-            invoices.removeIf(invoice -> ObjectUtil.equal(invoice.getId(), reqVO.getId()));
-        }
-        BigDecimal notInvoicePrice = contract.getTotalPrice().subtract(
-                CollectionUtils.getSumValue(invoices, CrmInvoiceDO::getPrice, BigDecimal::add, BigDecimal.ZERO));
+        BigDecimal notInvoicePrice = getRemainingInvoicePrice(contract, reqVO.getId());
         // 2. 鏍¢獙閲戦鏄惁瓒呰繃
         if (reqVO.getPrice().compareTo(notInvoicePrice) > 0) {
             throw exception(INVOICE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, notInvoicePrice);
         }
     }
 
+    /**
+     * 璁$畻鍚堝悓鍓╀綑鍙紑绁ㄩ噾棰濓紙瀹℃壒涓�佸鏍搁�氳繃鐨勫紑绁ㄥ崰鐢ㄩ搴︼級
+     *
+     * @param contract          鍚堝悓
+     * @param excludeInvoiceId  闇�鎺掗櫎鐨勫紑绁ㄧ紪鍙凤紝鍙负绌�
+     */
+    private BigDecimal getRemainingInvoicePrice(CrmContractDO contract, Long excludeInvoiceId) {
+        List<CrmInvoiceDO> invoices = invoiceMapper.selectListByContractIdAndStatus(contract.getId(),
+                List.of(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus()));
+        if (excludeInvoiceId != null) {
+            invoices.removeIf(invoice -> ObjectUtil.equal(invoice.getId(), excludeInvoiceId));
+        }
+        return contract.getTotalPrice().subtract(
+                CollectionUtils.getSumValue(invoices, CrmInvoiceDO::getPrice, BigDecimal::add, BigDecimal.ZERO));
+    }
+
     private void validateRelationDataExists(CrmInvoiceSaveReqVO reqVO) {
         if (reqVO.getOwnerUserId() != null) {
             adminUserApi.validateUser(reqVO.getOwnerUserId()); // 鏍¢獙璐熻矗浜哄瓨鍦�
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/MesQcIpqcController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/MesQcIpqcController.java
index f07a326..eb24041 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/MesQcIpqcController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/MesQcIpqcController.java
@@ -13,9 +13,11 @@
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
 import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
 import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO;
@@ -39,8 +41,10 @@
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -139,6 +143,30 @@
         ExcelUtils.write(response, "杩囩▼妫�楠屽崟.xls", "鏁版嵁", MesQcQualityExportVO.class, exportList);
     }
 
+    @GetMapping("/get-import-template")
+    @Operation(summary = "鑾峰緱瀵煎叆杩囩▼妫�楠屽崟妯℃澘")
+    @PreAuthorize("@ss.hasPermission('mes:qc-ipqc:import')")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        List<MesQcIpqcImportExcelVO> list = Collections.singletonList(
+                MesQcIpqcImportExcelVO.builder()
+                        .name("绀轰緥-鐗╂枡A杩囩▼妫�楠�").type("棣栨").itemCode("WL0001").batchCode("BC20260101")
+                        .templateName("").checkQuantity(new BigDecimal("100"))
+                        .qualifiedQuantity(new BigDecimal("95")).unqualifiedQuantity(new BigDecimal("5"))
+                        .laborScrapQuantity(new BigDecimal("2")).materialScrapQuantity(new BigDecimal("2"))
+                        .otherScrapQuantity(new BigDecimal("1")).checkResult("鏍¢獙閫氳繃").inspectorName("")
+                        .inspectDate("2026-01-01 10:00:00").remark("绀轰緥鏁版嵁锛屽鍏ュ墠璇锋浛鎹�")
+                        .build());
+        ExcelUtils.write(response, "杩囩▼妫�楠屽崟瀵煎叆妯℃澘.xls", "杩囩▼妫�楠�", MesQcIpqcImportExcelVO.class, list);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "瀵煎叆杩囩▼妫�楠屽崟")
+    @PreAuthorize("@ss.hasPermission('mes:qc-ipqc:import')")
+    public CommonResult<MesQcImportRespVO> importIpqcExcel(@RequestParam("file") MultipartFile file) throws Exception {
+        List<MesQcIpqcImportExcelVO> list = ExcelUtils.read(file, MesQcIpqcImportExcelVO.class);
+        return success(ipqcService.importIpqcList(list));
+    }
+
     // ==================== 鎷兼帴 VO ====================
 
     private List<MesQcIpqcRespVO> buildIpqcRespVOList(List<MesQcIpqcDO> list) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcImportExcelVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcImportExcelVO.java
new file mode 100644
index 0000000..31ff80d
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcImportExcelVO.java
@@ -0,0 +1,91 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 绠$悊鍚庡彴 - 杩囩▼妫�楠屽崟瀵煎叆 Excel VO
+ *
+ * 璇存槑锛氫竴琛� = 涓�寮犺繃绋嬫楠屽崟銆備骇鍝佺墿鏂欐寜鐗╂枡缂栫爜鍖归厤绯荤粺宸叉湁鏁版嵁锛堢墿鏂欐潵鑷� MDM锛夛紝鎵瑰彿蹇呭~锛�
+ * 璐ㄦ鏂规鎸夊悕绉板尮閰嶇郴缁熷凡鏈夋暟鎹紱妫�娴嬩汉鍛樼暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涙楠屽崟缂栧彿鐣欑┖鑷姩鐢熸垚锛涚姸鎬佸浐瀹氫负銆屽緟妫�楠屻�嶃��
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Schema(description = "绠$悊鍚庡彴 - 杩囩▼妫�楠屽崟瀵煎叆 Excel VO")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MesQcIpqcImportExcelVO {
+
+    @ExcelProperty("妫�楠屽崟缂栧彿")
+    @Schema(description = "妫�楠屽崟缂栧彿锛岀暀绌哄垯鑷姩鐢熸垚", example = "IPQC20260101000001")
+    private String code;
+
+    @ExcelProperty("妫�楠屽崟鍚嶇О")
+    @Schema(description = "妫�楠屽崟鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "XX鎵瑰彿杩囩▼妫�楠�")
+    private String name;
+
+    @ExcelProperty("妫�楠岀被鍨�")
+    @Schema(description = "妫�楠岀被鍨嬶紝濉啓瀛楀吀 mes_ipqc_type 鐨勫悕绉帮紝濡� 棣栨", requiredMode = Schema.RequiredMode.REQUIRED, example = "棣栨")
+    private String type;
+
+    @ExcelProperty("浜у搧鐗╂枡缂栫爜")
+    @Schema(description = "浜у搧鐗╂枡缂栫爜锛岄渶涓庣墿鏂欑鐞嗕腑鐨勭紪鐮佷竴鑷�", requiredMode = Schema.RequiredMode.REQUIRED, example = "WL001")
+    private String itemCode;
+
+    @ExcelProperty("鎵瑰彿")
+    @Schema(description = "鎵瑰彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "BC20260101")
+    private String batchCode;
+
+    @ExcelProperty("璐ㄦ鏂规")
+    @Schema(description = "璐ㄦ鏂规鍚嶇О锛岀暀绌哄垯鎸夌墿鏂欒嚜鍔ㄥ尮閰�", example = "杩囩▼妫�楠岄�氱敤鏂规")
+    private String templateName;
+
+    @ExcelProperty("妫�娴嬫暟閲�")
+    @Schema(description = "妫�娴嬫暟閲忥紝鐣欑┖榛樿绛変簬鍚堟牸鍝佹暟閲忎笌涓嶅悎鏍煎搧鏁伴噺涔嬪拰", example = "100")
+    private BigDecimal checkQuantity;
+
+    @ExcelProperty("鍚堟牸鍝佹暟閲�")
+    @Schema(description = "鍚堟牸鍝佹暟閲忥紝涓嶈兘灏忎簬 0", requiredMode = Schema.RequiredMode.REQUIRED, example = "95")
+    private BigDecimal qualifiedQuantity;
+
+    @ExcelProperty("涓嶅悎鏍煎搧鏁伴噺")
+    @Schema(description = "涓嶅悎鏍煎搧鏁伴噺锛屼笉鑳藉皬浜� 0", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    private BigDecimal unqualifiedQuantity;
+
+    @ExcelProperty("宸ュ簾鏁伴噺")
+    @Schema(description = "宸ュ簾鏁伴噺锛屼笉鑳藉皬浜� 0锛涚暀绌烘寜 0 澶勭悊", example = "2")
+    private BigDecimal laborScrapQuantity;
+
+    @ExcelProperty("鏂欏簾鏁伴噺")
+    @Schema(description = "鏂欏簾鏁伴噺锛屼笉鑳藉皬浜� 0锛涚暀绌烘寜 0 澶勭悊", example = "2")
+    private BigDecimal materialScrapQuantity;
+
+    @ExcelProperty("鍏朵粬搴熷搧鏁伴噺")
+    @Schema(description = "鍏朵粬搴熷搧鏁伴噺锛屼笉鑳藉皬浜� 0锛涚暀绌烘寜 0 澶勭悊", example = "1")
+    private BigDecimal otherScrapQuantity;
+
+    @ExcelProperty("妫�娴嬬粨鏋�")
+    @Schema(description = "妫�娴嬬粨鏋滐紝濉啓瀛楀吀 mes_qc_check_result 鐨勫悕绉帮紝濡� 鏍¢獙閫氳繃", example = "鏍¢獙閫氳繃")
+    private String checkResult;
+
+    @ExcelProperty("妫�娴嬩汉鍛�")
+    @Schema(description = "妫�娴嬩汉鍛樺鍚嶏紝鐣欑┖鍙栧綋鍓嶇櫥褰曠敤鎴�", example = "寮犱笁")
+    private String inspectorName;
+
+    @ExcelProperty("妫�娴嬫棩鏈�")
+    @Schema(description = "妫�娴嬫棩鏈燂紝鏍煎紡濡� 2026-01-01 10:00:00锛涚暀绌鸿〃绀烘湭濉啓", example = "2026-01-01 10:00:00")
+    private String inspectDate;
+
+    @ExcelProperty("澶囨敞")
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java
index 8a9f255..a9ff570 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java
@@ -9,9 +9,11 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
@@ -41,8 +43,10 @@
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -144,6 +148,30 @@
         ExcelUtils.write(response, "鏉ユ枡妫�楠屽崟.xls", "鏁版嵁", MesQcQualityExportVO.class, exportList);
     }
 
+    @GetMapping("/get-import-template")
+    @Operation(summary = "鑾峰緱瀵煎叆鏉ユ枡妫�楠屽崟妯℃澘")
+    @PreAuthorize("@ss.hasPermission('mes:qc-iqc:import')")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        List<MesQcIqcImportExcelVO> list = Collections.singletonList(
+                MesQcIqcImportExcelVO.builder()
+                        .name("绀轰緥-鐗╂枡A鏉ユ枡妫�楠�").itemCode("WL0001").vendorName("绀轰緥渚涘簲鍟嗘湁闄愬叕鍙�")
+                        .vendorBatch("VB20260101").templateName("")
+                        .receivedQuantity(new BigDecimal("100")).qualifiedQuantity(new BigDecimal("99"))
+                        .unqualifiedQuantity(new BigDecimal("1")).checkResult("鏍¢獙閫氳繃").inspectorName("")
+                        .inspectDate("2026-01-01 10:00:00").receiveDate("2026-01-01 09:00:00")
+                        .remark("绀轰緥鏁版嵁锛屽鍏ュ墠璇锋浛鎹�")
+                        .build());
+        ExcelUtils.write(response, "鏉ユ枡妫�楠屽崟瀵煎叆妯℃澘.xls", "鏉ユ枡妫�楠�", MesQcIqcImportExcelVO.class, list);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "瀵煎叆鏉ユ枡妫�楠屽崟")
+    @PreAuthorize("@ss.hasPermission('mes:qc-iqc:import')")
+    public CommonResult<MesQcImportRespVO> importIqcExcel(@RequestParam("file") MultipartFile file) throws Exception {
+        List<MesQcIqcImportExcelVO> list = ExcelUtils.read(file, MesQcIqcImportExcelVO.class);
+        return success(iqcService.importIqcList(list));
+    }
+
     // ==================== 鎷兼帴 VO ====================
 
     private List<MesQcIqcRespVO> buildIqcRespVOList(List<MesQcIqcDO> list) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcImportExcelVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcImportExcelVO.java
new file mode 100644
index 0000000..17a9204
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcImportExcelVO.java
@@ -0,0 +1,83 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 绠$悊鍚庡彴 - 鏉ユ枡妫�楠屽崟瀵煎叆 Excel VO
+ *
+ * 璇存槑锛氫竴琛� = 涓�寮犳潵鏂欐楠屽崟銆傜墿鏂欍�佷緵搴斿晢銆佽川妫�鏂规鍧囨寜鍚嶇О/缂栫爜鍖归厤绯荤粺宸叉湁鏁版嵁锛�
+ * 妫�娴嬩汉鍛樼暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涙楠屽崟缂栧彿鐣欑┖鑷姩鐢熸垚锛涚姸鎬佸浐瀹氫负銆屽緟妫�楠屻�嶃��
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Schema(description = "绠$悊鍚庡彴 - 鏉ユ枡妫�楠屽崟瀵煎叆 Excel VO")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MesQcIqcImportExcelVO {
+
+    @ExcelProperty("妫�楠屽崟缂栧彿")
+    @Schema(description = "妫�楠屽崟缂栧彿锛岀暀绌哄垯鑷姩鐢熸垚", example = "IQC20260101000001")
+    private String code;
+
+    @ExcelProperty("妫�楠屽崟鍚嶇О")
+    @Schema(description = "妫�楠屽崟鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鐗╂枡A鏉ユ枡妫�楠�")
+    private String name;
+
+    @ExcelProperty("浜у搧鐗╂枡缂栫爜")
+    @Schema(description = "浜у搧鐗╂枡缂栫爜锛岄渶涓庣墿鏂欑鐞嗕腑鐨勭紪鐮佷竴鑷�", requiredMode = Schema.RequiredMode.REQUIRED, example = "WL001")
+    private String itemCode;
+
+    @ExcelProperty("渚涘簲鍟嗗悕绉�")
+    @Schema(description = "渚涘簲鍟嗗悕绉帮紝闇�涓庝緵搴斿晢绠$悊涓殑鍚嶇О涓�鑷�", example = "绀轰緥渚涘簲鍟嗘湁闄愬叕鍙�")
+    private String vendorName;
+
+    @ExcelProperty("渚涘簲鍟嗘壒娆″彿")
+    @Schema(description = "渚涘簲鍟嗘壒娆″彿", example = "VB20260101")
+    private String vendorBatch;
+
+    @ExcelProperty("璐ㄦ鏂规")
+    @Schema(description = "璐ㄦ鏂规鍚嶇О锛岀暀绌哄垯鎸夌墿鏂欒嚜鍔ㄥ尮閰�", example = "鏉ユ枡妫�楠岄�氱敤鏂规")
+    private String templateName;
+
+    @ExcelProperty("鏈鎺ユ敹鏁伴噺")
+    @Schema(description = "鏈鎺ユ敹鏁伴噺锛岀暀绌洪粯璁ょ瓑浜庡悎鏍煎搧鏁伴噺涓庝笉鍚堟牸鍝佹暟閲忎箣鍜�", example = "100")
+    private BigDecimal receivedQuantity;
+
+    @ExcelProperty("鍚堟牸鍝佹暟閲�")
+    @Schema(description = "鍚堟牸鍝佹暟閲忥紝涓嶈兘灏忎簬 0", requiredMode = Schema.RequiredMode.REQUIRED, example = "99")
+    private BigDecimal qualifiedQuantity;
+
+    @ExcelProperty("涓嶅悎鏍煎搧鏁伴噺")
+    @Schema(description = "涓嶅悎鏍煎搧鏁伴噺锛屼笉鑳藉皬浜� 0", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private BigDecimal unqualifiedQuantity;
+
+    @ExcelProperty("妫�娴嬬粨鏋�")
+    @Schema(description = "妫�娴嬬粨鏋滐紝濉啓瀛楀吀 mes_qc_check_result 鐨勫悕绉帮紝濡� 鏍¢獙閫氳繃", example = "鏍¢獙閫氳繃")
+    private String checkResult;
+
+    @ExcelProperty("妫�娴嬩汉鍛�")
+    @Schema(description = "妫�娴嬩汉鍛樺鍚嶏紝鐣欑┖鍙栧綋鍓嶇櫥褰曠敤鎴�", example = "寮犱笁")
+    private String inspectorName;
+
+    @ExcelProperty("妫�娴嬫棩鏈�")
+    @Schema(description = "妫�娴嬫棩鏈燂紝鏍煎紡濡� 2026-01-01 10:00:00", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-01-01 10:00:00")
+    private String inspectDate;
+
+    @ExcelProperty("鏉ユ枡鏃ユ湡")
+    @Schema(description = "鏉ユ枡鏃ユ湡锛屾牸寮忓 2026-01-01 10:00:00", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-01-01 10:00:00")
+    private String receiveDate;
+
+    @ExcelProperty("澶囨敞")
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/MesQcInspectionLedgerController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/MesQcInspectionLedgerController.java
new file mode 100644
index 0000000..6a43a0b
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/MesQcInspectionLedgerController.java
@@ -0,0 +1,113 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ledger;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerExportVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerPageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerRespVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO;
+import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
+import cn.iocoder.yudao.module.mes.service.qc.ledger.MesQcInspectionLedgerService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "绠$悊鍚庡彴 - MES 妫�娴嬬粨鏋滃彴璐�")
+@RestController
+@RequestMapping("/mes/qc/inspection-ledger")
+@Validated
+public class MesQcInspectionLedgerController {
+
+    @Resource
+    private MesQcInspectionLedgerService ledgerService;
+
+    @GetMapping("/page")
+    @Operation(summary = "鑾峰緱妫�娴嬬粨鏋滃彴璐﹀垎椤碉紙鍥涚被妫�楠屽崟缁熶竴鏌ヨ锛�")
+    @PreAuthorize("@ss.hasPermission('mes:qc-inspection-ledger:query')")
+    public CommonResult<PageResult<MesQcInspectionLedgerRespVO>> getInspectionLedgerPage(
+            @Valid MesQcInspectionLedgerPageReqVO pageReqVO) {
+        return success(ledgerService.getInspectionLedgerPage(pageReqVO));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "瀵煎嚭妫�娴嬬粨鏋滃彴璐� Excel")
+    @PreAuthorize("@ss.hasPermission('mes:qc-inspection-ledger:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportInspectionLedgerExcel(@Valid MesQcInspectionLedgerPageReqVO pageReqVO,
+                                            HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<MesQcInspectionLedgerRespVO> list = ledgerService.getInspectionLedgerPage(pageReqVO).getList();
+        ExcelUtils.write(response, "妫�娴嬬粨鏋滃彴璐�.xls", "妫�娴嬬粨鏋滃彴璐�",
+                MesQcInspectionLedgerExportVO.class, buildExportList(list));
+    }
+
+    private List<MesQcInspectionLedgerExportVO> buildExportList(List<MesQcInspectionLedgerRespVO> list) {
+        if (CollUtil.isEmpty(list)) {
+            return new ArrayList<>();
+        }
+        List<MesQcInspectionLedgerExportVO> exportList = new ArrayList<>(list.size());
+        for (MesQcInspectionLedgerRespVO vo : list) {
+            MesQcInspectionLedgerExportVO export = new MesQcInspectionLedgerExportVO();
+            export.setQcTypeName(qcTypeName(vo.getQcType()));
+            export.setCode(vo.getCode());
+            export.setName(vo.getName());
+            export.setItemCode(vo.getItemCode());
+            export.setItemName(vo.getItemName());
+            export.setItemSpecification(vo.getItemSpecification());
+            export.setBatchNo(vo.getBatchNo());
+            export.setIndicatorName(vo.getIndicatorName());
+            export.setIndicatorValue(vo.getIndicatorValue());
+            export.setJudgementBasis(vo.getJudgementBasis());
+            export.setCheckResultText(parseDictLabel(DictTypeConstants.MES_QC_CHECK_RESULT, vo.getCheckResult()));
+            export.setCheckQuantity(MesQcQualityExportVO.formatDecimal(vo.getCheckQuantity()));
+            export.setQualifiedQuantity(MesQcQualityExportVO.formatDecimal(vo.getQualifiedQuantity()));
+            export.setUnqualifiedQuantity(MesQcQualityExportVO.formatDecimal(vo.getUnqualifiedQuantity()));
+            export.setInspectorName(vo.getInspectorName());
+            export.setInspectDate(MesQcQualityExportVO.formatDateTime(vo.getInspectDate()));
+            export.setStatusText(parseDictLabel(DictTypeConstants.MES_ORDER_STATUS, vo.getStatus()));
+            export.setRemark(vo.getRemark());
+            exportList.add(export);
+        }
+        return exportList;
+    }
+
+    private static String qcTypeName(Integer qcType) {
+        for (MesQcTypeEnum e : MesQcTypeEnum.values()) {
+            if (Objects.equals(e.getType(), qcType)) {
+                return e.getName();
+            }
+        }
+        return "";
+    }
+
+    private static String parseDictLabel(String dictType, Integer value) {
+        if (value == null) {
+            return "";
+        }
+        String label = DictFrameworkUtils.parseDictDataLabel(dictType, String.valueOf(value));
+        return StrUtil.nullToEmpty(label);
+    }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerExportVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerExportVO.java
new file mode 100644
index 0000000..6ea2e96
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerExportVO.java
@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+/**
+ * MES 妫�娴嬬粨鏋滃彴璐﹀鍑� Excel VO
+ *
+ * <p>涓�琛� = 涓�涓娴嬮」缁撴灉锛堟楠屽崟鏈綍鍏ユ娴嬮」鏃惰鍗曞崰涓�琛岋紝妫�娴嬮」鐩稿叧鍒椾负绌猴級锛岀敤浜庢煡璇€�佹墦鍗颁笌瀛樻。銆�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Data
+public class MesQcInspectionLedgerExportVO {
+
+    @ExcelProperty("鍒嗙被")
+    private String qcTypeName;
+
+    @ExcelProperty("妫�楠屽崟缂栧彿")
+    private String code;
+
+    @ExcelProperty("妫�楠屽崟鍚嶇О")
+    private String name;
+
+    @ExcelProperty("鐗╂枡缂栫爜")
+    private String itemCode;
+
+    @ExcelProperty("鐗╂枡鍚嶇О")
+    private String itemName;
+
+    @ExcelProperty("瑙勬牸鍨嬪彿")
+    private String itemSpecification;
+
+    @ExcelProperty("鎵规鍙�")
+    private String batchNo;
+
+    @ExcelProperty("妫�娴嬮」")
+    private String indicatorName;
+
+    @ExcelProperty("妫�娴嬪��")
+    private String indicatorValue;
+
+    @ExcelProperty("鍒ゅ畾渚濇嵁")
+    private String judgementBasis;
+
+    @ExcelProperty("妫�娴嬬粨鏋�")
+    private String checkResultText;
+
+    @ExcelProperty("妫�娴嬫暟閲�")
+    private String checkQuantity;
+
+    @ExcelProperty("鍚堟牸鏁伴噺")
+    private String qualifiedQuantity;
+
+    @ExcelProperty("涓嶅悎鏍兼暟閲�")
+    private String unqualifiedQuantity;
+
+    @ExcelProperty("妫�娴嬩汉鍛�")
+    private String inspectorName;
+
+    @ExcelProperty("妫�娴嬫棩鏈�")
+    private String inspectDate;
+
+    @ExcelProperty("鍗曟嵁鐘舵��")
+    private String statusText;
+
+    @ExcelProperty("澶囨敞")
+    private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerPageReqVO.java
new file mode 100644
index 0000000..f26551d
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerPageReqVO.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 妫�娴嬬粨鏋滃彴璐﹀垎椤� Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MesQcInspectionLedgerPageReqVO extends PageParam {
+
+    @Schema(description = "鍒嗙被锛堟楠岀被鍨嬶級锛�1 鏉ユ枡妫�楠� / 2 杩囩▼妫�楠� / 3 鍑鸿揣妫�楠� / 4 閫�璐ф楠岋紝涓嶄紶涓哄叏閮�", example = "1")
+    private Integer qcType;
+
+    @Schema(description = "妫�娴嬫棩鏈熷尯闂达紝浼� [寮�濮嬫椂闂�, 缁撴潫鏃堕棿]",
+            example = "[\"2026-01-01 00:00:00\", \"2026-12-31 23:59:59\"]")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private LocalDateTime[] inspectDate;
+
+    @Schema(description = "妫�楠屽崟缂栧彿锛堟ā绯婂尮閰嶏級", example = "IQC1787563511197")
+    private String code;
+
+    @Schema(description = "鐗╂枡缂栧彿", example = "28")
+    private Long itemId;
+
+    @Schema(description = "鎵规鍙凤紙妯$硦鍖归厤锛涙潵鏂欐楠屽彇渚涘簲鍟嗘壒娆″彿锛岃繃绋�/鍑鸿揣/閫�璐ф楠屽彇鎵规鍙凤級", example = "20260920a")
+    private String batchNo;
+
+    @Schema(description = "妫�娴嬬粨鏋�", example = "1")
+    private Integer checkResult;
+
+    @Schema(description = "妫�娴嬩汉鍛樼紪鍙�", example = "1")
+    private Long inspectorUserId;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerRespVO.java
new file mode 100644
index 0000000..4db3aa2
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ledger/vo/MesQcInspectionLedgerRespVO.java
@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 妫�娴嬬粨鏋滃彴璐� Response VO")
+@Data
+public class MesQcInspectionLedgerRespVO {
+
+    // ========== 妫�楠屽崟 ==========
+
+    @Schema(description = "鍒嗙被锛堟楠岀被鍨嬶級锛�1 鏉ユ枡妫�楠� / 2 杩囩▼妫�楠� / 3 鍑鸿揣妫�楠� / 4 閫�璐ф楠岋紱瀛楀吀 mes_qc_type", example = "1")
+    private Integer qcType;
+
+    @Schema(description = "妫�楠屽崟 ID", example = "1")
+    private Long qcId;
+
+    @Schema(description = "妫�楠屽崟缂栧彿", example = "IQC1787563511197")
+    private String code;
+
+    @Schema(description = "妫�楠屽崟鍚嶇О", example = "AN1787563511157-鐗╂枡妫�楠�")
+    private String name;
+
+    @Schema(description = "鍗曟嵁鐘舵�侊紱瀛楀吀 mes_order_status", example = "4")
+    private Integer status;
+
+    // ========== 鐗╂枡 ==========
+
+    @Schema(description = "鐗╂枡缂栧彿", example = "28")
+    private Long itemId;
+
+    @Schema(description = "鐗╂枡缂栫爜", example = "WL0001")
+    private String itemCode;
+
+    @Schema(description = "鐗╂枡鍚嶇О", example = "鐗╂枡A")
+    private String itemName;
+
+    @Schema(description = "瑙勬牸鍨嬪彿", example = "100mm*50mm")
+    private String itemSpecification;
+
+    @Schema(description = "璁¢噺鍗曚綅鍚嶇О", example = "涓�")
+    private String unitName;
+
+    @Schema(description = "鎵规鍙凤紙鏉ユ枡妫�楠屽彇渚涘簲鍟嗘壒娆″彿锛岃繃绋�/鍑鸿揣/閫�璐ф楠屽彇鎵规鍙凤級", example = "20260920a")
+    private String batchNo;
+
+    // ========== 鏁伴噺涓庣粨鏋� ==========
+
+    @Schema(description = "妫�娴嬫暟閲�", example = "100.00")
+    private BigDecimal checkQuantity;
+
+    @Schema(description = "鍚堟牸鏁伴噺", example = "99.00")
+    private BigDecimal qualifiedQuantity;
+
+    @Schema(description = "涓嶅悎鏍兼暟閲�", example = "1.00")
+    private BigDecimal unqualifiedQuantity;
+
+    @Schema(description = "妫�娴嬬粨鏋滐紱瀛楀吀 mes_qc_check_result", example = "1")
+    private Integer checkResult;
+
+    @Schema(description = "妫�娴嬫棩鏈�")
+    private LocalDateTime inspectDate;
+
+    @Schema(description = "妫�娴嬩汉鍛樼紪鍙�", example = "1")
+    private Long inspectorUserId;
+
+    @Schema(description = "妫�娴嬩汉鍛樻樀绉�", example = "寮犱笁")
+    private String inspectorName;
+
+    // ========== 妫�娴嬮」缁撴灉锛堜竴鏉℃槑缁嗕竴琛岋紱妫�楠屽崟鏈綍鍏ユ娴嬮」鏃惰缁勫瓧娈典负绌猴級 ==========
+
+    @Schema(description = "妫�娴嬮」缂栧彿", example = "2")
+    private Long indicatorId;
+
+    @Schema(description = "妫�娴嬮」鍚嶇О", example = "闀垮害")
+    private String indicatorName;
+
+    @Schema(description = "妫�娴嬪��", example = "15")
+    private String indicatorValue;
+
+    @Schema(description = "妫�娴嬮」澶囨敞", example = "鎶芥")
+    private String indicatorRemark;
+
+    @Schema(description = "鍒ゅ畾渚濇嵁锛岀敱妫�娴嬮」鏍囧噯鍊�/璇樊涓婁笅闄愭嫾瑁�", example = "10~20")
+    private String judgementBasis;
+
+    @Schema(description = "妫�娴嬮」鏍囧噯鍊硷紙鍒ゅ畾渚濇嵁鍘熷鍊硷紝鍓嶇灞曠ず鐢� judgementBasis 鍗冲彲锛�", example = "15")
+    private BigDecimal standardValue;
+
+    @Schema(description = "妫�娴嬮」璇樊涓嬮檺锛堝垽瀹氫緷鎹師濮嬪�硷級", example = "10")
+    private BigDecimal minThreshold;
+
+    @Schema(description = "妫�娴嬮」璇樊涓婇檺锛堝垽瀹氫緷鎹師濮嬪�硷級", example = "20")
+    private BigDecimal maxThreshold;
+
+    @Schema(description = "妫�楠屽崟澶囨敞")
+    private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java
index c77e06e..90c5f6f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java
@@ -9,9 +9,11 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO;
 import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
 import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
@@ -41,8 +43,10 @@
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -143,6 +147,30 @@
         ExcelUtils.write(response, "鍑鸿揣妫�楠屽崟.xls", "鏁版嵁", MesQcQualityExportVO.class, exportList);
     }
 
+    @GetMapping("/get-import-template")
+    @Operation(summary = "鑾峰緱瀵煎叆鍑鸿揣妫�楠屽崟妯℃澘")
+    @PreAuthorize("@ss.hasPermission('mes:qc-oqc:import')")
+    public void importTemplate(HttpServletResponse response) throws IOException {
+        List<MesQcOqcImportExcelVO> list = Collections.singletonList(
+                MesQcOqcImportExcelVO.builder()
+                        .name("绀轰緥-鐗╂枡A鍑鸿揣妫�楠�").itemCode("WL0001").clientName("绀轰緥瀹㈡埛鏈夐檺鍏徃")
+                        .batchCode("BC20260101").templateName("")
+                        .outQuantity(new BigDecimal("100")).checkQuantity(new BigDecimal("10"))
+                        .qualifiedQuantity(new BigDecimal("9")).unqualifiedQuantity(new BigDecimal("1"))
+                        .checkResult("鏍¢獙閫氳繃").inspectorName("").inspectDate("2026-01-01 10:00:00")
+                        .outDate("2026-01-01 09:00:00").remark("绀轰緥鏁版嵁锛屽鍏ュ墠璇锋浛鎹�")
+                        .build());
+        ExcelUtils.write(response, "鍑鸿揣妫�楠屽崟瀵煎叆妯℃澘.xls", "鍑鸿揣妫�楠�", MesQcOqcImportExcelVO.class, list);
+    }
+
+    @PostMapping("/import")
+    @Operation(summary = "瀵煎叆鍑鸿揣妫�楠屽崟")
+    @PreAuthorize("@ss.hasPermission('mes:qc-oqc:import')")
+    public CommonResult<MesQcImportRespVO> importOqcExcel(@RequestParam("file") MultipartFile file) throws Exception {
+        List<MesQcOqcImportExcelVO> list = ExcelUtils.read(file, MesQcOqcImportExcelVO.class);
+        return success(oqcService.importOqcList(list));
+    }
+
     // ==================== 鎷兼帴 VO ====================
 
     private List<MesQcOqcRespVO> buildOqcRespVOList(List<MesQcOqcDO> list) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcImportExcelVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcImportExcelVO.java
new file mode 100644
index 0000000..820848c
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcImportExcelVO.java
@@ -0,0 +1,87 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+/**
+ * 绠$悊鍚庡彴 - 鍑鸿揣妫�楠屽崟瀵煎叆 Excel VO
+ *
+ * 璇存槑锛氫竴琛� = 涓�寮犲嚭璐ф楠屽崟銆傜墿鏂欍�佸鎴枫�佽川妫�鏂规鎸夌紪鐮�/鍚嶇О鍖归厤绯荤粺宸叉湁鏁版嵁锛�
+ * 妫�娴嬩汉鍛樼暀绌哄彇褰撳墠鐧诲綍鐢ㄦ埛锛涙楠屽崟缂栧彿鐣欑┖鑷姩鐢熸垚锛涚姸鎬佸浐瀹氫负銆屽緟妫�楠屻�嶃��
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Schema(description = "绠$悊鍚庡彴 - 鍑鸿揣妫�楠屽崟瀵煎叆 Excel VO")
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class MesQcOqcImportExcelVO {
+
+    @ExcelProperty("妫�楠屽崟缂栧彿")
+    @Schema(description = "妫�楠屽崟缂栧彿锛岀暀绌哄垯鑷姩鐢熸垚", example = "OQC20260101000001")
+    private String code;
+
+    @ExcelProperty("妫�楠屽崟鍚嶇О")
+    @Schema(description = "妫�楠屽崟鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鐗╂枡A鍑鸿揣妫�楠�")
+    private String name;
+
+    @ExcelProperty("浜у搧鐗╂枡缂栫爜")
+    @Schema(description = "浜у搧鐗╂枡缂栫爜锛岄渶涓庣墿鏂欑鐞嗕腑鐨勭紪鐮佷竴鑷�", requiredMode = Schema.RequiredMode.REQUIRED, example = "WL001")
+    private String itemCode;
+
+    @ExcelProperty("瀹㈡埛鍚嶇О")
+    @Schema(description = "瀹㈡埛鍚嶇О锛岄渶涓� CRM 瀹㈡埛绠$悊涓殑鍚嶇О涓�鑷达紱鐣欑┖琛ㄧず涓嶅睘浜庝换浣曞鎴�", example = "绀轰緥瀹㈡埛鏈夐檺鍏徃")
+    private String clientName;
+
+    @ExcelProperty("鎵规鍙�")
+    @Schema(description = "鎵规鍙�", example = "BC20260101")
+    private String batchCode;
+
+    @ExcelProperty("璐ㄦ鏂规")
+    @Schema(description = "璐ㄦ鏂规鍚嶇О锛岀暀绌哄垯鎸夌墿鏂欒嚜鍔ㄥ尮閰�", example = "鍑鸿揣妫�楠岄�氱敤鏂规")
+    private String templateName;
+
+    @ExcelProperty("鏈鍑鸿揣鏁伴噺")
+    @Schema(description = "鏈鍑鸿揣鏁伴噺锛岀暀绌鸿〃绀烘湭濉啓锛涘~鍐欐椂涓嶈兘灏忎簬 0", example = "100")
+    private BigDecimal outQuantity;
+
+    @ExcelProperty("妫�娴嬫暟閲�")
+    @Schema(description = "妫�娴嬫暟閲忥紝鐣欑┖榛樿绛変簬鍚堟牸鍝佹暟閲忎笌涓嶅悎鏍煎搧鏁伴噺涔嬪拰", example = "10")
+    private BigDecimal checkQuantity;
+
+    @ExcelProperty("鍚堟牸鍝佹暟閲�")
+    @Schema(description = "鍚堟牸鍝佹暟閲忥紝涓嶈兘灏忎簬 0", requiredMode = Schema.RequiredMode.REQUIRED, example = "9")
+    private BigDecimal qualifiedQuantity;
+
+    @ExcelProperty("涓嶅悎鏍煎搧鏁伴噺")
+    @Schema(description = "涓嶅悎鏍煎搧鏁伴噺锛屼笉鑳藉皬浜� 0", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private BigDecimal unqualifiedQuantity;
+
+    @ExcelProperty("妫�娴嬬粨鏋�")
+    @Schema(description = "妫�娴嬬粨鏋滐紝濉啓瀛楀吀 mes_qc_check_result 鐨勫悕绉帮紝濡� 鏍¢獙閫氳繃", example = "鏍¢獙閫氳繃")
+    private String checkResult;
+
+    @ExcelProperty("妫�娴嬩汉鍛�")
+    @Schema(description = "妫�娴嬩汉鍛樺鍚嶏紝鐣欑┖鍙栧綋鍓嶇櫥褰曠敤鎴�", example = "寮犱笁")
+    private String inspectorName;
+
+    @ExcelProperty("妫�娴嬫棩鏈�")
+    @Schema(description = "妫�娴嬫棩鏈燂紝鏍煎紡濡� 2026-01-01 10:00:00", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-01-01 10:00:00")
+    private String inspectDate;
+
+    @ExcelProperty("鍑鸿揣鏃ユ湡")
+    @Schema(description = "鍑鸿揣鏃ユ湡锛屾牸寮忓 2026-01-01 10:00:00锛涚暀绌鸿〃绀烘湭濉啓", example = "2026-01-01 10:00:00")
+    private String outDate;
+
+    @ExcelProperty("澶囨敞")
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    private String remark;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/vo/MesQcImportRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/vo/MesQcImportRespVO.java
new file mode 100644
index 0000000..273b3e6
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/vo/MesQcImportRespVO.java
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 绠$悊鍚庡彴 - 璐ㄦ鍗曞鍏� Response VO锛圛QC / IPQC / OQC 鍏辩敤锛�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Schema(description = "绠$悊鍚庡彴 - 璐ㄦ鍗曞鍏� Response VO")
+@Data
+@Builder
+public class MesQcImportRespVO {
+
+    @Schema(description = "瀵煎叆鎴愬姛鐨勬楠屽崟缂栧彿鏁扮粍", example = "IQC20260101000001")
+    private List<String> createList;
+
+    @Schema(description = "瀵煎叆澶辫触闆嗗悎锛宬ey 涓鸿鏍囪瘑锛堟楠屽崟缂栧彿鎴栫 N 琛岋級锛寁alue 涓哄け璐ュ師鍥�")
+    private Map<String, String> failureList;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ipqc/MesQcIpqcMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ipqc/MesQcIpqcMapper.java
index 24185c1..f3a4623 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ipqc/MesQcIpqcMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ipqc/MesQcIpqcMapper.java
@@ -7,6 +7,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -41,4 +42,18 @@
                 .orderByDesc(MesQcIpqcDO::getId));
     }
 
+    /**
+     * 鎸夊鍏ュ幓閲嶉敭鏌ヨ锛氬悓涓� 鐗╂枡 + 鎵瑰彿 + 妫�楠岀被鍨� + 妫�娴嬫棩鏈� 瑙嗕负鍚屼竴鎵硅繃绋嬫楠屾暟鎹�
+     */
+    default List<MesQcIpqcDO> selectListByImportKey(Long itemId, String batchCode, Integer type,
+                                                    LocalDateTime inspectDate) {
+        return selectList(new LambdaQueryWrapperX<MesQcIpqcDO>()
+                .eq(MesQcIpqcDO::getItemId, itemId)
+                .eq(MesQcIpqcDO::getBatchCode, batchCode)
+                .eq(MesQcIpqcDO::getType, type)
+                .eq(inspectDate != null, MesQcIpqcDO::getInspectDate, inspectDate)
+                .isNull(inspectDate == null, MesQcIpqcDO::getInspectDate)
+                .orderByAsc(MesQcIpqcDO::getId));
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/iqc/MesQcIqcMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/iqc/MesQcIqcMapper.java
index bfa0a71..d164f65 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/iqc/MesQcIqcMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/iqc/MesQcIqcMapper.java
@@ -7,6 +7,9 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDateTime;
+import java.util.List;
+
 /**
  * MES 鏉ユ枡妫�楠屽崟锛圛QC锛� Mapper
  *
@@ -36,4 +39,19 @@
         return selectCount(MesQcIqcDO::getVendorId, vendorId);
     }
 
+    /**
+     * 鎸夊鍏ュ幓閲嶉敭鏌ヨ锛氬悓涓� 鐗╂枡 + 渚涘簲鍟� + 渚涘簲鍟嗘壒娆� + 鏉ユ枡鏃ユ湡 瑙嗕负鍚屼竴鎵规潵鏂欐暟鎹�
+     */
+    default List<MesQcIqcDO> selectListByImportKey(Long itemId, Long vendorId, String vendorBatch,
+                                                   LocalDateTime receiveDate) {
+        return selectList(new LambdaQueryWrapperX<MesQcIqcDO>()
+                .eq(MesQcIqcDO::getItemId, itemId)
+                .eq(vendorId != null, MesQcIqcDO::getVendorId, vendorId)
+                .isNull(vendorId == null, MesQcIqcDO::getVendorId)
+                .eq(vendorBatch != null, MesQcIqcDO::getVendorBatch, vendorBatch)
+                .isNull(vendorBatch == null, MesQcIqcDO::getVendorBatch)
+                .eq(MesQcIqcDO::getReceiveDate, receiveDate)
+                .orderByAsc(MesQcIqcDO::getId));
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ledger/MesQcInspectionLedgerMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ledger/MesQcInspectionLedgerMapper.java
new file mode 100644
index 0000000..3a27ea3
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/ledger/MesQcInspectionLedgerMapper.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.mes.dal.mysql.qc.ledger;
+
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerPageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerRespVO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * MES 妫�娴嬬粨鏋滃彴璐� Mapper
+ *
+ * 浣跨敤 UNION ALL SQL 浠庡洓绫绘楠屽崟锛圛QC/IPQC/OQC/RQC锛夋媺鍙栨楠屽崟锛屽啀鍏宠仈妫�楠岀粨鏋滄槑缁嗕笌妫�娴嬮」锛屼竴琛屼竴涓娴嬮」缁撴灉
+ */
+@Mapper
+public interface MesQcInspectionLedgerMapper {
+
+    IPage<MesQcInspectionLedgerRespVO> selectInspectionLedgerPage(IPage<MesQcInspectionLedgerRespVO> page,
+                                                                 @Param("reqVO") MesQcInspectionLedgerPageReqVO reqVO);
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java
index af252fd..3218466 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java
@@ -8,6 +8,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 /**
@@ -57,4 +58,22 @@
         return selectOne(MesQcOqcDO::getCode, code);
     }
 
+    /**
+     * 鎸夊鍏ュ幓閲嶉敭鏌ヨ锛氬悓涓� 鐗╂枡 + 瀹㈡埛 + 鎵规鍙� + 鍑鸿揣鏃ユ湡 瑙嗕负鍚屼竴鎵瑰嚭璐ф楠屾暟鎹�
+     *
+     * 瀹㈡埛涓庡嚭璐ф棩鏈熷湪鏈垎鏀彲涓虹┖锛屾晠涓変釜鍙┖缁村害鍧囧仛绌哄�煎畨鍏ㄦ瘮杈冿紙null 姣� null锛夈��
+     */
+    default List<MesQcOqcDO> selectListByImportKey(Long itemId, Long clientId, String batchCode,
+                                                   LocalDateTime outDate) {
+        return selectList(new LambdaQueryWrapperX<MesQcOqcDO>()
+                .eq(MesQcOqcDO::getItemId, itemId)
+                .eq(clientId != null, MesQcOqcDO::getClientId, clientId)
+                .isNull(clientId == null, MesQcOqcDO::getClientId)
+                .eq(batchCode != null, MesQcOqcDO::getBatchCode, batchCode)
+                .isNull(batchCode == null, MesQcOqcDO::getBatchCode)
+                .eq(outDate != null, MesQcOqcDO::getOutDate, outDate)
+                .isNull(outDate == null, MesQcOqcDO::getOutDate)
+                .orderByAsc(MesQcOqcDO::getId));
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/imports/MesQcImportHelper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/imports/MesQcImportHelper.java
new file mode 100644
index 0000000..d46e086
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/imports/MesQcImportHelper.java
@@ -0,0 +1,369 @@
+package cn.iocoder.yudao.module.mes.service.qc.imports;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
+import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
+import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
+import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService;
+import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
+import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.QC_NO_TEMPLATE;
+
+/**
+ * MES 璐ㄦ鍗曞鍏ヨ緟鍔╃被
+ *
+ * 璐熻矗鎶� Excel 涓殑浜虹被鍙鍊硷紙鐗╂枡缂栫爜銆佷緵搴斿晢鍚嶇О銆佸鎴峰悕绉般�佽川妫�鏂规鍚嶇О銆佹娴嬩汉鍛樺鍚嶇瓑锛�
+ * 瑙f瀽涓虹郴缁熺紪鍙凤紝骞剁粺涓�澶勭悊瀛楀吀銆佹棩鏈熴�侀敊璇枃妗堛��
+ * 鍚勭墿鏂�/渚涘簲鍟�/瀹㈡埛/鏂规鏄犲皠鍦� {@link #startImport()} 鍚庨娆′娇鐢ㄦ椂涓�娆℃�у姞杞斤紝閬垮厤閫愯鏌ヨ銆�
+ */
+@Component
+public class MesQcImportHelper {
+
+    @Resource
+    private AdminUserApi adminUserApi;
+    @Resource
+    private MdmItemApi mdmItemApi;
+    @Resource
+    private SrmSupplierApi srmSupplierApi;
+    @Resource
+    private CrmCustomerApi crmCustomerApi;
+    @Resource
+    private MesQcTemplateService templateService;
+    @Resource
+    private MesMdAutoCodeRecordService autoCodeRecordService;
+
+    /** 璐ㄦ鏂规鍚嶇О -> 缂栧彿锛堝彲鑳介噸鍚嶏紝鍛戒腑澶氫釜鏃跺彇绗竴涓級 */
+    private Map<String, Long> templateNameMap;
+    /** 渚涘簲鍟嗗悕绉� -> 缂栧彿 */
+    private Map<String, Long> supplierNameMap;
+    /** 瀹㈡埛鍚嶇О -> 缂栧彿 */
+    private Map<String, Long> customerNameMap;
+    /** 褰撳墠鐧诲綍鐢ㄦ埛鍙鐨勫鎴风紪鍙凤紱null 琛ㄧず涓嶉檺鍒� */
+    private Set<Long> permittedClientIds;
+    private boolean clientPermissionLoaded;
+
+    /**
+     * 寮�濮嬩竴娆″鍏ワ細娓呯┖涓婁竴娆″鍏ョ殑缂撳瓨锛屼繚璇佹柊寤虹殑渚涘簲鍟�/瀹㈡埛/鏂规鏈鍗冲彲鍛戒腑
+     */
+    public void startImport() {
+        templateNameMap = null;
+        supplierNameMap = null;
+        customerNameMap = null;
+        permittedClientIds = null;
+        clientPermissionLoaded = false;
+    }
+
+    /**
+     * 鐢熸垚妫�楠屽崟缂栧彿锛屽 IQC20260101000001
+     */
+    public String generateCode(String ruleCode) {
+        return autoCodeRecordService.generateAutoCode(ruleCode);
+    }
+
+    /**
+     * 瑙f瀽妫�娴嬩汉鍛橈細鐣欑┖鍙栧綋鍓嶇櫥褰曠敤鎴凤紱濉啓鏃舵寜濮撳悕锛堟樀绉帮級绮剧‘鍖归厤锛屽彇涓嶅埌鍒欑敤妯$硦鍖归厤鐨勭涓�鏉�
+     */
+    public Long resolveInspectorUserId(String nickname) {
+        if (StrUtil.isBlank(nickname)) {
+            return getLoginUserId();
+        }
+        List<AdminUserRespDTO> users = adminUserApi.getUserListByNickname(nickname.trim());
+        if (CollUtil.isEmpty(users)) {
+            throw new IllegalArgumentException("妫�娴嬩汉鍛樹笉瀛樺湪锛�" + nickname.trim()
+                    + "銆傝鍦ㄣ�屾娴嬩汉鍛樸�嶅垪濉啓绯荤粺鍐呯殑鐢ㄦ埛濮撳悕锛堝彲鍦ㄣ�岀郴缁熺鐞�-鐢ㄦ埛绠$悊銆嶄腑鏍稿锛夛紝鎴栫暀绌轰娇鐢ㄥ綋鍓嶇櫥褰曠敤鎴�");
+        }
+        return users.stream().filter(user -> nickname.trim().equals(user.getNickname()))
+                .map(AdminUserRespDTO::getId).findFirst().orElseGet(() -> users.get(0).getId());
+    }
+
+    /**
+     * 瑙f瀽浜у搧鐗╂枡缂栧彿锛屽熀浜庣墿鏂欑紪鐮�
+     */
+    public Long resolveItemId(String itemCode) {
+        if (StrUtil.isBlank(itemCode)) {
+            throw new IllegalArgumentException("浜у搧鐗╂枡缂栫爜涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆屼骇鍝佺墿鏂欑紪鐮併�嶅垪");
+        }
+        MdmItemRespDTO item = mdmItemApi.getItemByCode(itemCode.trim()).getCheckedData();
+        if (item == null) {
+            throw new IllegalArgumentException("鐗╂枡涓嶅瓨鍦細" + itemCode.trim()
+                    + "銆傝鍏堝湪銆岀墿鏂欑鐞嗐�嶄腑缁存姢璇ョ墿鏂欙紙缂栫爜闇�涓庢ā鏉垮畬鍏ㄤ竴鑷达級锛屾垨淇敼妯℃澘涓殑浜у搧鐗╂枡缂栫爜鍚庨噸鏂板鍏�");
+        }
+        return item.getId();
+    }
+
+    /**
+     * 瑙f瀽璐ㄦ鏂规缂栧彿锛屽熀浜庢柟妗堝悕绉帮紱鐣欑┖杩斿洖 null锛堢敱绯荤粺鎸夌墿鏂�+妫�楠岀被鍨嬭嚜鍔ㄥ尮閰嶏級
+     */
+    public Long resolveTemplateId(String templateName) {
+        if (StrUtil.isBlank(templateName)) {
+            return null;
+        }
+        if (templateNameMap == null) {
+            templateNameMap = new HashMap<>();
+            List<MesQcTemplateDO> templates = templateService.getTemplateList();
+            if (CollUtil.isNotEmpty(templates)) {
+                templates.forEach(template -> {
+                    if (StrUtil.isNotBlank(template.getName())) {
+                        templateNameMap.putIfAbsent(template.getName().trim(), template.getId());
+                    }
+                });
+            }
+        }
+        Long templateId = templateNameMap.get(templateName.trim());
+        if (templateId == null) {
+            throw new IllegalArgumentException("璐ㄦ鏂规涓嶅瓨鍦細" + templateName.trim()
+                    + "銆傝鍦ㄣ�岃川妫�绠$悊-璐ㄦ鏂规銆嶄腑鏍稿鏂规鍚嶇О锛屾垨鐣欑┖璇ュ垪鐢辩郴缁熸寜鐗╂枡鑷姩鍖归厤鏂规鍚庨噸鏂板鍏�");
+        }
+        return templateId;
+    }
+
+    /**
+     * 瑙f瀽渚涘簲鍟嗙紪鍙凤紝鍩轰簬渚涘簲鍟嗗悕绉帮紱鐣欑┖杩斿洖 null
+     */
+    public Long resolveVendorId(String vendorName) {
+        if (StrUtil.isBlank(vendorName)) {
+            return null;
+        }
+        if (supplierNameMap == null) {
+            supplierNameMap = new HashMap<>();
+            List<SrmSupplierRespDTO> suppliers = srmSupplierApi.getSupplierSimpleList().getCheckedData();
+            if (CollUtil.isNotEmpty(suppliers)) {
+                suppliers.forEach(supplier -> {
+                    if (StrUtil.isNotBlank(supplier.getName())) {
+                        supplierNameMap.putIfAbsent(supplier.getName().trim(), supplier.getId());
+                    }
+                });
+            }
+        }
+        Long vendorId = supplierNameMap.get(vendorName.trim());
+        if (vendorId == null) {
+            throw new IllegalArgumentException("渚涘簲鍟嗕笉瀛樺湪鎴栨湭閫氳繃鍑嗗叆瀹℃壒锛�" + vendorName.trim()
+                    + "銆傝鍦ㄣ�屼緵搴斿晢绠$悊銆嶄腑鏍稿渚涘簲鍟嗗悕绉帮紙鍚嶇О闇�涓庢ā鏉垮畬鍏ㄤ竴鑷达級锛屾垨鐣欑┖璇ュ垪鍚庨噸鏂板鍏�");
+        }
+        return vendorId;
+    }
+
+    /**
+     * 瑙f瀽瀹㈡埛缂栧彿锛屽熀浜庡鎴峰悕绉帮紱鐣欑┖杩斿洖 null
+     *
+     * 鏈垎鏀殑鍑鸿揣妫�楠屽崟涓嶅己鍒跺叧鑱斿鎴凤紙瀹㈡埛鍙┖锛夛紝鏁呮澶勭暀绌轰笉鎶ラ敊銆�
+     */
+    public Long resolveClientId(String clientName) {
+        if (StrUtil.isBlank(clientName)) {
+            return null;
+        }
+        if (customerNameMap == null) {
+            customerNameMap = new HashMap<>();
+            List<CrmCustomerRespDTO> customers = crmCustomerApi.getCustomerSimpleList();
+            if (CollUtil.isNotEmpty(customers)) {
+                customers.forEach(customer -> {
+                    if (StrUtil.isNotBlank(customer.getName())) {
+                        customerNameMap.putIfAbsent(customer.getName().trim(), customer.getId());
+                    }
+                });
+            }
+        }
+        Long clientId = customerNameMap.get(clientName.trim());
+        if (clientId == null) {
+            throw new IllegalArgumentException("瀹㈡埛涓嶅瓨鍦細" + clientName.trim()
+                    + "銆傝鍏堝湪銆孋RM 瀹㈡埛绠$悊銆嶄腑鍒涘缓璇ュ鎴凤紙鍚嶇О闇�涓庢ā鏉垮畬鍏ㄤ竴鑷达級锛屾垨淇敼妯℃澘涓殑瀹㈡埛鍚嶇О鍚庨噸鏂板鍏�");
+        }
+        return clientId;
+    }
+
+    /**
+     * 鏍¢獙瀹㈡埛鍦ㄥ綋鍓嶇櫥褰曠敤鎴风殑鍙鑼冨洿鍐�
+     *
+     * 鍑鸿揣妫�楠屽崟鍒楄〃鎸夊鎴锋暟鎹潈闄愯繃婊わ紝鏉冮檺澶栫殑瀹㈡埛瀵煎叆鍚庢湰浜哄湪鍒楄〃涓湅涓嶅埌璇ュ崟鎹紝鍥犳鎻愬墠鎷︽埅銆�
+     */
+    public void validateClientPermission(Long clientId, String clientName) {
+        if (!clientPermissionLoaded) {
+            List<Long> permittedIds = crmCustomerApi.getPermittedCustomerIds();
+            permittedClientIds = permittedIds == null ? null : new HashSet<>(permittedIds);
+            clientPermissionLoaded = true;
+        }
+        if (permittedClientIds == null || permittedClientIds.contains(clientId)) {
+            return;
+        }
+        throw new IllegalArgumentException("瀹㈡埛[" + clientName + "]涓嶅湪鎮ㄧ殑鏁版嵁鏉冮檺鑼冨洿鍐咃細"
+                + "瀵煎叆鍚庢偍灏嗘棤娉曞湪銆屽嚭璐ф楠屻�嶅垪琛ㄤ腑鐪嬪埌璇ユ楠屽崟銆傝鏀圭敤鎮ㄨ礋璐g殑瀹㈡埛锛堝彲鍦ㄣ�孋RM 瀹㈡埛绠$悊-鎴戣礋璐g殑銆嶄腑鏌ョ湅锛夛紝"
+                + "鎴栬仈绯荤鐞嗗憳涓烘偍寮�閫氳瀹㈡埛鐨勬潈闄愬悗閲嶆柊瀵煎叆");
+    }
+
+    /**
+     * 瑙f瀽瀛楀吀鍊硷細鏀寔濉啓瀛楀吀鍚嶇О锛堝 鏍¢獙閫氳繃锛夋垨瀛楀吀鍊硷紙濡� 1锛�
+     *
+     * @param text       Excel 鍗曞厓鏍煎唴瀹�
+     * @param dictType   瀛楀吀绫诲瀷
+     * @param fieldLabel 鍒楀悕锛岀敤浜庨敊璇彁绀�
+     * @param required   鏄惁蹇呭~
+     */
+    public Integer resolveDictValue(String text, String dictType, String fieldLabel, boolean required) {
+        List<String> labelList = DictFrameworkUtils.getDictDataLabelList(dictType);
+        if (StrUtil.isBlank(text)) {
+            if (required) {
+                throw new IllegalArgumentException(fieldLabel + "涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆�" + fieldLabel
+                        + "銆嶅垪锛屽彲閫夊�硷細" + String.join("銆�", labelList));
+            }
+            return null;
+        }
+        String value = text.trim();
+        if (value.matches("\\d+") && DictFrameworkUtils.getDictDataValueList(dictType).contains(value)) {
+            return Integer.valueOf(value);
+        }
+        String parsedValue = DictFrameworkUtils.parseDictDataValue(dictType, value);
+        if (parsedValue == null) {
+            throw new IllegalArgumentException(fieldLabel + "涓嶅瓨鍦細" + value + "銆傛ā鏉夸腑銆�" + fieldLabel
+                    + "銆嶈濉啓浠ヤ笅涔嬩竴锛�" + String.join("銆�", labelList));
+        }
+        return Integer.valueOf(parsedValue);
+    }
+
+    /**
+     * 瑙f瀽鏁伴噺锛氬繀濉椂涓嶈兘涓虹┖锛屼笖涓嶈兘灏忎簬 0
+     */
+    public BigDecimal resolveQuantity(BigDecimal value, String fieldLabel, boolean required) {
+        if (value == null) {
+            if (required) {
+                throw new IllegalArgumentException(fieldLabel + "涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆�" + fieldLabel + "銆嶅垪");
+            }
+            return null;
+        }
+        if (value.compareTo(BigDecimal.ZERO) < 0) {
+            throw new IllegalArgumentException(fieldLabel + "涓嶈兘灏忎簬 0锛屽綋鍓嶄负 " + value.stripTrailingZeros().toPlainString()
+                    + "銆傝淇敼妯℃澘鍚庨噸鏂板鍏�");
+        }
+        return value;
+    }
+
+    /**
+     * 鏍¢獙銆屽悎璁℃暟閲� = 鍚堟牸鍝佹暟閲� + 涓嶅悎鏍煎搧鏁伴噺銆�
+     *
+     * @param total            鍚堣鏁伴噺锛堝悎璁″垪鐣欑┖鏃朵紶 null锛岀敱鍚堟牸 + 涓嶅悎鏍� 鑷姩鎺ㄧ畻锛�
+     * @param qualified        鍚堟牸鍝佹暟閲�
+     * @param unqualified      涓嶅悎鏍煎搧鏁伴噺
+     * @param totalFieldLabel  鍚堣鍒楃殑鍒楀悕锛屽銆屾湰娆℃帴鏀舵暟閲忋��
+     */
+    public BigDecimal resolveCheckQuantity(BigDecimal total, BigDecimal qualified, BigDecimal unqualified,
+                                           String totalFieldLabel) {
+        BigDecimal sum = qualified.add(unqualified);
+        if (total == null) {
+            return sum;
+        }
+        if (total.compareTo(sum) != 0) {
+            throw new IllegalArgumentException(totalFieldLabel + "蹇呴』绛変簬鍚堟牸鍝佹暟閲忎笌涓嶅悎鏍煎搧鏁伴噺涔嬪拰锛氬悎鏍煎搧鏁伴噺 "
+                    + qualified.stripTrailingZeros().toPlainString() + " + 涓嶅悎鏍煎搧鏁伴噺 "
+                    + unqualified.stripTrailingZeros().toPlainString() + " = " + sum.stripTrailingZeros().toPlainString()
+                    + "锛岃�屻��" + totalFieldLabel + "銆嶅~鍐欑殑鏄� " + total.stripTrailingZeros().toPlainString()
+                    + "銆傝淇敼妯℃澘涓殑鏁伴噺鍒楋紝鎴栧皢璇ュ垪鐣欑┖鐢辩郴缁熻嚜鍔ㄥ悎璁″悗閲嶆柊瀵煎叆");
+        }
+        return total;
+    }
+
+    /**
+     * 瑙f瀽鏃ユ湡鏃堕棿锛氭敮鎸� 2026-01-01 10:00:00 / 2026-01-01 10:00 / 2026-01-01 / 2026/1/1锛涚暀绌鸿繑鍥� null
+     */
+    public LocalDateTime parseDateTime(String text, String fieldLabel) {
+        if (StrUtil.isBlank(text)) {
+            return null;
+        }
+        String value = text.trim();
+        for (String pattern : new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss"}) {
+            try {
+                return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(pattern));
+            } catch (Exception ignored) {
+                // 缁х画灏濊瘯涓嬩竴涓牸寮�
+            }
+        }
+        for (String pattern : new String[]{"yyyy-MM-dd", "yyyy/M/d"}) {
+            try {
+                return java.time.LocalDate.parse(value, DateTimeFormatter.ofPattern(pattern)).atStartOfDay();
+            } catch (Exception ignored) {
+                // 缁х画灏濊瘯涓嬩竴涓牸寮�
+            }
+        }
+        throw new IllegalArgumentException(fieldLabel + "鏍煎紡涓嶆纭細" + text + "锛岀ず渚� 2026-01-01 10:00:00");
+    }
+
+    /**
+     * 瑙f瀽蹇呭~鐨勬棩鏈熸椂闂达細鐣欑┖鎶ラ敊
+     */
+    public LocalDateTime parseRequiredDateTime(String text, String fieldLabel) {
+        LocalDateTime value = parseDateTime(text, fieldLabel);
+        if (value == null) {
+            throw new IllegalArgumentException(fieldLabel + "涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆�" + fieldLabel
+                    + "銆嶅垪锛岀ず渚� 2026-01-01 10:00:00");
+        }
+        return value;
+    }
+
+    /**
+     * 鐗╂枡鏈厤缃川妫�鏂规鏃讹紝琛ュ厖鐗╂枡涓庢楠岀被鍨嬩笂涓嬫枃鍐嶆姏鍑猴紱鍏朵綑寮傚父鍘熸牱鎶涘嚭
+     */
+    public static void rethrowTemplateError(ServiceException ex, String itemCode, String qcTypeLabel) {
+        if (Objects.equals(ex.getCode(), QC_NO_TEMPLATE.getCode())) {
+            throw new IllegalArgumentException("鐗╂枡[" + StrUtil.blankToDefault(itemCode, "鏈煡") + "]鏈厤缃�"
+                    + qcTypeLabel + "鐨勮川妫�鏂规銆傝鍦ㄣ�岃川妫�绠$悊-璐ㄦ鏂规銆嶄腑涓鸿鐗╂枡閰嶇疆" + qcTypeLabel
+                    + "鏂规锛屾垨鍦ㄦā鏉跨殑銆岃川妫�鏂规銆嶅垪濉啓璇ョ墿鏂欏凡閰嶇疆鐨勬柟妗堝悕绉板悗閲嶆柊瀵煎叆");
+        }
+        throw ex;
+    }
+
+    /**
+     * 鍗曡瀵煎叆澶辫触鏃剁殑鎻愮ず鏂囨
+     */
+    public static String resolveImportErrorMessage(Exception ex) {
+        if (ex instanceof cn.iocoder.yudao.framework.common.exception.ServiceException serviceException) {
+            return serviceException.getMessage();
+        }
+        return StrUtil.blankToDefault(ex.getMessage(), "瀵煎叆澶辫触");
+    }
+
+    /**
+     * 閲嶅鏁版嵁鎻愮ず涓殑鍙栧�煎睍绀猴細鐣欑┖鏄剧ず涓恒�屾湭濉啓銆�
+     */
+    public static String describeImportValue(String value) {
+        return StrUtil.isBlank(value) ? "鏈~鍐�" : value.trim();
+    }
+
+    /**
+     * 鍚屼竴浠芥ā鏉垮唴鍑虹幇閲嶅鏁版嵁鏃剁殑鎻愮ず
+     */
+    public static IllegalArgumentException duplicateRowException(int firstRowNo, String keyDesc) {
+        return new IllegalArgumentException("妫�娴嬪埌閲嶅瀵煎叆锛氭湰娆″鍏ョ殑绗� " + firstRowNo
+                + " 琛屼笌褰撳墠琛岀殑鏁版嵁瀹屽叏鐩稿悓锛�" + keyDesc + "锛夈�傝鍒犻櫎妯℃澘涓噸澶嶇殑琛屽悗閲嶆柊瀵煎叆");
+    }
+
+    /**
+     * 涓庣郴缁熶腑宸叉湁鍗曟嵁閲嶅鏃剁殑鎻愮ず
+     */
+    public static IllegalArgumentException duplicateExistingException(String keyDesc, String docLabel,
+                                                                    String existingCode, String menuPath) {
+        return new IllegalArgumentException("妫�娴嬪埌閲嶅瀵煎叆锛�" + keyDesc + " 涓庡凡鏈�" + docLabel + "銆�" + existingCode
+                + "銆嶅畬鍏ㄧ浉鍚岋紙鍙兘鏄湰娆″鍏ュ墠闈㈢殑琛岋紝涔熷彲鑳芥槸绯荤粺涓凡鏈夊崟鎹級銆傝鍕块噸澶嶅鍏ワ紱濡傜‘闇�閲嶆柊瀵煎叆锛岃鍏堝湪銆�"
+                + menuPath + "銆嶄腑鍒犻櫎璇�" + docLabel + "鍚庨噸璇�");
+    }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcService.java
index 20e9d23..cb2a953 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcService.java
@@ -1,8 +1,10 @@
 package cn.iocoder.yudao.module.mes.service.qc.ipqc;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
 import jakarta.validation.Valid;
@@ -77,4 +79,12 @@
      */
     void recalculateDefectStats(Long ipqcId, List<MesQcDefectRecordDO> records);
 
+    /**
+     * 鎵归噺瀵煎叆杩囩▼妫�楠屽崟
+     *
+     * @param importList 瀵煎叆琛屽垪琛�
+     * @return 瀵煎叆缁撴灉锛堟垚鍔熺紪鍙峰垪琛� + 閫愯澶辫触鍘熷洜锛�
+     */
+    MesQcImportRespVO importIpqcList(List<MesQcIpqcImportExcelVO> importList);
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java
index b609e4c..98bded1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java
@@ -4,24 +4,30 @@
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcMapper;
+import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
 import cn.iocoder.yudao.module.mes.service.pro.feedback.MesProFeedbackService;
 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
+import cn.iocoder.yudao.module.mes.service.qc.imports.MesQcImportHelper;
 import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
@@ -37,7 +43,11 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -55,6 +65,9 @@
 
     @Resource
     private MesQcIpqcMapper ipqcMapper;
+
+    @Resource
+    private MesQcImportHelper qcImportHelper;
 
     @Resource
     private MesQcTemplateItemService templateItemService;
@@ -349,4 +362,145 @@
         ipqcMapper.updateById(updateIpqc);
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public MesQcImportRespVO importIpqcList(List<MesQcIpqcImportExcelVO> importList) {
+        MesQcImportRespVO respVO = MesQcImportRespVO.builder()
+                .createList(new ArrayList<>()).failureList(new LinkedHashMap<>()).build();
+        if (CollUtil.isEmpty(importList)) {
+            return respVO;
+        }
+        qcImportHelper.startImport();
+        Map<String, Integer> firstRowNoByKey = new HashMap<>();
+        for (int i = 0; i < importList.size(); i++) {
+            MesQcIpqcImportExcelVO row = importList.get(i);
+            if (isImportRowEmpty(row)) {
+                continue; // 璺宠繃绌鸿
+            }
+            String label = StrUtil.isNotBlank(row.getCode()) ? "妫�楠屽崟缂栧彿 " + row.getCode().trim()
+                    : "绗� " + (i + 2) + " 琛�";
+            String importKey = buildImportKey(row);
+            try {
+                Integer firstRowNo = firstRowNoByKey.get(importKey);
+                if (firstRowNo != null) {
+                    throw MesQcImportHelper.duplicateRowException(firstRowNo, buildImportKeyDesc(row));
+                }
+                respVO.getCreateList().add(createIpqcByImport(row));
+                firstRowNoByKey.putIfAbsent(importKey, i + 2);
+            } catch (Exception ex) {
+                respVO.getFailureList().put(label, MesQcImportHelper.resolveImportErrorMessage(ex));
+            }
+        }
+        return respVO;
+    }
+
+    private boolean isImportRowEmpty(MesQcIpqcImportExcelVO row) {
+        return row == null || (StrUtil.isBlank(row.getCode()) && StrUtil.isBlank(row.getName())
+                && StrUtil.isBlank(row.getType()) && StrUtil.isBlank(row.getItemCode())
+                && StrUtil.isBlank(row.getBatchCode()) && StrUtil.isBlank(row.getTemplateName())
+                && row.getCheckQuantity() == null && row.getQualifiedQuantity() == null
+                && row.getUnqualifiedQuantity() == null && row.getLaborScrapQuantity() == null
+                && row.getMaterialScrapQuantity() == null && row.getOtherScrapQuantity() == null
+                && StrUtil.isBlank(row.getCheckResult()) && StrUtil.isBlank(row.getInspectorName())
+                && StrUtil.isBlank(row.getInspectDate()) && StrUtil.isBlank(row.getRemark()));
+    }
+
+    /**
+     * 渚濇嵁瀵煎叆琛屽垱寤轰竴寮犺繃绋嬫楠屽崟锛氱墿鏂欐寜浜у搧鐗╂枡缂栫爜鍖归厤锛屾壒鍙峰繀濉紝鐘舵�佷负銆屽緟妫�楠屻�嶏紝妫�楠岃鐢辫川妫�鏂规鑷姩鐢熸垚
+     *
+     * @return 妫�楠屽崟缂栧彿
+     */
+    private String createIpqcByImport(MesQcIpqcImportExcelVO row) {
+        MesQcIpqcSaveReqVO reqVO = new MesQcIpqcSaveReqVO();
+        // 1. 妫�楠屽崟缂栧彿锛氬~鍐欏垯浣跨敤锛岀暀绌鸿嚜鍔ㄧ敓鎴�
+        String code = StrUtil.isNotBlank(row.getCode()) ? row.getCode().trim()
+                : qcImportHelper.generateCode(MesMdAutoCodeRuleCodeEnum.QC_IPQC_CODE.getCode());
+        if (ipqcMapper.selectByCode(code) != null) {
+            throw new IllegalArgumentException("妫�楠屽崟缂栧彿宸插瓨鍦細" + code
+                    + "銆傝淇敼妯℃澘涓殑銆屾楠屽崟缂栧彿銆嶏紝鎴栧厛鍦ㄧ郴缁熶腑鍒犻櫎璇ョ紪鍙风殑妫�楠屽崟鍚庨噸鏂板鍏�");
+        }
+        reqVO.setCode(code);
+        // 2. 鍚嶇О銆佹楠岀被鍨嬨�佷骇鍝佺墿鏂欍�佹壒鍙枫�佽川妫�鏂规
+        if (StrUtil.isBlank(row.getName())) {
+            throw new IllegalArgumentException("妫�楠屽崟鍚嶇О涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆屾楠屽崟鍚嶇О銆嶅垪");
+        }
+        reqVO.setName(row.getName().trim());
+        reqVO.setType(qcImportHelper.resolveDictValue(row.getType(),
+                DictTypeConstants.MES_IPQC_TYPE, "妫�楠岀被鍨�", true));
+        reqVO.setItemId(qcImportHelper.resolveItemId(row.getItemCode()));
+        if (StrUtil.isBlank(row.getBatchCode())) {
+            throw new IllegalArgumentException("鎵瑰彿涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆屾壒鍙枫�嶅垪");
+        }
+        reqVO.setBatchCode(row.getBatchCode().trim());
+        reqVO.setTemplateId(qcImportHelper.resolveTemplateId(row.getTemplateName()));
+        // 3. 鏁伴噺锛氬悎鏍煎搧 + 涓嶅悎鏍煎搧 蹇呴』绛変簬妫�娴嬫暟閲忥紙妫�娴嬫暟閲忕暀绌哄垯鑷姩鍚堣锛夛紱宸ュ簾/鏂欏簾/鍏朵粬搴熺暀绌烘寜 0
+        BigDecimal qualified = qcImportHelper.resolveQuantity(row.getQualifiedQuantity(), "鍚堟牸鍝佹暟閲�", true);
+        BigDecimal unqualified = qcImportHelper.resolveQuantity(row.getUnqualifiedQuantity(), "涓嶅悎鏍煎搧鏁伴噺", true);
+        reqVO.setQualifiedQuantity(qualified);
+        reqVO.setUnqualifiedQuantity(unqualified);
+        reqVO.setCheckQuantity(qcImportHelper.resolveCheckQuantity(
+                row.getCheckQuantity(), qualified, unqualified, "妫�娴嬫暟閲�"));
+        BigDecimal laborScrap = qcImportHelper.resolveQuantity(row.getLaborScrapQuantity(), "宸ュ簾鏁伴噺", false);
+        BigDecimal materialScrap = qcImportHelper.resolveQuantity(row.getMaterialScrapQuantity(), "鏂欏簾鏁伴噺", false);
+        BigDecimal otherScrap = qcImportHelper.resolveQuantity(row.getOtherScrapQuantity(), "鍏朵粬搴熷搧鏁伴噺", false);
+        laborScrap = laborScrap != null ? laborScrap : BigDecimal.ZERO;
+        materialScrap = materialScrap != null ? materialScrap : BigDecimal.ZERO;
+        otherScrap = otherScrap != null ? otherScrap : BigDecimal.ZERO;
+        validateScrapQuantity(unqualified, laborScrap, materialScrap, otherScrap);
+        reqVO.setLaborScrapQuantity(laborScrap);
+        reqVO.setMaterialScrapQuantity(materialScrap);
+        reqVO.setOtherScrapQuantity(otherScrap);
+        // 4. 妫�楠屼俊鎭�
+        reqVO.setCheckResult(qcImportHelper.resolveDictValue(row.getCheckResult(),
+                DictTypeConstants.MES_QC_CHECK_RESULT, "妫�娴嬬粨鏋�", false));
+        reqVO.setInspectorUserId(qcImportHelper.resolveInspectorUserId(row.getInspectorName()));
+        reqVO.setInspectDate(qcImportHelper.parseDateTime(row.getInspectDate(), "妫�娴嬫棩鏈�"));
+        reqVO.setRemark(StrUtil.trimToNull(row.getRemark()));
+        // 5. 鍘婚噸锛氬悓涓� 鐗╂枡 + 鎵瑰彿 + 妫�楠岀被鍨� + 妫�娴嬫棩鏈� 鐨勮繃绋嬫楠屽崟涓嶅彲閲嶅瀵煎叆
+        MesQcIpqcDO duplicate = CollUtil.getFirst(ipqcMapper.selectListByImportKey(reqVO.getItemId(),
+                reqVO.getBatchCode(), reqVO.getType(), reqVO.getInspectDate()));
+        if (duplicate != null) {
+            throw MesQcImportHelper.duplicateExistingException(buildImportKeyDesc(row), "杩囩▼妫�楠屽崟",
+                    duplicate.getCode(), "璐ㄩ噺绠$悊-杩囩▼妫�楠�");
+        }
+        // 6. 澶嶇敤鍒涘缓閫昏緫锛氭牎楠岀墿鏂�/浜哄憳銆佹寜鐗╂枡鍖归厤璐ㄦ鏂规銆佺敓鎴愭楠岃
+        try {
+            createIpqc(reqVO);
+        } catch (ServiceException ex) {
+            MesQcImportHelper.rethrowTemplateError(ex, row.getItemCode(), "杩囩▼妫�楠�(IPQC)");
+        }
+        return code;
+    }
+
+    /**
+     * 鏍¢獙 宸ュ簾 + 鏂欏簾 + 鍏朵粬搴熷搧 涓嶈秴杩囦笉鍚堟牸鍝佹暟閲�
+     */
+    private void validateScrapQuantity(BigDecimal unqualified, BigDecimal laborScrap,
+                                       BigDecimal materialScrap, BigDecimal otherScrap) {
+        BigDecimal scrapSum = laborScrap.add(materialScrap).add(otherScrap);
+        if (scrapSum.compareTo(unqualified) <= 0) {
+            return;
+        }
+        throw new IllegalArgumentException("宸ュ簾銆佹枡搴熴�佸叾浠栧簾鍝佹暟閲忎箣鍜屼笉鑳借秴杩囦笉鍚堟牸鍝佹暟閲忥細涓嶅悎鏍煎搧鏁伴噺涓� "
+                + unqualified.stripTrailingZeros().toPlainString() + "锛岃�� 宸ュ簾 " + laborScrap.stripTrailingZeros().toPlainString()
+                + " + 鏂欏簾 " + materialScrap.stripTrailingZeros().toPlainString() + " + 鍏朵粬搴熷搧 "
+                + otherScrap.stripTrailingZeros().toPlainString() + " = " + scrapSum.stripTrailingZeros().toPlainString()
+                + "銆傝淇敼妯℃澘涓殑搴熷搧鏁伴噺鎴栦笉鍚堟牸鍝佹暟閲忓悗閲嶆柊瀵煎叆");
+    }
+
+    /**
+     * 瀵煎叆鍘婚噸閿細鍚屼竴 鐗╂枡 + 鎵瑰彿 + 妫�楠岀被鍨� + 妫�娴嬫棩鏈� 瑙嗕负鍚屼竴鎵硅繃绋嬫楠屾暟鎹�
+     */
+    private String buildImportKey(MesQcIpqcImportExcelVO row) {
+        return String.join("|", StrUtil.trimToEmpty(row.getItemCode()), StrUtil.trimToEmpty(row.getBatchCode()),
+                StrUtil.trimToEmpty(row.getType()), StrUtil.trimToEmpty(row.getInspectDate()));
+    }
+
+    private String buildImportKeyDesc(MesQcIpqcImportExcelVO row) {
+        return "鐗╂枡[" + MesQcImportHelper.describeImportValue(row.getItemCode())
+                + "]銆佹壒鍙穂" + MesQcImportHelper.describeImportValue(row.getBatchCode())
+                + "]銆佹楠岀被鍨媅" + MesQcImportHelper.describeImportValue(row.getType())
+                + "]銆佹娴嬫棩鏈焄" + MesQcImportHelper.describeImportValue(row.getInspectDate()) + "]";
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java
index d688a09..7d4d6df 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcService.java
@@ -1,8 +1,10 @@
 package cn.iocoder.yudao.module.mes.service.qc.iqc;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
@@ -107,4 +109,12 @@
      */
     Long createIqcFromArrivalNotice(Long noticeId, MesWmArrivalNoticeLineDO line, String noticeCode, Long vendorId);
 
+    /**
+     * 鎵归噺瀵煎叆鏉ユ枡妫�楠屽崟
+     *
+     * @param importList 瀵煎叆琛屽垪琛�
+     * @return 瀵煎叆缁撴灉锛堟垚鍔熺紪鍙峰垪琛� + 閫愯澶辫触鍘熷洜锛�
+     */
+    MesQcImportRespVO importIqcList(List<MesQcIqcImportExcelVO> importList);
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
index 1714b66..5d59e37 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
@@ -2,21 +2,28 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.hutool.core.util.ObjectUtil;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper;
+import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
+import cn.iocoder.yudao.module.mes.service.qc.imports.MesQcImportHelper;
 import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
 import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
@@ -79,6 +86,9 @@
     @Resource
     @Lazy
     private MesQcNcrService ncrService;
+
+    @Resource
+    private MesQcImportHelper qcImportHelper;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -422,4 +432,115 @@
         return "鐗╂枡ID:" + itemId;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public MesQcImportRespVO importIqcList(List<MesQcIqcImportExcelVO> importList) {
+        MesQcImportRespVO respVO = MesQcImportRespVO.builder()
+                .createList(new ArrayList<>()).failureList(new LinkedHashMap<>()).build();
+        if (CollUtil.isEmpty(importList)) {
+            return respVO;
+        }
+        qcImportHelper.startImport();
+        Map<String, Integer> firstRowNoByKey = new HashMap<>();
+        for (int i = 0; i < importList.size(); i++) {
+            MesQcIqcImportExcelVO row = importList.get(i);
+            if (isImportRowEmpty(row)) {
+                continue; // 璺宠繃绌鸿
+            }
+            String label = StrUtil.isNotBlank(row.getCode()) ? "妫�楠屽崟缂栧彿 " + row.getCode().trim()
+                    : "绗� " + (i + 2) + " 琛�";
+            String importKey = buildImportKey(row);
+            try {
+                Integer firstRowNo = firstRowNoByKey.get(importKey);
+                if (firstRowNo != null) {
+                    throw MesQcImportHelper.duplicateRowException(firstRowNo, buildImportKeyDesc(row));
+                }
+                respVO.getCreateList().add(createIqcByImport(row));
+                firstRowNoByKey.putIfAbsent(importKey, i + 2);
+            } catch (Exception ex) {
+                respVO.getFailureList().put(label, MesQcImportHelper.resolveImportErrorMessage(ex));
+            }
+        }
+        return respVO;
+    }
+
+    private boolean isImportRowEmpty(MesQcIqcImportExcelVO row) {
+        return row == null || (StrUtil.isBlank(row.getCode()) && StrUtil.isBlank(row.getName())
+                && StrUtil.isBlank(row.getItemCode()) && StrUtil.isBlank(row.getVendorName())
+                && StrUtil.isBlank(row.getVendorBatch()) && StrUtil.isBlank(row.getTemplateName())
+                && row.getReceivedQuantity() == null && row.getQualifiedQuantity() == null
+                && row.getUnqualifiedQuantity() == null && StrUtil.isBlank(row.getCheckResult())
+                && StrUtil.isBlank(row.getInspectorName()) && StrUtil.isBlank(row.getInspectDate())
+                && StrUtil.isBlank(row.getReceiveDate()) && StrUtil.isBlank(row.getRemark()));
+    }
+
+    /**
+     * 渚濇嵁瀵煎叆琛屽垱寤轰竴寮犳潵鏂欐楠屽崟锛氱姸鎬佷负銆屽緟妫�楠屻�嶏紝妫�楠岃鐢辫川妫�鏂规鑷姩鐢熸垚
+     *
+     * @return 妫�楠屽崟缂栧彿
+     */
+    private String createIqcByImport(MesQcIqcImportExcelVO row) {
+        MesQcIqcSaveReqVO reqVO = new MesQcIqcSaveReqVO();
+        // 1. 妫�楠屽崟缂栧彿锛氬~鍐欏垯浣跨敤锛岀暀绌鸿嚜鍔ㄧ敓鎴�
+        String code = StrUtil.isNotBlank(row.getCode()) ? row.getCode().trim()
+                : qcImportHelper.generateCode(MesMdAutoCodeRuleCodeEnum.QC_IQC_CODE.getCode());
+        if (iqcMapper.selectByCode(code) != null) {
+            throw new IllegalArgumentException("妫�楠屽崟缂栧彿宸插瓨鍦細" + code
+                    + "銆傝淇敼妯℃澘涓殑銆屾楠屽崟缂栧彿銆嶏紝鎴栧厛鍦ㄧ郴缁熶腑鍒犻櫎璇ョ紪鍙风殑妫�楠屽崟鍚庨噸鏂板鍏�");
+        }
+        reqVO.setCode(code);
+        // 2. 鍚嶇О銆佺墿鏂欍�佷緵搴斿晢銆佽川妫�鏂规
+        if (StrUtil.isBlank(row.getName())) {
+            throw new IllegalArgumentException("妫�楠屽崟鍚嶇О涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆屾楠屽崟鍚嶇О銆嶅垪");
+        }
+        reqVO.setName(row.getName().trim());
+        reqVO.setItemId(qcImportHelper.resolveItemId(row.getItemCode()));
+        reqVO.setVendorId(qcImportHelper.resolveVendorId(row.getVendorName()));
+        reqVO.setVendorBatch(StrUtil.trimToNull(row.getVendorBatch()));
+        reqVO.setTemplateId(qcImportHelper.resolveTemplateId(row.getTemplateName()));
+        // 3. 鏁伴噺锛氬悎鏍煎搧 + 涓嶅悎鏍煎搧 蹇呴』绛変簬鏈鎺ユ敹鏁伴噺锛堟帴鏀舵暟閲忕暀绌哄垯鑷姩鍚堣锛�
+        BigDecimal qualified = qcImportHelper.resolveQuantity(row.getQualifiedQuantity(), "鍚堟牸鍝佹暟閲�", true);
+        BigDecimal unqualified = qcImportHelper.resolveQuantity(row.getUnqualifiedQuantity(), "涓嶅悎鏍煎搧鏁伴噺", true);
+        reqVO.setQualifiedQuantity(qualified);
+        reqVO.setUnqualifiedQuantity(unqualified);
+        reqVO.setReceivedQuantity(qcImportHelper.resolveCheckQuantity(
+                row.getReceivedQuantity(), qualified, unqualified, "鏈鎺ユ敹鏁伴噺"));
+        // 4. 妫�楠屼俊鎭�
+        reqVO.setCheckResult(qcImportHelper.resolveDictValue(row.getCheckResult(),
+                DictTypeConstants.MES_QC_CHECK_RESULT, "妫�娴嬬粨鏋�", false));
+        reqVO.setInspectorUserId(qcImportHelper.resolveInspectorUserId(row.getInspectorName()));
+        reqVO.setInspectDate(qcImportHelper.parseRequiredDateTime(row.getInspectDate(), "妫�娴嬫棩鏈�"));
+        reqVO.setReceiveDate(qcImportHelper.parseRequiredDateTime(row.getReceiveDate(), "鏉ユ枡鏃ユ湡"));
+        reqVO.setRemark(StrUtil.trimToNull(row.getRemark()));
+        // 5. 鍘婚噸锛氬悓涓� 鐗╂枡 + 渚涘簲鍟� + 渚涘簲鍟嗘壒娆� + 鏉ユ枡鏃ユ湡 鐨勬潵鏂欐楠屽崟涓嶅彲閲嶅瀵煎叆
+        MesQcIqcDO duplicate = CollUtil.getFirst(iqcMapper.selectListByImportKey(reqVO.getItemId(), reqVO.getVendorId(),
+                reqVO.getVendorBatch(), reqVO.getReceiveDate()));
+        if (duplicate != null) {
+            throw MesQcImportHelper.duplicateExistingException(buildImportKeyDesc(row), "鏉ユ枡妫�楠屽崟",
+                    duplicate.getCode(), "璐ㄩ噺绠$悊-鏉ユ枡妫�楠�");
+        }
+        // 6. 澶嶇敤鍒涘缓閫昏緫锛氭牎楠岀墿鏂�/渚涘簲鍟�/浜哄憳銆佹寜鐗╂枡鍖归厤璐ㄦ鏂规銆佺敓鎴愭楠岃
+        try {
+            createIqc(reqVO);
+        } catch (ServiceException ex) {
+            MesQcImportHelper.rethrowTemplateError(ex, row.getItemCode(), "鏉ユ枡妫�楠�(IQC)");
+        }
+        return code;
+    }
+
+    /**
+     * 瀵煎叆鍘婚噸閿細鍚屼竴 鐗╂枡 + 渚涘簲鍟� + 渚涘簲鍟嗘壒娆� + 鏉ユ枡鏃ユ湡 瑙嗕负鍚屼竴鎵规潵鏂欐暟鎹�
+     */
+    private String buildImportKey(MesQcIqcImportExcelVO row) {
+        return String.join("|", StrUtil.trimToEmpty(row.getItemCode()), StrUtil.trimToEmpty(row.getVendorName()),
+                StrUtil.trimToEmpty(row.getVendorBatch()), StrUtil.trimToEmpty(row.getReceiveDate()));
+    }
+
+    private String buildImportKeyDesc(MesQcIqcImportExcelVO row) {
+        return "鐗╂枡[" + MesQcImportHelper.describeImportValue(row.getItemCode())
+                + "]銆佷緵搴斿晢[" + MesQcImportHelper.describeImportValue(row.getVendorName())
+                + "]銆佷緵搴斿晢鎵规[" + MesQcImportHelper.describeImportValue(row.getVendorBatch())
+                + "]銆佹潵鏂欐棩鏈焄" + MesQcImportHelper.describeImportValue(row.getReceiveDate()) + "]";
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerService.java
new file mode 100644
index 0000000..055f3a8
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerService.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.mes.service.qc.ledger;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerPageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerRespVO;
+
+/**
+ * MES 妫�娴嬬粨鏋滃彴璐� Service 鎺ュ彛
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface MesQcInspectionLedgerService {
+
+    /**
+     * 鑾峰緱妫�娴嬬粨鏋滃彴璐﹀垎椤碉紙鍥涚被妫�楠屽崟缁熶竴鏌ヨ锛屼竴琛屼竴涓娴嬮」缁撴灉锛�
+     *
+     * @param pageReqVO 鍒嗛〉鏌ヨ
+     * @return 鍙拌处鍒嗛〉
+     */
+    PageResult<MesQcInspectionLedgerRespVO> getInspectionLedgerPage(MesQcInspectionLedgerPageReqVO pageReqVO);
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerServiceImpl.java
new file mode 100644
index 0000000..eff0bea
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ledger/MesQcInspectionLedgerServiceImpl.java
@@ -0,0 +1,75 @@
+package cn.iocoder.yudao.module.mes.service.qc.ledger;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerPageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerRespVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO;
+import cn.iocoder.yudao.module.mes.dal.mysql.qc.ledger.MesQcInspectionLedgerMapper;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
+import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
+
+/**
+ * MES 妫�娴嬬粨鏋滃彴璐� Service 瀹炵幇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class MesQcInspectionLedgerServiceImpl implements MesQcInspectionLedgerService {
+
+    @Resource
+    private MesQcInspectionLedgerMapper ledgerMapper;
+
+    @Resource
+    private MdmItemApi mdmItemApi;
+    @Resource
+    private MdmUnitMeasureApi mdmUnitMeasureApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @Override
+    public PageResult<MesQcInspectionLedgerRespVO> getInspectionLedgerPage(MesQcInspectionLedgerPageReqVO pageReqVO) {
+        // 鍒嗛〉鏌ヨ锛堝洓绫绘楠屽崟 UNION ALL锛�
+        IPage<MesQcInspectionLedgerRespVO> page = MyBatisUtils.buildPage(pageReqVO);
+        ledgerMapper.selectInspectionLedgerPage(page, pageReqVO);
+        List<MesQcInspectionLedgerRespVO> list = page.getRecords();
+        if (CollUtil.isEmpty(list)) {
+            return PageResult.empty(page.getTotal());
+        }
+        // 鎵归噺鏌ヨ鐗╂枡銆佽閲忓崟浣嶃�佹娴嬩汉鍛橈紝骞舵嫾瑁呭垽瀹氫緷鎹紙鐗╂枡涓庤閲忓崟浣嶅潎鏉ヨ嚜 MDM锛�
+        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(convertSet(list, MesQcInspectionLedgerRespVO::getItemId));
+        Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureMap(
+                convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId));
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
+                convertSet(list, MesQcInspectionLedgerRespVO::getInspectorUserId));
+        for (MesQcInspectionLedgerRespVO vo : list) {
+            findAndThen(itemMap, vo.getItemId(), item -> {
+                vo.setItemCode(item.getCode());
+                vo.setItemName(item.getName());
+                vo.setItemSpecification(item.getSpecification());
+                findAndThen(unitMeasureMap, item.getUnitMeasureId(), unit -> vo.setUnitName(unit.getName()));
+            });
+            findAndThen(userMap, vo.getInspectorUserId(), user -> vo.setInspectorName(user.getNickname()));
+            vo.setJudgementBasis(MesQcQualityExportVO.buildRangeText(
+                    vo.getStandardValue(), vo.getMinThreshold(), vo.getMaxThreshold()));
+        }
+        return new PageResult<>(list, page.getTotal());
+    }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcService.java
index b6425fd..b9ac350 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcService.java
@@ -1,8 +1,10 @@
 package cn.iocoder.yudao.module.mes.service.qc.oqc;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
@@ -89,4 +91,12 @@
      */
     Long createOqcFromProductSales(Long salesId, MesWmProductSalesLineDO line, String salesCode, Long clientId);
 
+    /**
+     * 鎵归噺瀵煎叆鍑鸿揣妫�楠屽崟
+     *
+     * @param importList 瀵煎叆琛屽垪琛�
+     * @return 瀵煎叆缁撴灉锛堟垚鍔熺紪鍙峰垪琛� + 閫愯澶辫触鍘熷洜锛�
+     */
+    MesQcImportRespVO importOqcList(List<MesQcOqcImportExcelVO> importList);
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java
index e50b21e..fcecfbe 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java
@@ -2,19 +2,26 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 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.qc.oqc.vo.MesQcOqcImportExcelVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcSaveReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcImportRespVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.qc.oqc.MesQcOqcMapper;
+import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
 import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
+import cn.iocoder.yudao.module.mes.service.qc.imports.MesQcImportHelper;
 import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
 import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
 import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesLineService;
@@ -35,7 +42,11 @@
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -52,6 +63,9 @@
 
     @Resource
     private MesQcOqcMapper oqcMapper;
+
+    @Resource
+    private MesQcImportHelper qcImportHelper;
 
     @Resource
     private MesQcTemplateItemService templateItemService;
@@ -397,4 +411,124 @@
         return "鐗╂枡ID:" + itemId;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public MesQcImportRespVO importOqcList(List<MesQcOqcImportExcelVO> importList) {
+        MesQcImportRespVO respVO = MesQcImportRespVO.builder()
+                .createList(new ArrayList<>()).failureList(new LinkedHashMap<>()).build();
+        if (CollUtil.isEmpty(importList)) {
+            return respVO;
+        }
+        qcImportHelper.startImport();
+        Map<String, Integer> firstRowNoByKey = new HashMap<>();
+        for (int i = 0; i < importList.size(); i++) {
+            MesQcOqcImportExcelVO row = importList.get(i);
+            if (isImportRowEmpty(row)) {
+                continue; // 璺宠繃绌鸿
+            }
+            String label = StrUtil.isNotBlank(row.getCode()) ? "妫�楠屽崟缂栧彿 " + row.getCode().trim()
+                    : "绗� " + (i + 2) + " 琛�";
+            String importKey = buildImportKey(row);
+            try {
+                Integer firstRowNo = firstRowNoByKey.get(importKey);
+                if (firstRowNo != null) {
+                    throw MesQcImportHelper.duplicateRowException(firstRowNo, buildImportKeyDesc(row));
+                }
+                respVO.getCreateList().add(createOqcByImport(row));
+                firstRowNoByKey.putIfAbsent(importKey, i + 2);
+            } catch (Exception ex) {
+                respVO.getFailureList().put(label, MesQcImportHelper.resolveImportErrorMessage(ex));
+            }
+        }
+        return respVO;
+    }
+
+    private boolean isImportRowEmpty(MesQcOqcImportExcelVO row) {
+        return row == null || (StrUtil.isBlank(row.getCode()) && StrUtil.isBlank(row.getName())
+                && StrUtil.isBlank(row.getItemCode()) && StrUtil.isBlank(row.getClientName())
+                && StrUtil.isBlank(row.getBatchCode()) && StrUtil.isBlank(row.getTemplateName())
+                && row.getOutQuantity() == null && row.getCheckQuantity() == null
+                && row.getQualifiedQuantity() == null && row.getUnqualifiedQuantity() == null
+                && StrUtil.isBlank(row.getCheckResult()) && StrUtil.isBlank(row.getInspectorName())
+                && StrUtil.isBlank(row.getInspectDate()) && StrUtil.isBlank(row.getOutDate())
+                && StrUtil.isBlank(row.getRemark()));
+    }
+
+    /**
+     * 渚濇嵁瀵煎叆琛屽垱寤轰竴寮犲嚭璐ф楠屽崟锛氱姸鎬佷负銆屽緟妫�楠屻�嶏紝妫�楠岃鐢辫川妫�鏂规鑷姩鐢熸垚
+     *
+     * 鏈垎鏀殑鍑鸿揣妫�楠屽崟涓嶅己鍒跺叧鑱斿鎴枫�佷笉寮哄埗濉啓鍑鸿揣鏁伴噺涓庡嚭璐ф棩鏈燂紝鏁呰繖涓夊垪鐣欑┖涓嶆姤閿欍��
+     *
+     * @return 妫�楠屽崟缂栧彿
+     */
+    private String createOqcByImport(MesQcOqcImportExcelVO row) {
+        MesQcOqcSaveReqVO reqVO = new MesQcOqcSaveReqVO();
+        // 1. 妫�楠屽崟缂栧彿锛氬~鍐欏垯浣跨敤锛岀暀绌鸿嚜鍔ㄧ敓鎴�
+        String code = StrUtil.isNotBlank(row.getCode()) ? row.getCode().trim()
+                : qcImportHelper.generateCode(MesMdAutoCodeRuleCodeEnum.QC_OQC_CODE.getCode());
+        if (oqcMapper.selectByCode(code) != null) {
+            throw new IllegalArgumentException("妫�楠屽崟缂栧彿宸插瓨鍦細" + code
+                    + "銆傝淇敼妯℃澘涓殑銆屾楠屽崟缂栧彿銆嶏紝鎴栧厛鍦ㄧ郴缁熶腑鍒犻櫎璇ョ紪鍙风殑妫�楠屽崟鍚庨噸鏂板鍏�");
+        }
+        reqVO.setCode(code);
+        // 2. 鍚嶇О銆佺墿鏂欍�佸鎴枫�佹壒娆°�佽川妫�鏂规
+        if (StrUtil.isBlank(row.getName())) {
+            throw new IllegalArgumentException("妫�楠屽崟鍚嶇О涓嶈兘涓虹┖锛氳濉啓妯℃澘涓琛岀殑銆屾楠屽崟鍚嶇О銆嶅垪");
+        }
+        reqVO.setName(row.getName().trim());
+        reqVO.setItemId(qcImportHelper.resolveItemId(row.getItemCode()));
+        Long clientId = qcImportHelper.resolveClientId(row.getClientName());
+        if (clientId != null) {
+            // 鏈~瀹㈡埛鏃朵笉鏍¢獙鏁版嵁鏉冮檺锛堝崟鎹笉灞炰簬浠讳綍瀹㈡埛锛�
+            qcImportHelper.validateClientPermission(clientId, row.getClientName().trim());
+        }
+        reqVO.setClientId(clientId);
+        reqVO.setBatchCode(StrUtil.trimToNull(row.getBatchCode()));
+        reqVO.setTemplateId(qcImportHelper.resolveTemplateId(row.getTemplateName()));
+        // 3. 鏁伴噺锛氬悎鏍煎搧 + 涓嶅悎鏍煎搧 蹇呴』绛変簬妫�娴嬫暟閲忥紙妫�娴嬫暟閲忕暀绌哄垯鑷姩鍚堣锛夛紱鍑鸿揣鏁伴噺鍙暀绌�
+        reqVO.setOutQuantity(qcImportHelper.resolveQuantity(row.getOutQuantity(), "鏈鍑鸿揣鏁伴噺", false));
+        BigDecimal qualified = qcImportHelper.resolveQuantity(row.getQualifiedQuantity(), "鍚堟牸鍝佹暟閲�", true);
+        BigDecimal unqualified = qcImportHelper.resolveQuantity(row.getUnqualifiedQuantity(), "涓嶅悎鏍煎搧鏁伴噺", true);
+        reqVO.setQualifiedQuantity(qualified);
+        reqVO.setUnqualifiedQuantity(unqualified);
+        reqVO.setCheckQuantity(qcImportHelper.resolveCheckQuantity(
+                row.getCheckQuantity(), qualified, unqualified, "妫�娴嬫暟閲�"));
+        // 4. 妫�楠屼俊鎭�
+        reqVO.setCheckResult(qcImportHelper.resolveDictValue(row.getCheckResult(),
+                DictTypeConstants.MES_QC_CHECK_RESULT, "妫�娴嬬粨鏋�", false));
+        reqVO.setInspectorUserId(qcImportHelper.resolveInspectorUserId(row.getInspectorName()));
+        reqVO.setInspectDate(qcImportHelper.parseRequiredDateTime(row.getInspectDate(), "妫�娴嬫棩鏈�"));
+        reqVO.setOutDate(qcImportHelper.parseDateTime(row.getOutDate(), "鍑鸿揣鏃ユ湡"));
+        reqVO.setRemark(StrUtil.trimToNull(row.getRemark()));
+        // 5. 鍘婚噸锛氬悓涓� 鐗╂枡 + 瀹㈡埛 + 鎵规鍙� + 鍑鸿揣鏃ユ湡 鐨勫嚭璐ф楠屽崟涓嶅彲閲嶅瀵煎叆
+        MesQcOqcDO duplicate = CollUtil.getFirst(oqcMapper.selectListByImportKey(reqVO.getItemId(), reqVO.getClientId(),
+                reqVO.getBatchCode(), reqVO.getOutDate()));
+        if (duplicate != null) {
+            throw MesQcImportHelper.duplicateExistingException(buildImportKeyDesc(row), "鍑鸿揣妫�楠屽崟",
+                    duplicate.getCode(), "璐ㄩ噺绠$悊-鍑鸿揣妫�楠�");
+        }
+        // 6. 澶嶇敤鍒涘缓閫昏緫锛氭牎楠岀墿鏂�/浜哄憳銆佹寜鐗╂枡鍖归厤璐ㄦ鏂规銆佺敓鎴愭楠岃
+        try {
+            createOqc(reqVO);
+        } catch (ServiceException ex) {
+            MesQcImportHelper.rethrowTemplateError(ex, row.getItemCode(), "鍑鸿揣妫�楠�(OQC)");
+        }
+        return code;
+    }
+
+    /**
+     * 瀵煎叆鍘婚噸閿細鍚屼竴 鐗╂枡 + 瀹㈡埛 + 鎵规鍙� + 鍑鸿揣鏃ユ湡 瑙嗕负鍚屼竴鎵瑰嚭璐ф楠屾暟鎹�
+     */
+    private String buildImportKey(MesQcOqcImportExcelVO row) {
+        return String.join("|", StrUtil.trimToEmpty(row.getItemCode()), StrUtil.trimToEmpty(row.getClientName()),
+                StrUtil.trimToEmpty(row.getBatchCode()), StrUtil.trimToEmpty(row.getOutDate()));
+    }
+
+    private String buildImportKeyDesc(MesQcOqcImportExcelVO row) {
+        return "鐗╂枡[" + MesQcImportHelper.describeImportValue(row.getItemCode())
+                + "]銆佸鎴穂" + MesQcImportHelper.describeImportValue(row.getClientName())
+                + "]銆佹壒娆″彿[" + MesQcImportHelper.describeImportValue(row.getBatchCode())
+                + "]銆佸嚭璐ф棩鏈焄" + MesQcImportHelper.describeImportValue(row.getOutDate()) + "]";
+    }
+
 }
diff --git a/yudao-module-mes/src/main/resources/mapper/qc/ledger/MesQcInspectionLedgerMapper.xml b/yudao-module-mes/src/main/resources/mapper/qc/ledger/MesQcInspectionLedgerMapper.xml
new file mode 100644
index 0000000..6048d03
--- /dev/null
+++ b/yudao-module-mes/src/main/resources/mapper/qc/ledger/MesQcInspectionLedgerMapper.xml
@@ -0,0 +1,176 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.iocoder.yudao.module.mes.dal.mysql.qc.ledger.MesQcInspectionLedgerMapper">
+
+    <!--
+        妫�娴嬬粨鏋滃彴璐︽煡璇紙鍥涚被妫�楠屽崟 UNION ALL锛�
+        涓�琛� = 涓�涓娴嬮」缁撴灉锛涙楠屽崟鏈綍鍏ユ楠岀粨鏋滄椂璇ュ崟鍗犱竴琛岋紝妫�娴嬮」鐩稿叧鍒椾负绌猴紙LEFT JOIN 淇濊瘉涓嶄涪鍗曪級銆�
+        璇存槑锛氭楠岀粨鏋滅殑鍏宠仈閿负 (qc_id, qc_type)锛宷c_id 涓哄悇妫�楠屽崟鑷韩鐨� id锛岄渶鎸夌被鍨嬭繃婊� qc_type锛屽惁鍒欎笉鍚岀被鍨嬬殑 id 浼氫覆鍗曘��
+        璇存槑锛氭娴嬮」鍚嶇О涓嶅仛閫昏緫鍒犻櫎杩囨护锛屽凡鍋滅敤/鍒犻櫎鐨勬娴嬮」浠嶅睍绀哄巻鍙插彴璐﹀悕绉般��
+    -->
+    <select id="selectInspectionLedgerPage" resultType="cn.iocoder.yudao.module.mes.controller.admin.qc.ledger.vo.MesQcInspectionLedgerRespVO">
+        SELECT * FROM (
+            <!-- 1. 鏉ユ枡妫�楠岋紙IQC锛夛紝鎵规鍙栦緵搴斿晢鎵规鍙� -->
+            SELECT
+                ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@IQC.type} AS qcType,
+                q.id AS qcId,
+                q.code AS code,
+                q.name AS name,
+                q.status AS status,
+                q.item_id AS itemId,
+                q.vendor_batch AS batchNo,
+                q.check_quantity AS checkQuantity,
+                q.qualified_quantity AS qualifiedQuantity,
+                q.unqualified_quantity AS unqualifiedQuantity,
+                q.check_result AS checkResult,
+                q.inspect_date AS inspectDate,
+                q.inspector_user_id AS inspectorUserId,
+                q.remark AS remark,
+                d.indicator_id AS indicatorId,
+                i.name AS indicatorName,
+                d.value AS indicatorValue,
+                d.remark AS indicatorRemark,
+                l.standard_value AS standardValue,
+                l.min_threshold AS minThreshold,
+                l.max_threshold AS maxThreshold
+            FROM mes_qc_iqc q
+                LEFT JOIN mes_qc_indicator_result r ON r.qc_id = q.id
+                    AND r.qc_type = ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@IQC.type}
+                    AND r.deleted = 0
+                LEFT JOIN mes_qc_indicator_result_detail d ON d.result_id = r.id AND d.deleted = 0
+                LEFT JOIN mes_qc_indicator i ON i.id = d.indicator_id
+                LEFT JOIN mes_qc_iqc_line l ON l.iqc_id = q.id AND l.indicator_id = d.indicator_id AND l.deleted = 0
+            WHERE q.deleted = 0
+
+            UNION ALL
+
+            <!-- 2. 杩囩▼妫�楠岋紙IPQC锛夛紝鎵规鍙峰彇杩囩▼鐨勬壒鍙� -->
+            SELECT
+                ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@IPQC.type} AS qcType,
+                q.id AS qcId,
+                q.code AS code,
+                q.name AS name,
+                q.status AS status,
+                q.item_id AS itemId,
+                q.batch_code AS batchNo,
+                q.check_quantity AS checkQuantity,
+                q.qualified_quantity AS qualifiedQuantity,
+                q.unqualified_quantity AS unqualifiedQuantity,
+                q.check_result AS checkResult,
+                q.inspect_date AS inspectDate,
+                q.inspector_user_id AS inspectorUserId,
+                q.remark AS remark,
+                d.indicator_id AS indicatorId,
+                i.name AS indicatorName,
+                d.value AS indicatorValue,
+                d.remark AS indicatorRemark,
+                l.standard_value AS standardValue,
+                l.min_threshold AS minThreshold,
+                l.max_threshold AS maxThreshold
+            FROM mes_qc_ipqc q
+                LEFT JOIN mes_qc_indicator_result r ON r.qc_id = q.id
+                    AND r.qc_type = ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@IPQC.type}
+                    AND r.deleted = 0
+                LEFT JOIN mes_qc_indicator_result_detail d ON d.result_id = r.id AND d.deleted = 0
+                LEFT JOIN mes_qc_indicator i ON i.id = d.indicator_id
+                LEFT JOIN mes_qc_ipqc_line l ON l.ipqc_id = q.id AND l.indicator_id = d.indicator_id AND l.deleted = 0
+            WHERE q.deleted = 0
+
+            UNION ALL
+
+            <!-- 3. 鍑鸿揣妫�楠岋紙OQC锛夛紝鎵规鍙栨壒娆″彿 -->
+            SELECT
+                ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@OQC.type} AS qcType,
+                q.id AS qcId,
+                q.code AS code,
+                q.name AS name,
+                q.status AS status,
+                q.item_id AS itemId,
+                q.batch_code AS batchNo,
+                q.check_quantity AS checkQuantity,
+                q.qualified_quantity AS qualifiedQuantity,
+                q.unqualified_quantity AS unqualifiedQuantity,
+                q.check_result AS checkResult,
+                q.inspect_date AS inspectDate,
+                q.inspector_user_id AS inspectorUserId,
+                q.remark AS remark,
+                d.indicator_id AS indicatorId,
+                i.name AS indicatorName,
+                d.value AS indicatorValue,
+                d.remark AS indicatorRemark,
+                l.standard_value AS standardValue,
+                l.min_threshold AS minThreshold,
+                l.max_threshold AS maxThreshold
+            FROM mes_qc_oqc q
+                LEFT JOIN mes_qc_indicator_result r ON r.qc_id = q.id
+                    AND r.qc_type = ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@OQC.type}
+                    AND r.deleted = 0
+                LEFT JOIN mes_qc_indicator_result_detail d ON d.result_id = r.id AND d.deleted = 0
+                LEFT JOIN mes_qc_indicator i ON i.id = d.indicator_id
+                LEFT JOIN mes_qc_oqc_line l ON l.oqc_id = q.id AND l.indicator_id = d.indicator_id AND l.deleted = 0
+            WHERE q.deleted = 0
+
+            UNION ALL
+
+            <!-- 4. 閫�璐ф楠岋紙RQC锛夛紝鎵规鍙栨壒娆″彿 -->
+            SELECT
+                ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@RQC.type} AS qcType,
+                q.id AS qcId,
+                q.code AS code,
+                q.name AS name,
+                q.status AS status,
+                q.item_id AS itemId,
+                q.batch_code AS batchNo,
+                q.check_quantity AS checkQuantity,
+                q.qualified_quantity AS qualifiedQuantity,
+                q.unqualified_quantity AS unqualifiedQuantity,
+                q.check_result AS checkResult,
+                q.inspect_date AS inspectDate,
+                q.inspector_user_id AS inspectorUserId,
+                q.remark AS remark,
+                d.indicator_id AS indicatorId,
+                i.name AS indicatorName,
+                d.value AS indicatorValue,
+                d.remark AS indicatorRemark,
+                l.standard_value AS standardValue,
+                l.min_threshold AS minThreshold,
+                l.max_threshold AS maxThreshold
+            FROM mes_qc_rqc q
+                LEFT JOIN mes_qc_indicator_result r ON r.qc_id = q.id
+                    AND r.qc_type = ${@cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum@RQC.type}
+                    AND r.deleted = 0
+                LEFT JOIN mes_qc_indicator_result_detail d ON d.result_id = r.id AND d.deleted = 0
+                LEFT JOIN mes_qc_indicator i ON i.id = d.indicator_id
+                LEFT JOIN mes_qc_rqc_line l ON l.rqc_id = q.id AND l.indicator_id = d.indicator_id AND l.deleted = 0
+            WHERE q.deleted = 0
+        ) t
+        <where>
+            <if test="reqVO.qcType != null">
+                AND t.qcType = #{reqVO.qcType}
+            </if>
+            <if test="reqVO.inspectDate != null and reqVO.inspectDate.length > 0 and reqVO.inspectDate[0] != null">
+                AND t.inspectDate &gt;= #{reqVO.inspectDate[0]}
+            </if>
+            <if test="reqVO.inspectDate != null and reqVO.inspectDate.length > 1 and reqVO.inspectDate[1] != null">
+                AND t.inspectDate &lt;= #{reqVO.inspectDate[1]}
+            </if>
+            <if test="reqVO.code != null and reqVO.code != ''">
+                AND t.code LIKE CONCAT('%', #{reqVO.code}, '%')
+            </if>
+            <if test="reqVO.itemId != null">
+                AND t.itemId = #{reqVO.itemId}
+            </if>
+            <if test="reqVO.batchNo != null and reqVO.batchNo != ''">
+                AND t.batchNo LIKE CONCAT('%', #{reqVO.batchNo}, '%')
+            </if>
+            <if test="reqVO.checkResult != null">
+                AND t.checkResult = #{reqVO.checkResult}
+            </if>
+            <if test="reqVO.inspectorUserId != null">
+                AND t.inspectorUserId = #{reqVO.inspectorUserId}
+            </if>
+        </where>
+        ORDER BY t.inspectDate DESC, t.qcId DESC, t.qcType ASC
+    </select>
+
+</mapper>

--
Gitblit v1.9.3