From 4ca4444ad33a2a1a51f5c06e78ee9365dfb1eed6 Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期四, 08 十月 2026 17:11:11 +0800
Subject: [PATCH] feat(mes): 种子二维码标签国标四要素合规改造

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java                             |   47 ++
 yudao-server/src/main/resources/static/trace.html                                                                     |  107 ++++-
 docs/seed_qr_label_gb_compliance_integration.md                                                                       |  159 +++++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesProperties.java                        |   30 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/MesProBagCodeController.java  |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeService.java              |   19 +
 yudao-server/src/main/resources/application-thzy.yaml                                                                 |    5 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java          |   16 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesQrCodeConfig.java                      |  122 +++++++
 yudao-server/src/main/resources/application.yaml                                                                      |   14 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java            |   63 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/QrCodeImageUtils.java                     |   55 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java             |   33 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicConfigRespVO.java |   27 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java       |   15 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java                              |    7 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/MesProTracePublicController.java      |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/MesProQrLabelBuilder.java                 |  189 +++++++++-
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicService.java                |   11 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/trace/MesTraceCodeStatusEnum.java                    |   36 ++
 yudao-server/src/main/resources/application-local.yaml                                                                |    5 
 21 files changed, 895 insertions(+), 81 deletions(-)

diff --git a/docs/seed_qr_label_gb_compliance_integration.md b/docs/seed_qr_label_gb_compliance_integration.md
new file mode 100644
index 0000000..044f3a6
--- /dev/null
+++ b/docs/seed_qr_label_gb_compliance_integration.md
@@ -0,0 +1,159 @@
+# 绉嶅瓙浜岀淮鐮佹爣绛惧浗鏍囧悎瑙� - 鍓嶇鑱旇皟鏂规
+
+## 鑳屾櫙
+
+渚濇嵁銆婂啘浣滅墿绉嶅瓙鏍囩浜岀淮鐮佺紪鐮佽鍒欍�嬩笌銆婂啘浣滅墿绉嶅瓙鏍囩閫氬垯銆嬶紙GB 20464-2024锛夛紝
+姣忎釜浜岀淮鐮佸搴斿敮涓�鐨勬渶灏忛攢鍞崟鍏冪瀛愪笖涓嶅緱閲嶅浣跨敤锛屼簩缁寸爜蹇呴』鍖呭惈鍥涢」淇℃伅骞舵寜椤哄簭鎺掑垪锛�
+
+1. 鍝佺鍚嶇О
+2. 鐢熶骇缁忚惀鑰呭悕绉帮紙杩涘彛绉嶅瓙涓鸿繘鍙e晢鍚嶇О锛�
+3. 鍗曞厓璇嗗埆浠g爜锛堜笉瓒呰繃 20 涓瓧绗︼紝鏁板瓧鎴栧瓧姣嶆暟瀛楃粍鍚堬級
+4. 杩芥函缃戝潃
+
+鏈鏀归�犲皢銆岀敓浜х粡钀ヨ�呭悕绉般�嶄笌銆岃拷婧綉鍧�銆嶆敹鏁涗负鍚庣缁熶竴鏁版嵁婧愶紝骞惰宸蹭綔搴熺殑杩芥函鐮佸澶栧け鏁堛��
+
+## 娑夊強椤甸潰
+
+- H5 鎵爜婧簮椤� `trace.html`锛堝凡瀹屾垚鏀归�狅紝鏃犻渶鍓嶇鍙﹁澶勭悊锛�
+- 绠$悊鍚庡彴 - 璧嬬爜绠$悊椤碉紙鑿滃崟 `mes/pro/bagCode/index`锛屽墠绔伐绋嬩腑**褰撳墠缂哄け**锛岄渶鏂板缓锛�
+- 绠$悊鍚庡彴 - 鎵樼洏鐮佺鐞嗛〉 / 鎵规鐢熶骇绠$悊椤碉紙浜岀淮鐮侀瑙堜笌瀵煎嚭锛屽鏈夛級
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 | 閴存潈 |
+|------|------|------|------|
+| GET | `/public/mes/trace/config` | **鏂板**锛氳幏寰楁函婧愰〉閰嶇疆锛堢敓浜х粡钀ヨ�呭悕绉般�佽拷婧綉鍧�锛� | 鍏嶇櫥褰� |
+| GET | `/public/mes/trace/query` | 杩芥函鏌ヨ锛�**鍝嶅簲鏂板 3 涓瓧娈�** | 鍏嶇櫥褰� |
+| GET | `/admin-api/mes/pro/bag-code/export-qr` | 瀵煎嚭浜岀淮鐮佹爣绛� ZIP锛�**鏂板鏍¢獙涓庢嫆缁濋�昏緫** | `mes:pro-bag-code:export` |
+| GET | `/admin-api/mes/pro/bag-code/export-excel` | 瀵煎嚭琚嬬爜 Excel | `mes:pro-bag-code:export` |
+
+### 1. 鏂板锛氳幏寰楁函婧愰〉閰嶇疆
+
+`GET /public/mes/trace/config`
+
+**璇锋眰鍙傛暟锛�** 鏃�
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+  "code": 0,
+  "data": {
+    "operatorName": "鏂扮枂濉旈噷鏈ㄦ渤绉嶄笟鑲′唤鏈夐檺鍏徃",
+    "traceUrl": "http://1.95.78.124:9008/prod/trace.html",
+    "imported": false
+  },
+  "msg": ""
+}
+```
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| operatorName | String | 鐢熶骇缁忚惀鑰呭悕绉帮紱`imported=true` 鏃朵负杩涘彛鍟嗗悕绉� |
+| traceUrl | String | 杩芥函缃戝潃锛屼笌浜岀淮鐮佸唴瀹瑰墠缂�瀹屽叏涓�鑷达紙涓嶅惈 `code` 鍙傛暟锛� |
+| imported | Boolean | 鏄惁杩涘彛绉嶅瓙鍦烘櫙锛涗负 true 鏃堕〉闈㈠瓧娈靛悕搴旀樉绀恒�岃繘鍙e晢鍚嶇О銆� |
+
+### 2. 杩芥函鏌ヨ鍝嶅簲鏂板瀛楁
+
+`GET /public/mes/trace/query?code=xxx`
+
+**鍝嶅簲瀛楁鏂板锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| codeStatus | String | `VALID`-鏈夋晥 / `CANCELED`-宸蹭綔搴� |
+| operatorName | String | 鐢熶骇缁忚惀鑰呭悕绉帮紙涓庢爣绛惧嵃鍒峰唴瀹瑰悓婧愶級 |
+| traceUrl | String | 杩芥函缃戝潃锛堜笌鏍囩鍗板埛鍐呭鍚屾簮锛� |
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+  "code": 0,
+  "data": {
+    "sourceType": "BAG",
+    "requestCode": "0286267849370923",
+    "codeStatus": "VALID",
+    "operatorName": "鏂扮枂濉旈噷鏈ㄦ渤绉嶄笟鑲′唤鏈夐檺鍏徃",
+    "traceUrl": "http://1.95.78.124:9008/prod/trace.html",
+    "batchBound": false,
+    "itemCode": "xxx",
+    "itemName": "濉旀渤4鍙�",
+    "itemUnitMeasureName": "48鍗冨厠"
+  }
+}
+```
+
+## 瀛楁灞曠ず瑙勫垯
+
+### H5 婧簮椤碉紙杩芥函鏌ヨ鍖哄潡锛�
+
+鍥涜绱犲繀椤绘寜鍥芥爣椤哄簭灞曠ず锛屽叾浣欎俊鎭帓鍒楀叾鍚庯細
+
+| 椤哄簭 | 瀛楁 | 鍙栧�兼潵婧� | 璇存槑 |
+|------|------|----------|------|
+| 1 | 鍝佺鍚嶇О | `itemName` + `itemSpecification` | 鍥芥爣瑕佺礌鈶� |
+| 2 | 鐢熶骇缁忚惀鑰� / 杩涘彛鍟嗗悕绉� | `operatorName`锛岄��鍥� `/config` 鐨� `operatorName` | 鍥芥爣瑕佺礌鈶★紱`imported=true` 鏃舵爣绛炬敼涓恒�岃繘鍙e晢鍚嶇О銆� |
+| 3 | 鍗曞厓璇嗗埆浠g爜 | `requestCode` 鍘� URL 鍚庣殑鐮佸�� | 鍥芥爣瑕佺礌鈶€��**浠呰鐮侊紙`sourceType=BAG`锛変娇鐢ㄦ鍚嶇О**锛涙墭鐩樼爜/鎵规鐮佸睍绀轰负銆屾墭鐩樼爜锛堥潪鏈�灏忛攢鍞崟鍏冿級銆嶃�屾壒娆$爜锛堥潪鏈�灏忛攢鍞崟鍏冿級銆嶏紝閬垮厤璇 |
+| 4 | 杩芥函缃戝潃 | `traceUrl`锛岄��鍥� `/config` 鐨� `traceUrl` | 鍥芥爣瑕佺礌鈶o紝鍙偣鍑昏烦杞� |
+| 5+ | 璁¢噺鍗曚綅銆佽拷婧被鍨嬨�佹壒娆″彿銆佺敓浜ф棩鏈熴�佷骇绾� | 鍚勮嚜瀛楁 | 杈呭姪淇℃伅 |
+
+### H5 婧簮椤碉紙鐢熶骇鏄庣粏鎶樺彔鍖猴級
+
+鍥芥爣瑕佹眰杩芥函缃戝潃搴斿彲鏌ヨ銆岀瀛愬姞宸ユ壒娆°�嶅強銆岀墿娴侀攢鍞俊鎭�嶏紝鍥犳浠ヤ笅鍖哄潡**瀵瑰灞曠ず**锛堥粯璁ゆ姌鍙犱笉灞曞紑锛夛細
+
+| 鍖哄潡 | 鏁版嵁鏉ユ簮 | 璇存槑 |
+|------|----------|------|
+| 鐢熶骇鎵规淇℃伅 | `batchCode`銆乣batchStatusName`銆乣produceDate`銆乣lineName`銆乣planQuantity` 绛� | 瀵瑰簲鍥芥爣銆岀瀛愬姞宸ユ壒娆°�嶏紱`batchBound=false`锛堟湭褰曞叆鎵规锛夋椂**涓嶅睍绀鸿鍖哄潡**锛岄伩鍏嶆殫绀烘棤鍔犲伐鎵规 |
+| 绉嶅瓙娴佸悜淇℃伅 | `seedFlows`锛堥攢鍞嚭搴� bizType=119锛夋垨 `salesTargets` | 瀵瑰簲鍥芥爣銆岀墿娴侀攢鍞俊鎭�嶏紝鍚潵婧愬崟鍙枫�佸鎴枫�佹暟閲忋�佷粨搴�/搴撳尯 |
+| 鍔犲伐娴佽浆 | `processTraces` | 鍔犲伐鑺傜偣娴佽浆璁板綍 |
+
+浠ヤ笅鍖哄潡**浠呭唴閮ㄥ彲瑙侊紝涓嶅澶栧睍绀�**锛堝闇�涓�骞跺睍绀猴紝灏� `detailSections(d, false)` 鏀逛负 `detailSections(d, true)`锛夛細
+
+| 鍖哄潡 | 涓嶅睍绀哄師鍥� |
+|------|-----------|
+| 鐢熶骇浜哄憳 | 娑夊強涓汉淇℃伅 |
+| 璐ㄦ淇℃伅銆佹墭鐩樹俊鎭�佽鐮佸垪琛ㄣ�侀噰璐潵婧� | 鍐呴儴鏄庣粏锛岄潪娑堣垂鑰呭叧鍒� |
+
+### 绠$悊鍚庡彴 - 璧嬬爜绠$悊椤�
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鍝佺鍚嶇О | 鍒楄〃銆佽鎯� | 琚嬬爜缁戝畾鐨勫搧绉� |
+| 鍗曞厓璇嗗埆浠g爜 | 鍒楄〃銆佽鎯� | 鍗宠鐮� `code` |
+| 鐘舵�� | 鍒楄〃 | 宸茬敓鎴� / 宸插鍑哄嵃鍒� / 宸蹭綔搴� |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 瀵煎嚭浜岀淮鐮佹爣绛� | 鍥涜绱犱换涓�缂哄け鍒�**鎷掔粷瀵煎嚭骞舵姤閿�**锛屼笉鍐嶉潤榛樿烦杩囩┖瀛楁 |
+| 鍥涜绱犵己澶辨彁绀� | 缂哄皯鍝佺鍚嶇О 鈫� 1040750021锛涚己灏戠敓浜х粡钀ヨ�� 鈫� 1040750022锛涚己灏戝崟鍏冭瘑鍒唬鐮� 鈫� 1040750023锛涙湭閰嶇疆杩芥函缃戝潃 鈫� 1040750020 |
+| 鍗曞厓璇嗗埆浠g爜鏍煎紡 | 蹇呴』涓� 1~20 浣嶆暟瀛楁垨瀛楁瘝鏁板瓧缁勫悎锛屽惈杩炲瓧绗︾瓑绗﹀彿鏃舵姤 1040750024 |
+| 鎵爜鍛戒腑宸蹭綔搴熻鐮� | 杩斿洖 `codeStatus=CANCELED`锛岄〉闈㈡樉绀恒�岃杩芥函鐮佸凡浣滃簾锛岄潪鏈叕鍙告湁鏁堜骇鍝侊紝璋ㄩ槻鍋囧啋銆嶏紝涓斾笉鍐嶅睍绀烘函婧愯鎯� |
+| 宸蹭綔搴熺爜琚壂鎻� | **浠嶈褰曟壂鐮佷簨浠�**锛堜綔搴熺爜琚壂鏄噸瑕侀槻浼俊鍙凤級锛屼笉璁′负鏌ヨ澶辫触 |
+| 鏈厤缃拷婧綉鍧� | 闈� local 鐜鍚姩鏃剁洿鎺ュけ璐ワ紱local 鐜鍙�氳繃 `mes.allow-plain-qr-code=true` 鍏煎鍘嗗彶鐮� |
+| 瀵煎嚭浜岀淮鐮佹爣绛� | **宸蹭綔搴熺殑琚嬬爜涓嶅啀瀵煎嚭**锛堟寜鎵规/鐢熸垚缁勫鍑烘椂鍧囨帓闄わ級銆備綔搴熺爜鏍囩鑻ュ嵃鍑哄苟璐村埌浜у搧涓婏紝娑堣垂鑰呮壂鐮佷細琚彁绀恒�岃杩芥函鐮佸凡浣滃簾锛岃皑闃插亣鍐掋�嶏紝瀵艰嚧鐪熷搧琚鍒や负鍋囪揣 |
+
+## 閰嶇疆椤�
+
+鍚庣閰嶇疆椤癸紙`application-*.yaml` 鐨� `mes` 鍓嶇紑锛夛細
+
+| 閰嶇疆椤� | 蹇呭~ | 璇存槑 |
+|--------|------|------|
+| `mes.trace-base-url` | 鏄� | 杩芥函缃戝潃锛屽繀椤讳负鍏紬鍙闂湴鍧�銆傞潪 local 鐜鑻ヤ负鍐呯綉 IP / localhost / 鍗犱綅绗﹀皢**鎷掔粷鍚姩** |
+| `mes.operator-name` | 鏄� | 鐢熶骇缁忚惀鑰呭悕绉般�傛湭閰嶇疆鍒欐棤娉曞鍑烘爣绛撅紙鍚姩鏃跺憡璀︼級 |
+| `mes.importer-name` | 鍚� | 杩涘彛鍟嗗悕绉帮紝杩涘彛绉嶅瓙鍦烘櫙濉啓 |
+| `mes.imported` | 鍚� | 鏄惁杩涘彛绉嶅瓙鍦烘櫙锛岄粯璁� false |
+| `mes.allow-plain-qr-code` | 鍚� | 鏄惁鍏佽浜岀淮鐮佺紪鐮佺函鐮佸�硷紝浠呬緵鏈湴鍏煎鍘嗗彶鐮侊紝榛樿 false |
+
+## 娉ㄦ剰浜嬮」
+
+- **鍓嶇绂佹鍐嶇‖缂栫爜鐢熶骇缁忚惀鑰呭悕绉颁笌杩芥函缃戝潃**锛屽繀椤婚�氳繃 `/public/mes/trace/config` 鑾峰彇锛屽惁鍒欎細涓庝簩缁寸爜鍐呭涓嶄竴鑷淬��
+- 杩芥函缃戝潃鍦ㄤ簩缁寸爜鍐呭涓庨〉闈㈠睍绀轰箣闂村彧鏈� `mes.trace-base-url` 涓�涓湡鍊兼簮锛屽墠绔睍绀烘椂涓嶈鑷鎷兼帴 `/prod` 绛夎矾寰勩��
+- 宸蹭綔搴熺爜鐨勫垽瀹氳鐩栦笁绉嶅叆鍙o細琚嬬爜锛堢姸鎬� 30锛夈�佹墭鐩樼爜锛堢姸鎬� 20锛夈�佹壒娆$爜锛堢姸鎬� 40锛夛紝涓夎�呯殑浣滃簾鏋氫妇鍊间笉鍚岋紝鍓嶇涓嶈鍏辩敤甯搁噺鍒ゆ柇銆�
+- **瀛橀噺鏍囩闇�閲嶆柊瀵煎嚭**锛氭敼閫犲墠瀵煎嚭鐨勪簩缁寸爜鍐呭宸插浐鍖栵紝鍏朵腑杩芥函缃戝潃鍙兘涓哄唴缃戝湴鍧�涓旀爣绛剧己灏戝洓瑕佺礌锛岄渶閲嶆柊瀵煎嚭鍗板埛銆�
+- **琚嬪簭鍙锋敼涓哄師瀛愬垎閰�**锛氬師鍏堢敱銆屾煡搴撳唴鏈�澶у�煎啀 +1銆嶅緱鍒帮紝骞跺彂鐢熸垚浼氫骇鐢熼噸澶嶈搴忓彿锛堣搴忓彿浼氬嵃鍦ㄦ爣绛句笂锛夛紱鐜板凡鏀逛负 Redis 鍘熷瓙鑷鍒嗛厤锛屽苟鐢ㄥ簱鍐呮渶澶у�兼牎姝e熀鍑嗐�傚簭鍙�**鍙繚璇佷笉閲嶅銆佷笉鍥為��锛屼笉淇濊瘉杩炵画**锛堝苟鍙戜笅涓嶅悓璇锋眰鎷垮埌涓嶅悓鍖烘锛屽睘姝e父锛夈��
+- 绠$悊鍚庡彴璧嬬爜绠$悊椤甸潰锛坄mes/pro/bagCode/index`锛夊湪鍓嶇宸ョ▼涓己澶憋紝涓氬姟浜哄憳鐩墠鏃犳硶鑷姪鏌ョ湅/瀵煎嚭浜岀淮鐮侊紝寤鸿琛ュ厖銆�
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/MesProBagCodeController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/MesProBagCodeController.java
index 8e6ca6e..46b6160 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/MesProBagCodeController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/MesProBagCodeController.java
@@ -143,7 +143,8 @@
 
     @GetMapping("/get-by-code")
     @Operation(summary = "鎵爜杩芥函锛氭牴鎹鐮侊紙浜岀淮鐮佸唴瀹癸級鍙嶆煡鎵规銆佷骇绾裤�佽矗浠讳汉绛変俊鎭�")
-    @Parameter(name = "code", description = "琚嬬爜锛堜簩缁寸爜鍐呭锛�", required = true, example = "PC20260818L01001-0001")
+    @Parameter(name = "code", description = "鍗曞厓璇嗗埆浠g爜锛堝浗鏍囷細涓嶈秴杩� 20 涓瓧绗︼紝鏁板瓧鎴栧瓧姣嶆暟瀛楃粍鍚堬級",
+            required = true, example = "0286267849370923")
     @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:query')")
     public CommonResult<MesProBagCodeTraceRespVO> getBagCodeByCode(@RequestParam("code") String code) {
         MesProBagCodeDO bagCode = bagCodeService.getBagCodeByCode(code);
@@ -197,11 +198,12 @@
                                 HttpServletResponse response) throws IOException {
         List<MesProBagCodeDO> list;
         String zipName;
+        // 鍙鍑哄彲鍗板埛鐨勮鐮侊細宸蹭綔搴熻鐮佺殑鏍囩涓嶅緱鍐嶅嵃鍒凤紙璐村埌浜у搧涓婁細琚壂鐮佹彁绀�"宸蹭綔搴燂紝璋ㄩ槻鍋囧啋"锛�
         if (StrUtil.isNotEmpty(uuid)) {
-            list = bagCodeService.getBagCodeListByUuid(uuid);
+            list = bagCodeService.getPrintableBagCodeListByUuid(uuid);
             zipName = "琚嬬爜浜岀淮鐮�-" + uuid;
         } else if (batchId != null) {
-            list = bagCodeService.getBagCodeListByBatchId(batchId);
+            list = bagCodeService.getPrintableBagCodeListByBatchId(batchId);
             zipName = "琚嬬爜浜岀淮鐮�-" + batchId;
         } else {
             throw exception(PRO_QR_EXPORT_PARAMS_INVALID);
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/MesProTracePublicController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/MesProTracePublicController.java
index 6d1f910..4fa596d 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/MesProTracePublicController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/MesProTracePublicController.java
@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.mes.controller.pub.trace;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.mes.controller.pub.trace.vo.MesProTracePublicConfigRespVO;
 import cn.iocoder.yudao.module.mes.controller.pub.trace.vo.MesProTracePublicRespVO;
 import cn.iocoder.yudao.module.mes.service.trace.MesProTracePublicService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -38,4 +39,11 @@
         return success(service.queryTrace(code));
     }
 
+    @GetMapping("/config")
+    @PermitAll
+    @Operation(summary = "鑾峰緱婧簮椤甸厤缃紙鐢熶骇缁忚惀鑰呭悕绉般�佽拷婧綉鍧�锛�")
+    public CommonResult<MesProTracePublicConfigRespVO> getConfig() {
+        return success(service.getPublicConfig());
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicConfigRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicConfigRespVO.java
new file mode 100644
index 0000000..b494fe5
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicConfigRespVO.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.mes.controller.pub.trace.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * H5 鎵爜婧簮 - 椤甸潰閰嶇疆 Response VO
+ * <p>
+ * 鍚� H5 婧簮椤典笅鍙戙�岀敓浜х粡钀ヨ�呭悕绉般�嶄笌銆岃拷婧綉鍧�銆嶏紝浜岃�呭潎涓虹瀛愭爣绛惧洓瑕佺礌鐨勭粍鎴愰儴鍒嗐��
+ * 鐢卞悗绔粺涓�涓嬪彂锛屼繚璇佷簩缁寸爜鍐呭銆佹爣绛惧嵃鍒峰唴瀹逛笌 H5 椤甸潰灞曠ず涓夎�呬竴鑷达紝
+ * 閬垮厤鍓嶇纭紪鐮佸鑷寸殑澶氱湡鍊兼簮涓嶄竴鑷撮棶棰樸��
+ */
+@Schema(description = "H5 鎵爜婧簮 - 椤甸潰閰嶇疆 Response VO")
+@Data
+public class MesProTracePublicConfigRespVO {
+
+    @Schema(description = "鐢熶骇缁忚惀鑰呭悕绉帮紙杩涘彛绉嶅瓙鍦烘櫙涓鸿繘鍙e晢鍚嶇О锛�", example = "鏂扮枂濉旈噷鏈ㄦ渤绉嶄笟鑲′唤鏈夐檺鍏徃")
+    private String operatorName;
+
+    @Schema(description = "杩芥函缃戝潃锛堜笌浜岀淮鐮佸唴瀹瑰墠缂�涓�鑷达紝涓嶅惈 code 鍙傛暟锛�",
+            example = "http://1.95.78.124:9008/prod/trace.html")
+    private String traceUrl;
+
+    @Schema(description = "鏄惁杩涘彛绉嶅瓙鍦烘櫙锛氫负 true 鏃堕〉闈㈡爣绛惧簲灞曠ず銆岃繘鍙e晢鍚嶇О銆�")
+    private Boolean imported;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java
index 7c1e32e..060ca21 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java
@@ -24,12 +24,25 @@
     @Schema(description = "杩芥函鍏ュ彛绫诲瀷锛欱AG-琚嬬爜 / PALLET-鎵樼洏鐮� / BATCH-鎵规鐮�", example = "BAG")
     private String sourceType;
 
-    @Schema(description = "璇锋眰鐨勮拷婧爜锛堝師濮嬫壂鐮佸�硷級", example = "PC20260828twoITEM_001")
+    @Schema(description = "璇锋眰鐨勮拷婧爜锛堝師濮嬫壂鐮佸�硷級", example = "0286267849370923")
     private String requestCode;
+
+    @Schema(description = "杩芥函鐮佹湁鏁堟�э細VALID-鏈夋晥 / CANCELED-宸蹭綔搴燂紙浣滃簾鐮佷笉寰椾綔涓烘湁鏁堜骇鍝佹祦閫氾級",
+            example = "VALID")
+    private String codeStatus;
 
     @Schema(description = "鏄惁宸插綍鍏ョ敓浜ф壒娆★紙false 琛ㄧず琚嬬爜/鎵樼洏宸茬敓鎴愪絾灏氭湭褰曞叆鎵规锛屾鏃朵粎杩斿洖鍝佺淇℃伅锛�", example = "true")
     private Boolean batchBound;
 
+    // ====== 鏍囩鍥涜绱犲尯鍧楋紙涓庝簩缁寸爜鏍囩鍗板埛鍐呭淇濇寔涓�鑷达級 ======
+    @Schema(description = "鐢熶骇缁忚惀鑰呭悕绉帮紙杩涘彛绉嶅瓙鍦烘櫙涓鸿繘鍙e晢鍚嶇О锛�",
+            example = "鏂扮枂濉旈噷鏈ㄦ渤绉嶄笟鑲′唤鏈夐檺鍏徃")
+    private String operatorName;
+
+    @Schema(description = "杩芥函缃戝潃锛堜笌浜岀淮鐮佸唴瀹瑰墠缂�涓�鑷达級",
+            example = "http://1.95.78.124:9008/prod/trace.html")
+    private String traceUrl;
+
     // ====== 鍝佺淇℃伅鍖哄潡 ======
     @Schema(description = "鍝佺缂栫爜")
     private String itemCode;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java
index effcd75..7024430 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java
@@ -5,6 +5,7 @@
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodePageReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeStatusEnum;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -72,6 +73,29 @@
         return selectList(MesProBagCodeDO::getBatchId, batchId);
     }
 
+    /**
+     * 鏌ヨ鐢熸垚缁勪笅鍙嵃鍒风殑琚嬬爜锛堟帓闄ゅ凡浣滃簾锛夛紝鐢ㄤ簬浜岀淮鐮佹爣绛惧鍑�
+     * <p>
+     * 宸蹭綔搴熻鐮佺殑鏍囩涓嶅緱鍐嶅嵃鍒凤細鍏舵爣绛捐嫢璐村埌浜у搧涓婏紝娑堣垂鑰呮壂鐮佷細琚彁绀恒�岃杩芥函鐮佸凡浣滃簾銆嶏紝
+     * 瀵艰嚧鐪熷搧琚鍒や负鍋囪揣銆�
+     */
+    default List<MesProBagCodeDO> selectPrintableListByUuid(String uuid) {
+        return selectList(new LambdaQueryWrapperX<MesProBagCodeDO>()
+                .eq(MesProBagCodeDO::getUuid, uuid)
+                .ne(MesProBagCodeDO::getStatus, MesProBagCodeStatusEnum.CANCELED.getStatus())
+                .orderByAsc(MesProBagCodeDO::getBagNo));
+    }
+
+    /**
+     * 鏌ヨ鎵规涓嬪彲鍗板埛鐨勮鐮侊紙鎺掗櫎宸蹭綔搴燂級锛岀敤浜庝簩缁寸爜鏍囩瀵煎嚭
+     */
+    default List<MesProBagCodeDO> selectPrintableListByBatchId(Long batchId) {
+        return selectList(new LambdaQueryWrapperX<MesProBagCodeDO>()
+                .eq(MesProBagCodeDO::getBatchId, batchId)
+                .ne(MesProBagCodeDO::getStatus, MesProBagCodeStatusEnum.CANCELED.getStatus())
+                .orderByAsc(MesProBagCodeDO::getBagNo));
+    }
+
     default Long selectCountByBatchId(Long batchId) {
         return selectCount(MesProBagCodeDO::getBatchId, batchId);
     }
@@ -137,10 +161,13 @@
     }
 
     /**
-     * 鏌ヨ鎵规鍐呰鐮佹渶澶у簭鍙凤紙鍖呭惈宸查�昏緫鍒犻櫎璁板綍锛屼繚璇佸簭鍙蜂笉鍥為��銆佺爜鍊煎敮涓�锛�
+     * 鏌ヨ鍏ㄥ眬鏈�澶ц搴忓彿锛堣搴忓彿涓哄叏灞�搴忓彿锛屼笉鍖哄垎鎵规锛涘寘鍚凡閫昏緫鍒犻櫎璁板綍锛屼繚璇佸簭鍙蜂笉鍥為��锛�
+     * <p>
+     * 浠呯敤浜庝负 Redis 搴忓彿鍒嗛厤鎻愪緵鍩哄噯鍊硷紝涓嶈鍦ㄤ笟鍔′腑鐩存帴鐢ㄣ�岃繑鍥炲�� + 1銆嶄綔涓鸿捣濮嬪簭鍙封�斺��
+     * 閭f槸鍏堣鍚庡啓锛屽苟鍙戠敓鎴愪細浜х敓閲嶅搴忓彿銆傝浣跨敤
+     * {@code MesCodeRedisDAO#allocateBagNoStart(int, Integer)} 鍘熷瓙鍒嗛厤銆�
      *
-     * @param batchId 鎵规缂栧彿
-     * @return 鏈�澶ц搴忓彿锛屾棤璁板綍杩斿洖 0
+     * @return 鍏ㄥ眬鏈�澶ц搴忓彿锛屾棤璁板綍杩斿洖 0
      */
     @Select("SELECT IFNULL(MAX(bag_no), 0) FROM mes_pro_bag_code")
     Integer selectMaxBagNo();
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java
index b0a9318..5a805eb 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java
@@ -3,7 +3,11 @@
 import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
 import jakarta.annotation.Resource;
 import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
 import org.springframework.stereotype.Repository;
+
+import java.util.List;
 
 /**
  * MES 涓氬姟缂栫爜鐢熸垚鐨� Redis DAO锛堝疄渚嬬爜锛氳鐮� / 鎵樼洏鐮� / 涓存椂鐮佺瓑锛夈��
@@ -13,6 +17,26 @@
  */
 @Repository
 public class MesCodeRedisDAO {
+
+    /**
+     * 琚嬪簭鍙疯嚜澧� KEY锛堝叏灞�琚嬪簭鍙凤紝淇濊瘉鍚屼竴鎵�/璺ㄦ壒鐢熸垚鏃朵笉閲嶅锛�
+     */
+    private static final String BAG_NO_SEQ_KEY = "mes:bag_no:seq";
+
+    /**
+     * 鍘熷瓙鍒嗛厤琚嬪簭鍙峰尯闂寸殑 Lua 鑴氭湰銆�
+     * <p>
+     * 鍏堟寜搴撳唴鏈�澶у簭鍙锋牎姝e熀鍑嗗�硷紙Redis 棣栨浣跨敤銆佹垨搴撳唴鏁版嵁琚洿鎺ュ鍏ュ鑷村熀鍑嗚惤鍚庢椂锛夛紝
+     * 鍐嶅師瀛愯嚜澧炲苟杩斿洖鑷鍚庣殑鍊笺�傛暣涓�屾牎姝� + 鑷銆嶅湪 Redis 鍗曠嚎绋嬪唴瀹屾垚锛岄伩鍏嶅苟鍙戜笅浜掔浉瑕嗙洊璁℃暟銆�
+     * <p>
+     * KEYS[1] = 搴忓彿 KEY锛汚RGV[1] = 鏈闇�瑕佺殑涓暟锛汚RGV[2] = 搴撳唴褰撳墠鏈�澶у簭鍙�
+     */
+    private static final RedisScript<Long> ALLOCATE_BAG_NO_SCRIPT = new DefaultRedisScript<>(
+            "local cur = redis.call('GET', KEYS[1]) "
+                    + "local base = tonumber(ARGV[2]) "
+                    + "if (not cur) or (tonumber(cur) < base) then redis.call('SET', KEYS[1], base) end "
+                    + "return redis.call('INCRBY', KEYS[1], ARGV[1])",
+            Long.class);
 
     @Resource
     private StringRedisTemplate stringRedisTemplate;
@@ -34,4 +58,27 @@
         throw new IllegalStateException("鐢熸垚鍞竴涓氬姟缂栫爜澶辫触锛岃閲嶈瘯");
     }
 
+    /**
+     * 鍘熷瓙鍒嗛厤涓�娈佃繛缁殑琚嬪簭鍙凤紝杩斿洖鍖洪棿璧峰鍊笺��
+     * <p>
+     * 琚嬪簭鍙峰師鍏堢敱銆屾煡搴撳唴鏈�澶у�煎啀 +1銆嶅緱鍒帮紝骞跺彂鐢熸垚鏃朵袱涓姹備細璇诲埌鍚屼竴涓渶澶у�硷紝
+     * 浠庤�屼骇鍑洪噸澶嶇殑琚嬪簭鍙凤紙琚嬪簭鍙蜂細鍗板湪鏍囩涓婏級銆傛澶勬敼涓� Redis 鍘熷瓙鑷鍒嗛厤锛�
+     * 骞剁敤搴撳唴褰撳墠鏈�澶у�煎仛鍩哄噯鏍℃锛屼繚璇佸簭鍙锋棦涓嶉噸澶嶄篃涓嶅洖閫�銆�
+     *
+     * @param count      鏈闇�瑕佺殑杩炵画搴忓彿涓暟锛堥』澶т簬 0锛�
+     * @param dbMaxBagNo 鏁版嵁搴撳綋鍓嶆渶澶ц搴忓彿锛堝惈宸查�昏緫鍒犻櫎璁板綍锛屼紶 null 瑙嗕负 0锛�
+     * @return 鍖洪棿璧峰搴忓彿锛堝惈锛夛紝鍙敤鍖洪棿涓� [start, start + count - 1]
+     */
+    public int allocateBagNoStart(int count, Integer dbMaxBagNo) {
+        if (count <= 0) {
+            throw new IllegalArgumentException("琚嬪簭鍙峰垎閰嶄釜鏁板繀椤诲ぇ浜� 0");
+        }
+        Long end = stringRedisTemplate.execute(ALLOCATE_BAG_NO_SCRIPT, List.of(BAG_NO_SEQ_KEY),
+                String.valueOf(count), String.valueOf(dbMaxBagNo == null ? 0 : dbMaxBagNo));
+        if (end == null) {
+            throw new IllegalStateException("鍒嗛厤琚嬪簭鍙峰け璐ワ紝璇烽噸璇�");
+        }
+        return (int) (end - count + 1);
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
index 17303ff..728ef9f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -860,6 +860,13 @@
     ErrorCode PRO_BAG_CODE_PALLET_COUNT_REQUIRED = new ErrorCode(1_040_752_013, "璇ュ搧绉嶆寜鎵樼洏鐢熸垚琚嬬爜锛屾墭鐩樻暟蹇呴』澶т簬 0");
     ErrorCode PRO_BAG_CODE_BIND_BATCH_ITEM_MISMATCH = new ErrorCode(1_040_752_014, "褰曞叆鎵规鐨勮鐮佸繀椤诲睘浜庡悓涓�鍝佺");
 
+    // ========== MES 绉嶅瓙鏍囩鍥芥爣鍥涜绱狅紙1-040-750-020锛� ==========
+    ErrorCode PRO_QR_LABEL_TRACE_URL_NOT_CONFIGURED = new ErrorCode(1_040_750_020, "鏈厤缃拷婧綉鍧�锛屾棤娉曠敓鎴愪簩缁寸爜鏍囩");
+    ErrorCode PRO_QR_LABEL_ITEM_NAME_MISSING = new ErrorCode(1_040_750_021, "鏍囩缂哄皯鍝佺鍚嶇О锛屾棤娉曞鍑�");
+    ErrorCode PRO_QR_LABEL_OPERATOR_MISSING = new ErrorCode(1_040_750_022, "鏍囩缂哄皯鐢熶骇缁忚惀鑰呭悕绉帮紝鏃犳硶瀵煎嚭");
+    ErrorCode PRO_QR_LABEL_UNIT_CODE_MISSING = new ErrorCode(1_040_750_023, "鏍囩缂哄皯鍗曞厓璇嗗埆浠g爜锛屾棤娉曞鍑�");
+    ErrorCode PRO_QR_LABEL_UNIT_CODE_INVALID = new ErrorCode(1_040_750_024, "鍗曞厓璇嗗埆浠g爜涓嶇鍚堝浗鏍囷細闀垮害涓嶈秴杩� 20 涓瓧绗︼紝涓斿彧鑳戒负鏁板瓧鎴栧瓧姣嶆暟瀛楃粍鍚�");
+
     // ========== MES 鐢熶骇绠$悊-鎵樼洏鐮侊紙1-040-753-000锛� ==========
     ErrorCode PRO_PALLET_NOT_EXISTS = new ErrorCode(1_040_753_000, "鎵樼洏鐮佷笉瀛樺湪");
     ErrorCode PRO_PALLET_HAS_BAG_NOT_DELETE = new ErrorCode(1_040_753_001, "鎵樼洏鐮佸凡缁戝畾琚嬬爜锛屼笉鍏佽鍒犻櫎");
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/trace/MesTraceCodeStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/trace/MesTraceCodeStatusEnum.java
new file mode 100644
index 0000000..ebc0d60
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/trace/MesTraceCodeStatusEnum.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.mes.enums.trace;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 杩芥函鐮佹湁鏁堟�х姸鎬佹灇涓撅紙鍏紑婧簮鏌ヨ缁撴灉锛�
+ * <p>
+ * 渚濇嵁銆婂啘浣滅墿绉嶅瓙鏍囩浜岀淮鐮佺紪鐮佽鍒欍�嬶紝姣忎釜浜岀淮鐮佸搴斿敮涓�鐨勬渶灏忛攢鍞崟鍏冧笖涓嶅緱閲嶅浣跨敤銆�
+ * 宸蹭綔搴熺殑琚嬬爜锛堢牬鎹熸浛鎹㈠悗浣滃簾锛夊繀椤诲澶栧け鏁堬紝鍚﹀垯浣滃簾鐮佷粛鍙鍐掔敤閿�鍞紝
+ * 鍥犳婧簮鏌ヨ闇�鏄庣‘鍖哄垎銆屾湁鏁堛�嶄笌銆屽凡浣滃簾銆嶃��
+ */
+@Getter
+@AllArgsConstructor
+public enum MesTraceCodeStatusEnum {
+
+    /**
+     * 鏈夋晥锛氱爜澶勪簬姝e父娴佽浆鐘舵��
+     */
+    VALID("VALID", "鏈夋晥"),
+
+    /**
+     * 宸蹭綔搴燂細琚嬬爜宸茶鐮存崯鏇挎崲娴佺▼浣滃簾锛屼笉寰椾綔涓烘湁鏁堜骇鍝佹祦閫�
+     */
+    CANCELED("CANCELED", "宸蹭綔搴�");
+
+    /**
+     * 鐘舵�佺紪鐮侊紙瀵瑰杈撳嚭锛�
+     */
+    private final String status;
+    /**
+     * 鐘舵�佸悕绉�
+     */
+    private final String name;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesProperties.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesProperties.java
index 4858583..cbbd9d1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesProperties.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesProperties.java
@@ -14,8 +14,36 @@
 
     /**
      * H5 婧簮椤甸潰鍦板潃锛坱race.html锛夊熀纭� URL锛岀敤浜庣敓鎴愭惡甯﹁拷婧爜鍙傛暟鐨勪簩缁寸爜鍐呭銆�
-     * 涓虹┖鏃朵簩缁寸爜浠嶇紪鐮佺函鐮佸�硷紙鍏煎鏃х爜锛夈��
+     * <p>
+     * 璇ュ�间负銆岃拷婧綉鍧�銆嶇殑鍞竴鐪熷�兼簮锛氫簩缁寸爜鍐呭涓� H5 椤甸潰灞曠ず鐨勮拷婧綉鍧�鍧囧彇鑷閰嶇疆銆�
+     * 蹇呴』涓哄叕浼楁壂鐮佸彲璁块棶鐨勫湴鍧�锛岄潪鏈湴鐜閰嶇疆涓哄唴缃�/鍗犱綅绗﹀湴鍧�鏃跺惎鍔ㄥ皢鐩存帴澶辫触銆�
      */
     private String traceBaseUrl = "";
 
+    /**
+     * 鐢熶骇缁忚惀鑰呭悕绉帮紙绉嶅瓙鏍囩鍥涜绱犱箣涓�锛夈��
+     * <p>
+     * 浣滀负銆岀敓浜х粡钀ヨ�呭悕绉般�嶇殑鍞竴鐪熷�兼簮锛屾爣绛惧嵃鍒蜂笌 H5 婧簮椤靛潎鍙栨鍊硷紝绂佹鍦ㄥ墠绔‖缂栫爜銆�
+     */
+    private String operatorName = "";
+
+    /**
+     * 杩涘彛鍟嗗悕绉帮紙杩涘彛绉嶅瓙鍦烘櫙锛屾爣绛惧洓瑕佺礌涔嬩竴锛夈��
+     * <p>
+     * 杩涘彛绉嶅瓙搴旀爣娉ㄨ繘鍙e晢鍚嶇О锛涗负绌烘椂鍥為��浣跨敤 {@link #operatorName}銆�
+     */
+    private String importerName = "";
+
+    /**
+     * 鏄惁澶勪簬杩涘彛绉嶅瓙鍦烘櫙锛氫负 true 鏃舵爣绛句笌婧簮椤靛睍绀恒�岃繘鍙e晢鍚嶇О銆嶈�岄潪銆岀敓浜х粡钀ヨ�呭悕绉般��
+     */
+    private boolean imported = false;
+
+    /**
+     * 鍏佽浜岀淮鐮佺紪鐮佺函鐮佸�硷紙涓嶅甫婧簮 URL锛夈��
+     * <p>
+     * 浠呬緵鏈湴/娴嬭瘯鐜鍏煎鍘嗗彶鐮佷娇鐢紱鐢熶骇鐜蹇呴』涓� false锛屽惁鍒欎細鍗板嚭涓嶅惈杩芥函缃戝潃鐨勬爣绛俱��
+     */
+    private boolean allowPlainQrCode = false;
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesQrCodeConfig.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesQrCodeConfig.java
index d4da7f7..e18c9f5 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesQrCodeConfig.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/config/MesQrCodeConfig.java
@@ -1,22 +1,142 @@
 package cn.iocoder.yudao.module.mes.framework.config;
 
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.module.mes.framework.qrcode.MesProQrLabelBuilder;
+import cn.iocoder.yudao.module.mes.framework.qrcode.QrCodeImageUtils;
 import jakarta.annotation.PostConstruct;
 import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+
+import java.net.URI;
+import java.util.Arrays;
 
 /**
- * MES 浜岀淮鐮佺浉鍏抽厤缃細鍚姩鏃跺皢 traceBaseUrl 娉ㄥ叆浜岀淮鐮佹爣绛炬瀯寤哄櫒锛岄伩鍏嶄镜鍏� Controller 璋冪敤鐐�
+ * MES 浜岀淮鐮佺浉鍏抽厤缃細鍚姩鏃舵牎楠屽苟鎶婇厤缃敞鍏ヤ簩缁寸爜鏍囩鏋勫缓鍣紝閬垮厤渚靛叆 Controller 璋冪敤鐐广��
+ * <p>
+ * 绉嶅瓙鏍囩灞炰簬鍙楀浗鏍囩害鏉熺殑鍚堣鐗╂枡锛屼竴鏃﹀嵃鍑哄嵆鏃犳硶鍙洖锛屽洜姝よ繖閲岄噰鍙栥�屽惎鍔ㄥ嵆鏍¢獙銆佷笉鍚堟硶鍗虫嫆缁濆惎鍔ㄣ��
+ * 鐨勭瓥鐣ワ紝鑰屼笉鏄瓑鍒板鍑烘椂鎵嶅け璐ユ垨闈欓粯闄嶇骇锛�
+ * <ul>
+ *     <li>杩芥函缃戝潃蹇呴』涓哄叕浼楀彲璁块棶鍦板潃锛岀姝㈠唴缃� IP / 鏈満鍦板潃 / 鏈浛鎹㈢殑鍗犱綅绗︼紱</li>
+ *     <li>鐢熶骇缁忚惀鑰呭悕绉板繀濉紙鏍囩鍥涜绱犱箣涓�锛岀己澶卞嵆涓轰笉鍚堣鏍囩锛夛紱</li>
+ *     <li>褰撳墠鐜蹇呴』瀛樺湪鍙覆鏌撲腑鏂囩殑瀛椾綋锛堝惁鍒欐爣绛句腑鏂囦細鍙樻垚鏂规锛夈��</li>
+ * </ul>
  */
+@Slf4j
 @Configuration(proxyBeanMethods = false)
 public class MesQrCodeConfig {
+
+    /**
+     * 鏈湴鐜 profile 鍚嶏細璇� profile 涓嬫斁瀹芥牎楠岋紝渚夸簬鏈湴寮�鍙戣皟璇�
+     */
+    private static final String PROFILE_LOCAL = "local";
 
     @Resource
     private MesProperties mesProperties;
 
+    @Resource
+    private Environment environment;
+
     @PostConstruct
     public void init() {
+        // 1. 娉ㄥ叆閰嶇疆锛堝瓧浣撴牎楠岀嫭绔嬩簬涓氬姟閰嶇疆锛屾湰鍦扮幆澧冨悓鏍峰繀椤婚�氳繃锛屽惁鍒欐爣绛句腑鏂囦笉鍙锛�
         MesProQrLabelBuilder.setTraceBaseUrl(mesProperties.getTraceBaseUrl());
+        MesProQrLabelBuilder.setOperatorName(mesProperties.getOperatorName());
+        MesProQrLabelBuilder.setImporterName(mesProperties.getImporterName());
+        MesProQrLabelBuilder.setImported(mesProperties.isImported());
+        MesProQrLabelBuilder.setAllowPlainQrCode(mesProperties.isAllowPlainQrCode());
+        QrCodeImageUtils.assertCjkFontAvailable();
+
+        // 2. 闈炴湰鍦扮幆澧冩墽琛屼弗鏍兼牎楠�
+        if (isLocalProfile()) {
+            log.info("[init] 褰撳墠涓烘湰鍦扮幆澧冿紙{}锛夛紝璺宠繃鏍囩鍚堣閰嶇疆鐨勪弗鏍兼牎楠岋紱traceBaseUrl={}",
+                    PROFILE_LOCAL, mesProperties.getTraceBaseUrl());
+            return;
+        }
+        validateTraceBaseUrl();
+        validateOperatorName();
+        log.info("[init] 浜岀淮鐮佹爣绛鹃厤缃牎楠岄�氳繃锛泃raceBaseUrl={}锛岀敓浜х粡钀ヨ��={}",
+                mesProperties.getTraceBaseUrl(), MesProQrLabelBuilder.resolveOperatorName());
+    }
+
+    private boolean isLocalProfile() {
+        return Arrays.asList(environment.getActiveProfiles()).contains(PROFILE_LOCAL);
+    }
+
+    /**
+     * 鏍¢獙杩芥函缃戝潃锛氬繀椤讳负鍏紬鎵爜鍙闂殑鍦板潃
+     */
+    private void validateTraceBaseUrl() {
+        String url = StrUtil.trim(mesProperties.getTraceBaseUrl());
+        if (StrUtil.isEmpty(url)) {
+            throw new IllegalStateException("mes.trace-base-url 鏈厤缃�備簩缁寸爜鏍囩蹇呴』鍖呭惈杩芥函缃戝潃锛�"
+                    + "璇峰湪閰嶇疆涓~鍐欏叕浼楁壂鐮佸彲璁块棶鐨� H5 婧簮椤靛湴鍧�銆�");
+        }
+        if (StrUtil.containsAnyIgnoreCase(url, "your-domain", "example.com", "localhost")) {
+            throw new IllegalStateException("mes.trace-base-url 浠嶄负鍗犱綅绗︽垨鏈満鍦板潃锛�" + url
+                    + "锛屽嵃鍑虹殑鏍囩娑堣垂鑰呭皢鏃犳硶璁块棶锛岃濉啓鐪熷疄鐨勫缃戝湴鍧�銆�");
+        }
+        String host = extractHost(url);
+        if (StrUtil.isEmpty(host)) {
+            throw new IllegalStateException("mes.trace-base-url 鏍煎紡闈炴硶锛屾棤娉曡В鏋愪富鏈哄悕锛�" + url);
+        }
+        if (isPrivateHost(host)) {
+            throw new IllegalStateException("mes.trace-base-url 鎸囧悜鍐呯綉鍦板潃锛�" + host + "锛夛細" + url
+                    + "锛屽叕浼楁壂鐮佹棤娉曡闂紝璇峰~鍐欏缃戝彲璁块棶鐨勫湴鍧�銆�");
+        }
+    }
+
+    /**
+     * 鏍¢獙鐢熶骇缁忚惀鑰呭悕绉帮細鏍囩鍥涜绱犱箣涓�锛岀己澶卞嵆涓轰笉鍚堣鏍囩
+     * <p>
+     * 姝ゅ浠呭憡璀︿笉闃绘柇鍚姩锛氱敓浜х粡钀ヨ�呭悕绉版寜涓讳綋鍖哄垎锛岄儴鍒嗙幆澧冨彲鑳藉皻鏈厤缃紝
+     * 寮哄埗鍚姩澶辫触浼氬奖鍝嶆棤鍏冲姛鑳姐�傜湡姝g殑鎷︽埅鐐瑰湪鏍囩鏋勫缓澶勶紙缂哄け鏃舵嫆缁濆鍑猴級锛�
+     * 鍥犳涓嶄細鍑虹幇"閰嶇疆缂哄け鍗村嵃鍑轰笉鍚堣鏍囩"鐨勬儏鍐点��
+     */
+    private void validateOperatorName() {
+        if (StrUtil.isBlank(MesProQrLabelBuilder.resolveOperatorName())) {
+            log.warn("[init] mes.operator-name锛堢敓浜х粡钀ヨ�呭悕绉帮級鏈厤缃�傝瀛楁涓虹瀛愭爣绛惧洓瑕佺礌涔嬩竴锛�"
+                    + "缂哄け鏃跺皢鏃犳硶瀵煎嚭浜岀淮鐮佹爣绛撅紝璇峰敖蹇湪閰嶇疆涓ˉ鍏呫��");
+        }
+    }
+
+    /**
+     * 浠� URL 涓彁鍙栦富鏈哄悕
+     */
+    private String extractHost(String url) {
+        try {
+            URI uri = URI.create(url);
+            return uri.getHost();
+        } catch (IllegalArgumentException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * 鍒ゆ柇鏄惁涓烘湰鏈� / 鍐呯綉 / 閾捐矾鏈湴鍦板潃
+     */
+    private boolean isPrivateHost(String host) {
+        if ("localhost".equalsIgnoreCase(host) || host.endsWith(".local")) {
+            return true;
+        }
+        String[] parts = host.split("\\.");
+        if (parts.length != 4) {
+            return false; // 鍩熷悕褰㈠紡锛屼氦鐢� DNS 瑙f瀽锛屼笉鍦ㄦ澶勬嫤鎴�
+        }
+        int a, b;
+        try {
+            a = Integer.parseInt(parts[0]);
+            b = Integer.parseInt(parts[1]);
+        } catch (NumberFormatException ex) {
+            return false;
+        }
+        // 127.0.0.0/8 鏈満銆�10.0.0.0/8銆�172.16.0.0/12銆�192.168.0.0/16 绉佺綉銆�169.254.0.0/16 閾捐矾鏈湴
+        return a == 127
+                || a == 10
+                || (a == 172 && b >= 16 && b <= 31)
+                || (a == 192 && b == 168)
+                || (a == 169 && b == 254);
     }
 
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/MesProQrLabelBuilder.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/MesProQrLabelBuilder.java
index d21aee2..65b9938 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/MesProQrLabelBuilder.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/MesProQrLabelBuilder.java
@@ -2,22 +2,42 @@
 
 import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO;
 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 
-import java.nio.charset.StandardCharsets;
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.regex.Pattern;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_LABEL_ITEM_NAME_MISSING;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_LABEL_OPERATOR_MISSING;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_LABEL_TRACE_URL_NOT_CONFIGURED;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_LABEL_UNIT_CODE_INVALID;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_QR_LABEL_UNIT_CODE_MISSING;
 
 /**
- * MES 浜岀淮鐮佹爣绛炬瀯寤哄櫒锛氱粺涓�"浜岀淮鐮佺紪鐮佸唴瀹� + 鍥剧墖鍙犲姞鍙鏂囨湰"鐨勬枃鏈粍瑁呰鍒欙紝
- * 淇濊瘉鎵规銆佹墭鐩樸�佽鐮佷笁绉嶆爣绛剧殑鍏抽敭淇℃伅淇濈暀涓�鑷淬��
+ * MES 浜岀淮鐮佹爣绛炬瀯寤哄櫒锛氱粺涓�"浜岀淮鐮佺紪鐮佸唴瀹� + 鍥剧墖鍙犲姞鍙鏂囨湰"鐨勬枃鏈粍瑁呰鍒欍��
  * <p>
- * 閰嶇疆浜� {@code mes.trace-base-url} 鏃讹紝浜岀淮鐮佸唴瀹逛负 H5 婧簮鍦板潃锛堝甫 code 鍙傛暟锛夛紝鍚﹀垯鍥為��涓虹函鐮佸�硷紙鍏煎鏃х爜锛�
+ * 绉嶅瓙鏍囩鍙椼�婂啘浣滅墿绉嶅瓙鏍囩浜岀淮鐮佺紪鐮佽鍒欍�嬩笌銆婂啘浣滅墿绉嶅瓙鏍囩閫氬垯銆嬶紙GB 20464-2024锛夌害鏉燂紝
+ * 鏈�灏忛攢鍞崟鍏冪殑鏍囩蹇呴』鍖呭惈鍥涢」淇℃伅涓旀寜鍥哄畾椤哄簭鎺掑垪锛�
+ * <ol>
+ *     <li>鍝佺鍚嶇О</li>
+ *     <li>鐢熶骇缁忚惀鑰呭悕绉帮紙杩涘彛绉嶅瓙涓鸿繘鍙e晢鍚嶇О锛�</li>
+ *     <li>鍗曞厓璇嗗埆浠g爜锛堜笉瓒呰繃 20 涓瓧绗︼紝鏁板瓧鎴栧瓧姣嶆暟瀛楃粍鍚堬級</li>
+ *     <li>杩芥函缃戝潃</li>
+ * </ol>
+ * 鍥涜绱犵己澶卞嵆鏋勬垚涓嶅悎瑙勬爣绛撅紝鍥犳杩欓噷閲囧彇銆岀己澶卞嵆鎶涘紓甯搞�佹嫆缁濆鍑恒�嶇殑绛栫暐锛岀粷涓嶉潤榛樿烦杩団�斺��
+ * 闈欓粯璺宠繃姝f槸鏍囩缂洪」娴佸叆鍗板埛鐜妭鐨勬牴鍥犮��
+ * <p>
+ * 浜岀淮鐮佸唴瀹瑰湪閰嶇疆浜� {@code mes.trace-base-url} 鏃朵负甯� code 鍙傛暟鐨勬函婧� URL锛屽惁鍒欐寜
+ * {@code mes.allow-plain-qr-code} 鍐冲畾鏄惁闄嶇骇涓虹函鐮佸�硷紙浠呴檺鏈湴鍏煎鍘嗗彶鐮侊級銆�
  */
 public final class MesProQrLabelBuilder {
 
@@ -25,20 +45,78 @@
     }
 
     /**
-     * H5 婧簮椤甸潰鍩虹鍦板潃锛岀敱 {@code MesQrCodeConfig} 鍚姩鏃舵敞鍏ワ紱涓虹┖鏃朵簩缁寸爜缂栫爜绾爜鍊�
+     * 鍗曞厓璇嗗埆浠g爜鏍煎紡锛�1~20 浣嶆暟瀛楁垨瀛楁瘝鏁板瓧缁勫悎锛堝浗鏍囪姹傦紝涓嶅惈杩炲瓧绗︾瓑绗﹀彿锛�
      */
+    private static final Pattern UNIT_CODE_PATTERN = Pattern.compile("^[0-9A-Za-z]{1,20}$");
+
+    /** H5 婧簮椤甸潰鍩虹鍦板潃锛岀敱 {@code MesQrCodeConfig} 鍚姩鏃舵敞鍏ワ紱涓虹┖鏃朵簩缁寸爜缂栫爜绾爜鍊� */
     private static volatile String traceBaseUrl = "";
+    /** 鐢熶骇缁忚惀鑰呭悕绉帮紝鐢� {@code MesQrCodeConfig} 鍚姩鏃舵敞鍏� */
+    private static volatile String operatorName = "";
+    /** 杩涘彛鍟嗗悕绉帮紝杩涘彛绉嶅瓙鍦烘櫙浣跨敤 */
+    private static volatile String importerName = "";
+    /** 鏄惁杩涘彛绉嶅瓙鍦烘櫙 */
+    private static volatile boolean imported = false;
+    /** 鏄惁鍏佽浜岀淮鐮侀檷绾х紪鐮佺函鐮佸�硷紙浠呴檺鏈湴鍏煎鍘嗗彶鐮侊級 */
+    private static volatile boolean allowPlainQrCode = false;
 
     public static void setTraceBaseUrl(String traceBaseUrl) {
         MesProQrLabelBuilder.traceBaseUrl = StrUtil.blankToDefault(traceBaseUrl, "");
     }
 
+    public static void setOperatorName(String operatorName) {
+        MesProQrLabelBuilder.operatorName = StrUtil.blankToDefault(operatorName, "");
+    }
+
+    public static void setImporterName(String importerName) {
+        MesProQrLabelBuilder.importerName = StrUtil.blankToDefault(importerName, "");
+    }
+
+    public static void setImported(boolean imported) {
+        MesProQrLabelBuilder.imported = imported;
+    }
+
+    public static void setAllowPlainQrCode(boolean allowPlainQrCode) {
+        MesProQrLabelBuilder.allowPlainQrCode = allowPlainQrCode;
+    }
+
     /**
-     * 缁勮浜岀淮鐮佸唴瀹癸細閰嶇疆浜嗘函婧愬湴鍧�鍒欐嫾 URL锛屽惁鍒欒繑鍥炵函鐮佸��
+     * 褰撳墠搴旀爣娉ㄧ殑鐢熶骇缁忚惀鑰呭悕绉帮細杩涘彛绉嶅瓙鍦烘櫙浼樺厛鍙栬繘鍙e晢鍚嶇О锛屽惁鍒欏彇鐢熶骇缁忚惀鑰呭悕绉�
+     *
+     * @return 鍚嶇О锛屾湭閰嶇疆鏃惰繑鍥炵┖涓�
+     */
+    public static String resolveOperatorName() {
+        if (imported && StrUtil.isNotBlank(importerName)) {
+            return importerName;
+        }
+        return operatorName;
+    }
+
+    /**
+     * 杩芥函缃戝潃锛堝澶栧睍绀虹敤锛屼笉鍚� code 鍙傛暟锛�
+     */
+    public static String resolveTraceUrl() {
+        return traceBaseUrl;
+    }
+
+    /**
+     * 鏄惁杩涘彛绉嶅瓙鍦烘櫙锛氫负 true 鏃舵爣绛句笌婧簮椤靛簲灞曠ず銆岃繘鍙e晢鍚嶇О銆�
+     */
+    public static boolean isImported() {
+        return imported;
+    }
+
+    /**
+     * 缁勮浜岀淮鐮佸唴瀹癸細閰嶇疆浜嗘函婧愬湴鍧�鍒欐嫾 URL锛屽惁鍒欐寜寮�鍏冲喅瀹氭槸鍚﹂檷绾т负绾爜鍊�
+     * <p>
+     * 鏈厤缃函婧愬湴鍧�涓旀湭寮�鍚檷绾у紑鍏虫椂鐩存帴鎶ラ敊锛岄伩鍏嶅嵃鍑轰笉鍚拷婧綉鍧�鐨勬爣绛俱��
      */
     private static String buildQrCodeContent(String code) {
         if (StrUtil.isEmpty(traceBaseUrl)) {
-            return code;
+            if (allowPlainQrCode) {
+                return code;
+            }
+            throw exception(PRO_QR_LABEL_TRACE_URL_NOT_CONFIGURED);
         }
         return traceBaseUrl + "?code=" + URLEncoder.encode(code, StandardCharsets.UTF_8);
     }
@@ -99,48 +177,115 @@
             keyValues.put("宸茶琚嬫暟", String.valueOf(pallet.getBagCount()));
         }
         if (batch != null) {
+            // 鍝佺鍗曠嫭鍙栨壒娆″搧绉嶏細fillBatchKeyValues 宸蹭笉鍐嶅啓鍏ュ搧绉嶏紝閬垮厤涓庤鐮佸搧绉嶄簰鐩歌鐩�
+            MesMdmItemDO batchItem = itemMap.get(batch.getItemId());
+            if (batchItem != null && StrUtil.isNotEmpty(batchItem.getName())) {
+                keyValues.put("鍝佺", batchItem.getName());
+            }
             fillBatchKeyValues(keyValues, batch, itemMap, userMap);
         }
         return QrLabelInfo.builder().fileName(code).code(buildQrCodeContent(code)).keyValues(keyValues).build();
     }
 
     /**
-     * 琚嬬爜浜岀淮鐮佹爣绛�
+     * 琚嬬爜浜岀淮鐮佹爣绛撅紙鏈�灏忛攢鍞崟鍏冿級
+     * <p>
+     * 鎸夊浗鏍囪姹傦紝鍥涜绱犵疆浜庢爣绛炬渶鍓嶅苟鎸夊浐瀹氶『搴忔帓鍒楋細鍝佺鍚嶇О 鈫� 鐢熶骇缁忚惀鑰� 鈫� 鍗曞厓璇嗗埆浠g爜 鈫� 杩芥函缃戝潃锛�
+     * 琚嬪簭鍙枫�佹壒娆″彿銆佷骇绾裤�佽矗浠讳汉绛夌敓浜т俊鎭綔涓鸿緟鍔╁唴瀹规帓鍒楀湪鍏跺悗銆傚洓瑕佺礌浠讳竴缂哄け鍗虫姏寮傚父锛屾嫆缁濆鍑恒��
      */
     public static QrLabelInfo buildBagCodeLabel(MesProBagCodeDO bagCode,
                                                 MesProBatchDO batch,
                                                 Map<Long, MesMdmItemDO> itemMap,
                                                 Map<Long, AdminUserRespDTO> userMap) {
         LinkedHashMap<String, String> keyValues = new LinkedHashMap<>();
-        keyValues.put("琚嬬爜", bagCode.getCode());
-        keyValues.put("琚嬪簭鍙�", bagCode.getBagNo() == null ? "" : String.valueOf(bagCode.getBagNo()));
-        keyValues.put("鎵规鍙�", bagCode.getBatchCode());
+
+        // ===== 鍥涜绱狅紙鍥芥爣瑕佹眰锛岄『搴忓浐瀹氾紝缂哄け鍗抽樆鏂鍑猴級=====
+        putRequired(keyValues, "鍝佺鍚嶇О", resolveItemName(bagCode, batch, itemMap),
+                PRO_QR_LABEL_ITEM_NAME_MISSING);
+        putRequired(keyValues, imported ? "杩涘彛鍟嗗悕绉�" : "鐢熶骇缁忚惀鑰呭悕绉�", resolveOperatorName(),
+                PRO_QR_LABEL_OPERATOR_MISSING);
+        putRequired(keyValues, "鍗曞厓璇嗗埆浠g爜", resolveUnitCode(bagCode.getCode()),
+                PRO_QR_LABEL_UNIT_CODE_MISSING);
+        putRequired(keyValues, "杩芥函缃戝潃", resolveTraceUrl(), PRO_QR_LABEL_TRACE_URL_NOT_CONFIGURED);
+
+        // ===== 浠ヤ笅涓鸿緟鍔╃敓浜т俊鎭� =====
+        if (bagCode.getBagNo() != null) {
+            keyValues.put("琚嬪簭鍙�", String.valueOf(bagCode.getBagNo()));
+        }
+        if (StrUtil.isNotEmpty(bagCode.getBatchCode())) {
+            keyValues.put("鎵规鍙�", bagCode.getBatchCode());
+        }
         if (StrUtil.isNotEmpty(bagCode.getPalletCode())) {
             keyValues.put("缁戝畾鎵樼洏", bagCode.getPalletCode());
-        }
-        // 鍝佺浼樺厛鍙栬鐮佽嚜韬紙鏁版嵁鍖呯敓鎴愭椂缁戝畾锛屾湭褰曟壒娆′篃鍙墦鍗帮級锛屽巻鍙茶鐮佸洖閫�鎵规鍝佺
-        if (StrUtil.isNotEmpty(bagCode.getItemName())) {
-            keyValues.put("鍝佺", bagCode.getItemName());
         }
         if (batch != null) {
             fillBatchKeyValues(keyValues, batch, itemMap, userMap);
         }
-        return QrLabelInfo.builder().fileName(bagCode.getCode()).code(buildQrCodeContent(bagCode.getCode())).keyValues(keyValues).build();
+        return QrLabelInfo.builder()
+                .fileName(bagCode.getCode())
+                .code(buildQrCodeContent(bagCode.getCode()))
+                .keyValues(keyValues)
+                .build();
     }
 
     /**
-     * 濉厖鎵规鍏叡鍙鏂囨湰锛堝搧绉嶃�佽鏍笺�佷骇绾裤�佺敓浜ф棩鏈熴�佽矗浠讳汉锛�
+     * 鍐欏叆蹇呭~瀛楁锛氬�间负绌烘椂鎶涘嚭瀵瑰簲寮傚父锛岀粷涓嶉潤榛樿烦杩�
+     */
+    private static void putRequired(LinkedHashMap<String, String> keyValues, String label,
+                                    String value, ErrorCode errorCode) {
+        if (StrUtil.isBlank(value)) {
+            throw exception(errorCode);
+        }
+        keyValues.put(label, value.trim());
+    }
+
+    /**
+     * 瑙f瀽鍝佺鍚嶇О锛氫紭鍏堝彇琚嬬爜鑷韩缁戝畾鐨勫搧绉嶏紙鏁版嵁鍖呯敓鎴愭椂鍐欏叆锛屾湭褰曟壒娆′篃鍙嵃鍒凤級锛�
+     * 琚嬬爜鏈粦瀹氬搧绉嶆椂鍥為��鎵规鍝佺銆�
+     */
+    private static String resolveItemName(MesProBagCodeDO bagCode, MesProBatchDO batch,
+                                          Map<Long, MesMdmItemDO> itemMap) {
+        if (StrUtil.isNotBlank(bagCode.getItemName())) {
+            return bagCode.getItemName();
+        }
+        if (batch == null) {
+            return null;
+        }
+        MesMdmItemDO item = itemMap.get(batch.getItemId());
+        return item == null ? null : item.getName();
+    }
+
+    /**
+     * 鏍¢獙骞惰繑鍥炲崟鍏冭瘑鍒唬鐮侊紙鍥芥爣锛氫笉瓒呰繃 20 涓瓧绗︼紝鏁板瓧鎴栧瓧姣嶆暟瀛楃粍鍚堬級
+     */
+    private static String resolveUnitCode(String code) {
+        if (StrUtil.isBlank(code)) {
+            return null;
+        }
+        if (!UNIT_CODE_PATTERN.matcher(code).matches()) {
+            throw exception(PRO_QR_LABEL_UNIT_CODE_INVALID);
+        }
+        return code;
+    }
+
+    /**
+     * 濉厖鎵规鍏叡鍙鏂囨湰锛堣鏍笺�佷骇绾裤�佺敓浜ф棩鏈熴�佽矗浠讳汉锛�
+     * <p>
+     * 娉ㄦ剰锛氭澶勪笉鍐嶅啓鍏ャ�屽搧绉嶃�嶏紝鍝佺鍚嶇О缁熶竴鐢� {@link #resolveItemName} 瑙f瀽锛�
+     * 閬垮厤鎵规鍝佺瑕嗙洊琚嬬爜鍝佺瀵艰嚧鏍囩涓庢函婧愰〉灞曠ず涓嶄竴鑷淬��
      */
     private static void fillBatchKeyValues(LinkedHashMap<String, String> keyValues, MesProBatchDO batch,
                                            Map<Long, MesMdmItemDO> itemMap, Map<Long, AdminUserRespDTO> userMap) {
         MesMdmItemDO item = itemMap.get(batch.getItemId());
-        if (item != null) {
-            keyValues.put("鍝佺", item.getName());
+        if (item != null && StrUtil.isNotEmpty(item.getSpecification())) {
             keyValues.put("瑙勬牸", item.getSpecification());
         }
-        keyValues.put("浜х嚎", StrUtil.join(" / ", batch.getLineName(), batch.getLineCode()));
-        keyValues.put("鐢熶骇鏃ユ湡", batch.getProduceDate() == null ? ""
-                : LocalDateTimeUtil.format(batch.getProduceDate(), "yyyy-MM-dd HH:mm"));
+        if (StrUtil.isNotEmpty(batch.getLineName()) || StrUtil.isNotEmpty(batch.getLineCode())) {
+            keyValues.put("浜х嚎", StrUtil.join(" / ", batch.getLineName(), batch.getLineCode()));
+        }
+        if (batch.getProduceDate() != null) {
+            keyValues.put("鐢熶骇鏃ユ湡", LocalDateTimeUtil.format(batch.getProduceDate(), "yyyy-MM-dd HH:mm"));
+        }
         AdminUserRespDTO user = userMap.get(batch.getOwnerUserId());
         if (user != null) {
             keyValues.put("璐d换浜�", user.getNickname());
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/QrCodeImageUtils.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/QrCodeImageUtils.java
index 864066c..a3fa297 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/QrCodeImageUtils.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/framework/qrcode/QrCodeImageUtils.java
@@ -28,7 +28,8 @@
 /**
  * 浜岀淮鐮佹爣绛惧浘鐗囧伐鍏风被
  * <p>
- * 鐢熸垚"浜岀淮鐮� + 鍙充晶鍙鏂囨湰"鐨勫悎鎴愬浘鐗囷細浜岀淮鐮佸彧缂栫爜绾爜鍊硷紝鏂囨湰鍙犲姞鍏抽敭淇℃伅锛�
+ * 鐢熸垚"浜岀淮鐮� + 鍙充晶鍙鏂囨湰"鐨勫悎鎴愬浘鐗囷細浜岀淮鐮佺紪鐮佸唴瀹圭敱璋冪敤鏂逛紶鍏ワ紙閰嶇疆浜嗘函婧愬湴鍧�鏃朵负
+ * 甯� code 鍙傛暟鐨勬函婧� URL锛屾湭閰嶇疆鏃朵负绾爜鍊硷級锛屽彸渚у彔鍔犲叧閿俊鎭枃鏈紝
  * 鍗充娇鏁版嵁搴撲涪澶变篃鑳戒粠鎵撳嵃鍥剧墖杩樺師鍏抽敭淇℃伅銆�
  */
 @Slf4j
@@ -55,11 +56,21 @@
     /** 瀛楁闂磋窛 */
     private static final int FIELD_GAP = 6;
 
-    /** 涓枃瀛椾綋鍊欓�夛紙鎸変紭鍏堢骇锛夛紝閬垮厤 Linux headless 涓嬫枃瀛楀彉鏂规 */
+    /**
+     * 涓枃瀛椾綋鍊欓�夛紙鎸変紭鍏堢骇锛夈��
+     * <p>
+     * 鍙厑璁告敹褰曞彲娓叉煋涓枃鐨勫瓧浣擄細绾媺涓佸瓧浣擄紙濡� Arial锛変竴鏃﹀叆閫夛紝涓枃浼氬叏閮ㄦ覆鏌撴垚鏂规锛�
+     * 涓斿鍑鸿繃绋嬩笉鎶ラ敊锛屽睘浜庢渶闅愯斀鐨勪竴绫讳簨鏁呫��
+     */
     private static final String[] FONT_CANDIDATES = {
-            "Noto Sans CJK SC", "WenQuanYi Zen Hei", "Microsoft YaHei", "SimSun", "Arial"
+            "Noto Sans CJK SC", "Source Han Sans SC", "WenQuanYi Zen Hei", "PingFang SC",
+            "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", "SimSun"
     };
 
+    /** 涓枃瀛楀舰鎺㈤拡瀛楃锛堢敤浜庢帰娴嬪瓧浣撴槸鍚﹀叿澶囦腑鏂囧瓧褰級 */
+    private static final char CJK_PROBE = '琚�';
+
+    /** 瑙f瀽鍑虹殑涓枃瀛椾綋鏃忓悕锛涗负 null 琛ㄧず褰撳墠鐜娌℃湁浠讳綍鍙覆鏌撲腑鏂囩殑瀛椾綋 */
     private static final String FONT_NAME = resolveFontName();
 
     /**
@@ -70,6 +81,8 @@
      * @return 鍚堟垚鍥剧墖
      */
     public static BufferedImage createQrLabel(String code, LinkedHashMap<String, String> keyValues) {
+        // 鏃犱腑鏂囧瓧浣撴椂鐩存帴澶辫触锛岄伩鍏嶅鍑轰腑鏂囦负鏂规鐨勬爣绛�
+        assertCjkFontAvailable();
         QrConfig config = new QrConfig();
         config.setErrorCorrection(ErrorCorrectionLevel.H); // 鎵撳嵃鏍囩鏄撶(鎹燂紝鐢ㄩ珮瀹归敊
         config.setMargin(1); // 淇濊瘉闈欏尯锛屾壂鐮佺巼
@@ -134,14 +147,46 @@
 
     // ==================== 绉佹湁鏂规硶 ====================
 
+    /**
+     * 瑙f瀽褰撳墠鐜鍙敤鐨勪腑鏂囧瓧浣撴棌鍚嶃��
+     * <p>
+     * 涓嶈兘鎸夊瓧浣撴棌鍚嶅仛绮剧‘鍖归厤锛氬悓涓�瀛椾綋鍦ㄤ笉鍚岀郴缁熶笂鐨勬棌鍚嶅彲鑳戒笉鍚岋紙渚嬪 Windows 涓婂疄闄呮敞鍐屼负
+     * {@code Microsoft YaHei UI} 鑰岄潪 {@code Microsoft YaHei}锛夛紝绮剧‘鍖归厤钀界┖鍚庝細涓�璺��鍒扮函鎷変竵瀛椾綋锛�
+     * 涓枃鍗虫覆鏌撲负鏂规銆傚洜姝よ繖閲岀粺涓�鐢� {@link Font#canDisplay(char)} 鎺㈡祴涓枃瀛楀舰銆�
+     *
+     * @return 鍙覆鏌撲腑鏂囩殑瀛椾綋鏃忓悕锛涙棤鍙敤瀛椾綋鏃惰繑鍥� {@code null}
+     */
     private static String resolveFontName() {
         Set<String> families = Set.of(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
         for (String candidate : FONT_CANDIDATES) {
-            if (families.contains(candidate)) {
+            if (families.contains(candidate) && canDisplayCjk(candidate)) {
                 return candidate;
             }
         }
-        return Font.SANS_SERIF;
+        // 閫昏緫瀛椾綋鐢� JDK 璐熻矗瀛楀舰鍥為��锛屽鏁扮幆澧冨彲娓叉煋涓枃
+        if (canDisplayCjk(Font.SANS_SERIF)) {
+            return Font.SANS_SERIF;
+        }
+        return null;
+    }
+
+    /**
+     * 鎺㈡祴鎸囧畾瀛椾綋鏃忚兘鍚︽覆鏌撲腑鏂�
+     */
+    private static boolean canDisplayCjk(String family) {
+        return new Font(family, Font.PLAIN, FONT_SIZE).canDisplay(CJK_PROBE);
+    }
+
+    /**
+     * 鏍¢獙褰撳墠鐜瀛樺湪鍙覆鏌撲腑鏂囩殑瀛椾綋锛屽惁鍒欐姏鍑哄紓甯搞��
+     * <p>
+     * 渚涘惎鍔ㄦ湡璋冪敤锛氬畞鍙湪鍚姩鏃跺け璐ワ紝涔熶笉鑳芥妸涓枃娓叉煋鎴愭柟妗嗙殑鏍囩浜や粯鍗板埛銆�
+     */
+    public static void assertCjkFontAvailable() {
+        if (FONT_NAME == null) {
+            throw new IllegalStateException("褰撳墠鐜娌℃湁浠讳綍鍙覆鏌撲腑鏂囩殑瀛椾綋锛屼簩缁寸爜鏍囩鐨勪腑鏂囧皢鏄剧ず涓烘柟妗嗭紝"
+                    + "鎷掔粷鐢熸垚鏍囩銆傝鍦ㄦ湇鍔″櫒瀹夎涓枃瀛椾綋锛堝 fonts-noto-cjk锛夊悗閲嶅惎銆�");
+        }
     }
 
     private static int measureTextHeight(LinkedHashMap<String, String> keyValues, Font labelFont, Font valueFont) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeService.java
index 546f9d6..ed5d968 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeService.java
@@ -140,6 +140,25 @@
     List<MesProBagCodeDO> getBagCodeListByBatchId(Long batchId);
 
     /**
+     * 鑾峰緱鏌愭壒娆′笅鍙嵃鍒风殑琚嬬爜鍒楄〃锛堟帓闄ゅ凡浣滃簾琚嬬爜锛屾寜琚嬪簭鍙峰崌搴忥級锛岀敤浜庢壒閲忓鍑轰簩缁寸爜鏍囩
+     * <p>
+     * 宸蹭綔搴熺殑琚嬬爜锛堢牬鎹熸浛鎹㈠悗浣滃簾锛変笉寰楀啀鍗板埛锛氬叾鏍囩鑻ヨ创鍒颁骇鍝佷笂锛屾秷璐硅�呮壂鐮佷細琚彁绀�
+     * 銆岃杩芥函鐮佸凡浣滃簾锛岃皑闃插亣鍐掋�嶏紝瀵艰嚧鐪熷搧琚鍒や负鍋囪揣銆�
+     *
+     * @param batchId 鎵规缂栧彿
+     * @return 鍙嵃鍒风殑琚嬬爜鍒楄〃
+     */
+    List<MesProBagCodeDO> getPrintableBagCodeListByBatchId(Long batchId);
+
+    /**
+     * 鑾峰緱鏌愮敓鎴愮粍涓嬪彲鍗板埛鐨勮鐮佸垪琛紙鎺掗櫎宸蹭綔搴熻鐮侊級锛岀敤浜庢壒閲忓鍑轰簩缁寸爜鏍囩
+     *
+     * @param uuid 鐢熸垚缁� UUID
+     * @return 鍙嵃鍒风殑琚嬬爜鍒楄〃
+     */
+    List<MesProBagCodeDO> getPrintableBagCodeListByUuid(String uuid);
+
+    /**
      * 鏍囪琚嬬爜涓哄凡鍑哄簱锛堣褰曢攢鍞嚭搴撳崟缂栧彿鍜屽嚭搴撴椂闂达級
      *
      * @param id      琚嬬爜缂栧彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java
index f34b350..d7f9b24 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java
@@ -128,7 +128,8 @@
             }
             palletMapper.insertBatch(pallets);
         }
-        int startNo = bagCodeMapper.selectMaxBagNo() + 1;
+        // 琚嬪簭鍙峰師瀛愬垎閰嶏細鍏堝彇搴撳唴鏈�澶у簭鍙蜂綔鍩哄噯锛屽啀鐢� Redis 鍘熷瓙鑷锛岄伩鍏嶅苟鍙戠敓鎴愪骇鐢熼噸澶嶈搴忓彿
+        int startNo = codeRedisDao.allocateBagNoStart(totalCount, bagCodeMapper.selectMaxBagNo());
         List<MesProBagCodeDO> bagCodes = new ArrayList<>(totalCount);
         for (int i = 0; i < totalCount; i++) {
             int bagNo = startNo + i;
@@ -160,7 +161,8 @@
         // 鍘熷琚嬬爜涓嶄緷璧栬閲忓崟浣嶇被鍨嬩笌鎵樼洏锛氫粎鎸夋暟閲忕敓鎴愶紝琚嬬爜甯﹀搧绉嶅啑浣欙紝鍚庣画鍙甯稿綍鍏ユ壒娆�/缁戝畾鎵樼洏
         MesMdmItemDO item = mdmItemService.validateItemExists(generateReqVO.getItemId());
         String uuid = IdUtil.fastSimpleUUID();
-        int startNo = bagCodeMapper.selectMaxBagNo() + 1;
+        // 琚嬪簭鍙峰師瀛愬垎閰嶏細鍏堝彇搴撳唴鏈�澶у簭鍙蜂綔鍩哄噯锛屽啀鐢� Redis 鍘熷瓙鑷锛岄伩鍏嶅苟鍙戠敓鎴愪骇鐢熼噸澶嶈搴忓彿
+        int startNo = codeRedisDao.allocateBagNoStart(generateReqVO.getCount(), bagCodeMapper.selectMaxBagNo());
         List<MesProBagCodeDO> bagCodes = new ArrayList<>(generateReqVO.getCount());
         for (int i = 0; i < generateReqVO.getCount(); i++) {
             int bagNo = startNo + i;
@@ -532,6 +534,16 @@
     }
 
     @Override
+    public List<MesProBagCodeDO> getPrintableBagCodeListByBatchId(Long batchId) {
+        return bagCodeMapper.selectPrintableListByBatchId(batchId);
+    }
+
+    @Override
+    public List<MesProBagCodeDO> getPrintableBagCodeListByUuid(String uuid) {
+        return bagCodeMapper.selectPrintableListByUuid(uuid);
+    }
+
+    @Override
     public void markOutbound(Long id, Long salesId) {
         bagCodeMapper.markOutbound(id, salesId, LocalDateTime.now());
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicService.java
index 9122b85..1476a7f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicService.java
@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.mes.service.trace;
 
+import cn.iocoder.yudao.module.mes.controller.pub.trace.vo.MesProTracePublicConfigRespVO;
 import cn.iocoder.yudao.module.mes.controller.pub.trace.vo.MesProTracePublicRespVO;
 
 /**
@@ -18,4 +19,14 @@
      */
     MesProTracePublicRespVO queryTrace(String code);
 
+    /**
+     * 鑾峰緱 H5 婧簮椤甸厤缃紙鐢熶骇缁忚惀鑰呭悕绉般�佽拷婧綉鍧�锛�
+     * <p>
+     * 涓ら」鍧囦负绉嶅瓙鏍囩鍥涜绱犵殑缁勬垚閮ㄥ垎锛岀敱鍚庣缁熶竴涓嬪彂锛屼繚璇佷簩缁寸爜鍐呭銆佹爣绛惧嵃鍒峰唴瀹逛笌
+     * H5 椤甸潰灞曠ず涓�鑷淬��
+     *
+     * @return 椤甸潰閰嶇疆
+     */
+    MesProTracePublicConfigRespVO getPublicConfig();
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
index b19e03a..98da11f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
@@ -6,6 +6,7 @@
 import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchProcessTraceRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchPurchaseSourceRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchSalesTargetRespVO;
+import cn.iocoder.yudao.module.mes.controller.pub.trace.vo.MesProTracePublicConfigRespVO;
 import cn.iocoder.yudao.module.mes.controller.pub.trace.vo.MesProTracePublicRespVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
@@ -18,6 +19,9 @@
 import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchWorkerTypeEnum;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProPalletStatusEnum;
+import cn.iocoder.yudao.module.mes.enums.trace.MesTraceCodeStatusEnum;
+import cn.iocoder.yudao.module.mes.framework.qrcode.MesProQrLabelBuilder;
 import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
 import cn.iocoder.yudao.module.mes.service.pro.bagcode.MesProBagCodeService;
 import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService;
@@ -112,8 +116,8 @@
             if (entry.batchId == null && entry.bagItemId == null) {
                 throw exception(MES_TRACE_CODE_NOT_FOUND);
             }
-            // 2. 鑱氬悎鍏ㄧ▼淇℃伅
-            MesProTracePublicRespVO result = buildRespVO(rawCode, traceCode, entry.sourceType, entry.batchId, entry.bagItemId);
+            // 2. 鑱氬悎鍏ㄧ▼淇℃伅锛堜綔搴熺爜浠嶆甯歌繑鍥炲苟璁板綍鎵爜锛氫綔搴熺爜琚壂鏄噸瑕佺殑闃蹭吉淇″彿锛屼笉搴旇涓烘煡璇㈠け璐ワ級
+            MesProTracePublicRespVO result = buildRespVO(rawCode, traceCode, entry);
             recordConsumerScan(rawCode, traceCode, entry.sourceType, entry.batchId, true);
             return result;
         } catch (RuntimeException ex) {
@@ -121,6 +125,15 @@
                     entry == null ? null : entry.batchId, false);
             throw ex;
         }
+    }
+
+    @Override
+    public MesProTracePublicConfigRespVO getPublicConfig() {
+        MesProTracePublicConfigRespVO respVO = new MesProTracePublicConfigRespVO();
+        respVO.setOperatorName(MesProQrLabelBuilder.resolveOperatorName());
+        respVO.setTraceUrl(MesProQrLabelBuilder.resolveTraceUrl());
+        respVO.setImported(MesProQrLabelBuilder.isImported());
+        return respVO;
     }
 
     /**
@@ -173,7 +186,13 @@
         // 鈶� 琚嬬爜
         MesProBagCodeDO bag = bagCodeService.getBagCodeByCode(traceCode);
         if (bag != null) {
-            return new TraceEntry("BAG", bag.getBatchId(), bag.getItemId());
+            // 宸蹭綔搴熺殑琚嬬爜锛堢牬鎹熸浛鎹㈠悗浣滃簾锛夊繀椤诲澶栧け鏁堬細鍥芥爣瑕佹眰浜岀淮鐮佷笉寰楅噸澶嶄娇鐢紝
+            // 鑻ヤ綔搴熺爜浠嶈繑鍥炴湁鏁堟函婧愪俊鎭紝浣滃簾鐮佸嵆鍙鍐掔敤閿�鍞�
+            if (MesProBagCodeStatusEnum.CANCELED.getStatus().equals(bag.getStatus())) {
+                return new TraceEntry("BAG", bag.getBatchId(), bag.getItemId(),
+                        MesTraceCodeStatusEnum.CANCELED.getStatus());
+            }
+            return TraceEntry.valid("BAG", bag.getBatchId(), bag.getItemId());
         }
         // 鈶� 鎵樼洏鐮�
         MesProPalletDO pallet = palletService.getPalletByCode(traceCode);
@@ -181,19 +200,30 @@
             // 鏈綍鍏ユ壒娆$殑鎵樼洏鍙栨墭鐩樺唴浠讳竴琚嬬爜鍝佺锛屼繚璇佷粛鑳藉睍绀哄搧绉嶄俊鎭�
             Long itemId = pallet.getBatchId() == null
                     ? firstItemId(bagCodeService.getBagCodeListByPalletId(pallet.getId())) : null;
-            return new TraceEntry("PALLET", pallet.getBatchId(), itemId);
+            if (MesProPalletStatusEnum.CANCELED.getStatus().equals(pallet.getStatus())) {
+                return new TraceEntry("PALLET", pallet.getBatchId(), itemId,
+                        MesTraceCodeStatusEnum.CANCELED.getStatus());
+            }
+            return TraceEntry.valid("PALLET", pallet.getBatchId(), itemId);
         }
         // 鈶� 鎵规鐮侊紙鍚复鏃舵壒娆$爜锛実etBatchByCode 鍐呴儴鍏煎锛�
         MesProBatchDO batch = batchService.getBatchByCode(traceCode);
         if (batch != null) {
-            return new TraceEntry("BATCH", batch.getId(), null);
+            if (MesProBatchStatusEnum.CANCELED.getStatus().equals(batch.getStatus())) {
+                return new TraceEntry("BATCH", batch.getId(), null,
+                        MesTraceCodeStatusEnum.CANCELED.getStatus());
+            }
+            return TraceEntry.valid("BATCH", batch.getId(), null);
         }
-        return new TraceEntry("", null, null);
+        return new TraceEntry("", null, null, MesTraceCodeStatusEnum.VALID.getStatus());
     }
 
     // ==================== 鑱氬悎鏋勫缓 ====================
 
-    private MesProTracePublicRespVO buildRespVO(String requestCode, String traceCode, String sourceType, Long batchId, Long bagItemId) {
+    private MesProTracePublicRespVO buildRespVO(String requestCode, String traceCode, TraceEntry entry) {
+        String sourceType = entry.sourceType();
+        Long batchId = entry.batchId();
+        Long bagItemId = entry.bagItemId();
         MesProBatchDO batch = batchId == null ? null : batchService.getBatch(batchId);
         if (batchId != null && batch == null) {
             throw exception(MES_TRACE_CODE_NOT_FOUND);
@@ -201,6 +231,10 @@
         MesProTracePublicRespVO resp = new MesProTracePublicRespVO();
         resp.setSourceType(sourceType);
         resp.setRequestCode(requestCode);
+        // 杩芥函鐮佹湁鏁堟�� + 鏍囩鍥涜绱犱腑鐨勭敓浜х粡钀ヨ�呬笌杩芥函缃戝潃锛屼繚璇佷笌鏍囩鍗板埛鍐呭涓�鑷�
+        resp.setCodeStatus(entry.codeStatus());
+        resp.setOperatorName(MesProQrLabelBuilder.resolveOperatorName());
+        resp.setTraceUrl(MesProQrLabelBuilder.resolveTraceUrl());
         // 鍝佺鍖哄潡
         fillItem(resp, batch, bagItemId);
         if (batch == null) {
@@ -505,7 +539,20 @@
         };
     }
 
-    private record TraceEntry(String sourceType, Long batchId, Long bagItemId) {
+    /**
+     * 杩芥函鍏ュ彛瑙f瀽缁撴灉
+     *
+     * @param sourceType 鍏ュ彛绫诲瀷锛欱AG / PALLET / BATCH锛屾湭鍖归厤鍒版椂涓虹┖涓�
+     * @param batchId    鐢熶骇鎵规缂栧彿
+     * @param bagItemId  琚嬬爜缁戝畾鐨勫搧绉嶇紪鍙凤紙鏈綍鎵规鏃剁敤浜庡睍绀哄搧绉嶏級
+     * @param codeStatus 杩芥函鐮佹湁鏁堟�э細{@link MesTraceCodeStatusEnum#VALID} / {@link MesTraceCodeStatusEnum#CANCELED}
+     */
+    private record TraceEntry(String sourceType, Long batchId, Long bagItemId, String codeStatus) {
+
+        static TraceEntry valid(String sourceType, Long batchId, Long bagItemId) {
+            return new TraceEntry(sourceType, batchId, bagItemId, MesTraceCodeStatusEnum.VALID.getStatus());
+        }
+
     }
 
 }
diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml
index 41742db..b2adc31 100644
--- a/yudao-server/src/main/resources/application-local.yaml
+++ b/yudao-server/src/main/resources/application-local.yaml
@@ -1,9 +1,12 @@
 server:
   port: 48080
 
-# H5 婧簮椤甸潰鍩虹鍦板潃锛堜簩缁寸爜鍐呭鍓嶇紑锛夛紝闇�涓哄叕浼楁壂鐮佸彲璁块棶鐨勫湴鍧�
+# 绉嶅瓙鏍囩涓� H5 婧簮椤甸厤缃�
 mes:
+  # 杩芥函缃戝潃锛堜簩缁寸爜鍐呭鍓嶇紑锛夛紝闇�涓哄叕浼楁壂鐮佸彲璁块棶鐨勫湴鍧�
   trace-base-url: http://192.168.1.7:48080/trace.html
+  # 鐢熶骇缁忚惀鑰呭悕绉帮紙绉嶅瓙鏍囩鍥涜绱犱箣涓�锛屾爣绛句笌婧簮椤电粺涓�鍙栨鍊硷紱鏈厤缃垯鏃犳硶瀵煎嚭鏍囩锛�
+  operator-name: 鏂扮枂濉旈噷鏈ㄦ渤绉嶄笟鑲′唤鏈夐檺鍏徃
 
 --- #################### 鏁版嵁搴撶浉鍏抽厤缃� ####################
 spring:
diff --git a/yudao-server/src/main/resources/application-thzy.yaml b/yudao-server/src/main/resources/application-thzy.yaml
index 22c2a28..9cb9079 100644
--- a/yudao-server/src/main/resources/application-thzy.yaml
+++ b/yudao-server/src/main/resources/application-thzy.yaml
@@ -1,9 +1,12 @@
 server:
   port: 48080
 
-# H5 婧簮椤甸潰鍩虹鍦板潃锛堜簩缁寸爜鍐呭鍓嶇紑锛夛紝闇�涓哄叕浼楁壂鐮佸彲璁块棶鐨勫湴鍧�
+# 绉嶅瓙鏍囩涓� H5 婧簮椤甸厤缃�
 mes:
+  # 杩芥函缃戝潃锛堜簩缁寸爜鍐呭鍓嶇紑锛夛紝闇�涓哄叕浼楁壂鐮佸彲璁块棶鐨勫湴鍧�
   trace-base-url: http://1.95.78.124:9008/prod/trace.html
+  # 鐢熶骇缁忚惀鑰呭悕绉帮紙绉嶅瓙鏍囩鍥涜绱犱箣涓�锛屾爣绛句笌婧簮椤电粺涓�鍙栨鍊硷級
+  operator-name: 鏂扮枂濉旈噷鏈ㄦ渤绉嶄笟鑲′唤鏈夐檺鍏徃
 
 --- #################### 鏁版嵁搴撶浉鍏抽厤缃� ####################
 spring:
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index ce5b8ba..2600196 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -1,7 +1,17 @@
-# H5 婧簮椤甸潰鍩虹鍦板潃锛堜簩缁寸爜鍐呭鍓嶇紑锛屽舰濡� http://鍩熷悕:绔彛/trace.html锛夛紝
-# 闇�涓哄叕浼楁壂鐮佸彲璁块棶鐨勫湴鍧�锛涘悇 profile 鍙鐩栥�傜暀绌哄垯浜岀淮鐮佺紪鐮佺函鐮佸�硷紙鍏煎鏃х爜锛夈��
+# 绉嶅瓙鏍囩涓� H5 婧簮椤甸厤缃紙鍚� profile 鍙鐩栵級
+# 娉ㄦ剰锛歵race-base-url 涓� operator-name 鏄簩缁寸爜鏍囩鍥涜绱犱腑鐨勩�岃拷婧綉鍧�銆嶃�岀敓浜х粡钀ヨ�呭悕绉般�嶏紝
+# 闈� local 鐜涓� trace-base-url 鑻ヤ负鍐呯綉/鍗犱綅绗﹀湴鍧�灏嗘嫆缁濆惎鍔紝閬垮厤鍗板嚭娑堣垂鑰呮壂涓嶅紑鐨勬爣绛俱��
 mes:
+  # 杩芥函缃戝潃锛堜簩缁寸爜鍐呭鍓嶇紑锛屽舰濡� http://鍩熷悕:绔彛/trace.html锛夛紝闇�涓哄叕浼楁壂鐮佸彲璁块棶鐨勫湴鍧�
   trace-base-url: http://localhost:48080/trace.html
+  # 鐢熶骇缁忚惀鑰呭悕绉帮紙鏍囩鍥涜绱犱箣涓�锛屾爣绛句笌婧簮椤电粺涓�鍙栨鍊硷紱鏈厤缃垯鏃犳硶瀵煎嚭鏍囩锛�
+  operator-name: ''
+  # 杩涘彛鍟嗗悕绉帮紙杩涘彛绉嶅瓙鍦烘櫙濉啓锛沬mported=true 鏃舵爣绛句笌婧簮椤靛睍绀鸿繘鍙e晢鍚嶇О锛�
+  importer-name: ''
+  # 鏄惁杩涘彛绉嶅瓙鍦烘櫙
+  imported: false
+  # 鏄惁鍏佽浜岀淮鐮佺紪鐮佺函鐮佸�硷紙涓嶅甫婧簮 URL锛夛紝浠呬緵鏈湴鍏煎鍘嗗彶鐮侊紝鐢熶骇鐜蹇呴』涓� false
+  allow-plain-qr-code: false
 
 spring:
   application:
diff --git a/yudao-server/src/main/resources/static/trace.html b/yudao-server/src/main/resources/static/trace.html
index b5a75cc..128d955 100644
--- a/yudao-server/src/main/resources/static/trace.html
+++ b/yudao-server/src/main/resources/static/trace.html
@@ -347,11 +347,26 @@
 (function () {
   'use strict';
 
-  // ========== 椤甸潰鍙厤缃」 ==========
-  // 鐢熶骇缁忚惀鑰咃紙杩芥函鏌ヨ鍖哄潡灞曠ず锛�
-  var COMPANY_NAME = '鏂扮枂濉旈噷鏈ㄦ渤绉嶄笟鑲′唤鏈夐檺鍏徃';
-  // 杩芥函缃戝潃锛堝缃戣闂湴鍧�锛屼笌鍖呰/浜岀淮鐮佷笂鍗板埗鐨勪竴鑷达級
-  var TRACE_URL = 'http://1.95.78.124:9008/';
+  // ========== 椤甸潰閰嶇疆 ==========
+  // 鐢熶骇缁忚惀鑰呭悕绉颁笌杩芥函缃戝潃鏄瀛愭爣绛惧洓瑕佺礌鐨勭粍鎴愰儴鍒嗭紝蹇呴』涓庝簩缁寸爜/鏍囩鍗板埛鍐呭涓�鑷达紝
+  // 鍥犳缁熶竴鐢卞悗绔笅鍙戯紙/public/mes/trace/config锛夛紝绂佹鍦ㄦ纭紪鐮併��
+  var COMPANY_NAME = '';
+  var TRACE_URL = '';
+  var IS_IMPORTED = false;
+
+  // 鎷夊彇婧簮椤甸厤缃紱澶辫触鏃朵笉闃绘柇鏌ヨ锛堟煡璇㈢粨鏋滄湰韬篃甯︾敓浜х粡钀ヨ�呬笌杩芥函缃戝潃锛�
+  function loadTraceConfig() {
+    return fetch('/public/mes/trace/config')
+      .then(function (resp) { return resp.json(); })
+      .then(function (res) {
+        if (res && res.code === 0 && res.data) {
+          COMPANY_NAME = res.data.operatorName || '';
+          TRACE_URL = res.data.traceUrl || '';
+          IS_IMPORTED = !!res.data.imported;
+        }
+      })
+      .catch(function () { /* 閰嶇疆鎷夊彇澶辫触锛氶��鍥炰娇鐢ㄦ煡璇㈢粨鏋滀腑鐨勫悓鍚嶅瓧娈� */ });
+  }
 
   var STATE = document.getElementById('state');
   var CODE_INPUT = document.getElementById('codeInput');
@@ -477,11 +492,19 @@
       renderState('<div class="empty-tip error">鏈煡璇㈠埌璇ヨ拷婧爜锛岃鏍稿浜岀淮鐮佹垨鑱旂郴瀹樻柟</div>');
       return;
     }
+    // 宸蹭綔搴熺殑杩芥函鐮侊紙鐮存崯鏇挎崲鍚庝綔搴燂級涓嶅緱浣滀负鏈夋晥浜у搧娴侀�氾紝鐩存帴缁欏嚭璀︾ず骞跺仠姝㈠睍绀烘函婧愯鎯�
+    if (d.codeStatus === 'CANCELED') {
+      renderState('<div class="empty-tip error">璇ヨ拷婧爜宸蹭綔搴燂紝闈炴湰鍏徃鏈夋晥浜у搧锛岃皑闃插亣鍐掋��<br>'
+        + '濡傛湁鐤戦棶璇疯仈绯诲畼鏂规牳瀹炪��</div>');
+      return;
+    }
     var html = '';
     html += sec('闃蹭吉鏌ヨ', null, fakePanel(d), null);
     html += sec('杩芥函鏌ヨ', null, tracePanel(d), null);
-    // 鐢熶骇鏄庣粏鎶樺彔鍖猴紙鎵规/璐ㄦ/浜哄憳/鎵樼洏/琚嬬爜/閲囪喘鏉ユ簮/绉嶅瓙娴佸悜/鍔犲伐娴佽浆锛夋寜闇�姹備笉灞曠ず锛�
-    // detailSections 淇濈暀浠ヤ究鍚庣画鎸夐渶寮�鍚�
+    // 鐢熶骇鏄庣粏鎶樺彔鍖猴細鍥芥爣瑕佹眰杩芥函缃戝潃鍙煡璇€�岀瀛愬姞宸ユ壒娆°�嶅強銆岀墿娴侀攢鍞俊鎭�嶏紝鏁呭澶栧睍绀�
+    // 锛堝姞宸ユ壒娆°�佺瀛愭祦鍚戙�佸姞宸ユ祦杞紱鎶樺彔涓嶅睍寮�锛夈�傝川妫�/浜哄憳/鎵樼洏/琚嬬爜/閲囪喘鏉ユ簮绛夊唴閮ㄦ槑缁嗕笉瀵瑰灞曠ず锛�
+    // 濡傞渶涓�骞跺睍绀猴紝鏀逛负 detailSections(d, true) 鍗冲彲銆�
+    html += detailSections(d, false);
     STATE.innerHTML = html || '<div class="empty-tip">鏆傛棤杩芥函鏁版嵁</div>';
     window.scrollTo({ top: 0, behavior: 'smooth' });
   }
@@ -537,9 +560,19 @@
   }
 
   // 鍖哄潡2锛氳拷婧煡璇紙鏈綍鍏ユ壒娆℃椂鍙睍绀哄搧绉嶄俊鎭� + 鎻愮ず锛�
+  // 鍥涜绱狅紙鍝佺鍚嶇О 鈫� 鐢熶骇缁忚惀鑰� 鈫� 鍗曞厓璇嗗埆浠g爜 鈫� 杩芥函缃戝潃锛夋寜鍥芥爣椤哄簭鎺掑垪锛屽叾浣欎俊鎭帓鍦ㄥ叾鍚�
   function tracePanel(d) {
     var code = resolveCode(d);
     var name = [d.itemName, d.itemSpecification].filter(Boolean).join(' ');
+    // 鐢熶骇缁忚惀鑰呬笌杩芥函缃戝潃浼樺厛鍙栨煡璇㈢粨鏋滐紙涓庢爣绛惧嵃鍒峰唴瀹瑰悓婧愶級锛岄��鍥為〉闈㈤厤缃�
+    var operatorName = d.operatorName || COMPANY_NAME;
+    var traceUrl = d.traceUrl || TRACE_URL;
+    var operatorLabel = IS_IMPORTED ? '杩涘彛鍟嗗悕绉�' : '鐢熶骇缁忚惀鑰�';
+    // 鍙湁琚嬬爜鎵嶆槸銆屾渶灏忛攢鍞崟鍏冦�嶏紝鍏剁爜鍊兼墠绉般�屽崟鍏冭瘑鍒唬鐮併�嶏紱
+    // 鎵樼洏鐮�/鎵规鐮佷笉灞炰簬鏈�灏忛攢鍞崟鍏冩爣璇嗭紝鎸夊疄闄呯被鍨嬪睍绀猴紝閬垮厤璇娑堣垂鑰�
+    var codeLabel = d.sourceType === 'BAG'
+      ? '鍗曞厓璇嗗埆浠g爜'
+      : (SOURCE_TYPE[d.sourceType] ? SOURCE_TYPE[d.sourceType] + '锛堥潪鏈�灏忛攢鍞崟鍏冿級' : '杩芥函鐮�');
     var unbound = d.batchBound === false;
     var batchRows = unbound
       ? '<div class="notice">璇ヨ拷婧爜灏氭湭褰曞叆鐢熶骇鎵规锛屾壒娆″彿銆佺敓浜ф棩鏈熴�佷骇绾跨瓑淇℃伅寰呭畬鍠勶紱褰撳墠灞曠ず璇ョ爜瀵瑰簲鐨勫搧绉嶄俊鎭��</div>'
@@ -547,31 +580,40 @@
         kv('鐢熶骇鏃ユ湡', fmtDate(d.produceDate)) +
         kv('浜х嚎', text(d.lineName || d.lineCode));
     return kv('鍝佺鍚嶇О', text(name)) +
+      kv(operatorLabel, text(operatorName)) +
+      kv(codeLabel, text(code || '-')) +
+      kv('杩芥函缃戝潃', traceUrl
+        ? '<a class="link" href="' + esc(traceUrl) + '">' + esc(traceUrl) + '</a>'
+        : '-') +
       kv('璁¢噺鍗曚綅', text(d.itemUnitMeasureName)) +
-      kv('鐢熶骇缁忚惀鑰�', esc(COMPANY_NAME)) +
-      kv('鍗曞厓璇嗗埆浠g爜', text(code || '-')) +
-      kv('杩芥函缃戝潃', '<a class="link" href="' + esc(TRACE_URL) + '">' + esc(TRACE_URL) + '</a>') +
       kv('杩芥函绫诲瀷', esc(SOURCE_TYPE[d.sourceType] || '杩芥函鐮�')) +
       batchRows;
   }
 
   // 鍖哄潡3~锛氱敓浜ф槑缁嗭紙鎶樺彔锛屾湁鏁版嵁鎵嶅睍绀猴級
-  function detailSections(d) {
+  // full=true 鏃跺睍绀哄叏閮ㄦ槑缁嗭紙璐ㄦ/浜哄憳/鎵樼洏/琚嬬爜/閲囪喘鏉ユ簮锛夛紱
+  // 榛樿鍙睍绀哄浗鏍囪姹傜殑銆屽姞宸ユ壒娆°�嶄笌銆岀墿娴侀攢鍞紙绉嶅瓙娴佸悜锛夈�嶄俊鎭紝鍏朵綑鍖哄潡涓嶅澶栧睍绀恒��
+  function detailSections(d, full) {
     var html = '';
 
-    // 鐢熶骇鎵规淇℃伅
-    var batchBody =
-      kv('鎵规鐘舵��', d.batchStatusName ? statusBadge({ v: [d.batchStatusName, 'blue'] }, 'v') : text(d.batchStatus)) +
-      kv('璁″垝鏁伴噺', fmtNum(d.planQuantity)) +
-      kv('宸茬敓鎴愯鐮�', fmtNum(d.bagQuantity)) +
-      kv('宸茬敓鎴愭墭鐩�', fmtNum(d.palletQuantity)) +
-      kv('鍏ュ簱鏃堕棿', fmtDate(d.inboundTime)) +
-      kv('涓存椂鎵规鐮�', text(d.tempCode)) +
-      kv('澶囨敞', text(d.remark));
-    html += sec('鐢熶骇鎵规淇℃伅', null, batchBody, { collapsible: true, open: false });
+    // 鐢熶骇鎵规淇℃伅锛堝浗鏍囪姹傦細杩芥函缃戝潃搴斿彲鏌ヨ绉嶅瓙鍔犲伐鎵规锛�
+    // 鏈綍鍏ユ壒娆℃椂涓嶅睍绀虹┖鍖哄潡锛岄伩鍏嶇粰鍑�"鏃犲姞宸ユ壒娆′俊鎭�"鐨勯敊璇殫绀�
+    if (d.batchBound !== false) {
+      var batchBody =
+        kv('鎵规鍙�', text(d.batchCode)) +
+        kv('鎵规鐘舵��', d.batchStatusName ? statusBadge({ v: [d.batchStatusName, 'blue'] }, 'v') : text(d.batchStatus)) +
+        kv('鐢熶骇鏃ユ湡', fmtDate(d.produceDate)) +
+        kv('浜х嚎', text(d.lineName || d.lineCode)) +
+        kv('璁″垝鏁伴噺', fmtNum(d.planQuantity)) +
+        kv('宸茬敓鎴愯鐮�', fmtNum(d.bagQuantity)) +
+        kv('宸茬敓鎴愭墭鐩�', fmtNum(d.palletQuantity)) +
+        kv('鍏ュ簱鏃堕棿', fmtDate(d.inboundTime)) +
+        kv('澶囨敞', text(d.remark));
+      html += sec('鐢熶骇鎵规淇℃伅', null, batchBody, { collapsible: true, open: false });
+    }
 
-    // 璐ㄦ淇℃伅
-    if (d.quality) {
+    // 璐ㄦ淇℃伅锛堜粎瀹屾暣妯″紡灞曠ず锛�
+    if (full && d.quality) {
       var q = d.quality;
       var qBody =
         kv('璐ㄦ缁撹', q.resultName ? '<span class="badge ' + (q.result === 1 ? 'green' : 'red') + '">' + esc(q.resultName) + '</span>' : '-') +
@@ -596,8 +638,8 @@
       html += sec('璐ㄦ淇℃伅', null, qBody, { collapsible: true, open: false });
     }
 
-    // 鐢熶骇浜哄憳
-    if (!isEmptyList(d.workers)) {
+    // 鐢熶骇浜哄憳锛堜粎瀹屾暣妯″紡灞曠ず锛氭秹鍙婁釜浜轰俊鎭紝涓嶅澶栧叕寮�锛�
+    if (full && !isEmptyList(d.workers)) {
       var wBody = d.workers.map(function (w) {
         return li('<span class="li-ttl">' + text(w.userName) + '</span>' +
           (w.workerTypeName ? '<span class="badge gray">' + esc(w.workerTypeName) + '</span>' : ''),
@@ -607,8 +649,8 @@
       html += sec('鐢熶骇浜哄憳', d.workers.length + ' 浜�', '<div class="scroll-list">' + wBody + '</div>', { collapsible: true, open: false });
     }
 
-    // 鎵樼洏淇℃伅
-    if (!isEmptyList(d.pallets)) {
+    // 鎵樼洏淇℃伅锛堜粎瀹屾暣妯″紡灞曠ず锛�
+    if (full && !isEmptyList(d.pallets)) {
       var pRows = d.pallets.map(function (p) {
         return [text(p.code), fmtNum(p.palletNo), fmtNum(p.bagCount), fmtNum(p.capacity)];
       });
@@ -617,8 +659,8 @@
         { collapsible: true, open: false });
     }
 
-    // 琚嬬爜鍒楄〃
-    if (!isEmptyList(d.bags)) {
+    // 琚嬬爜鍒楄〃锛堜粎瀹屾暣妯″紡灞曠ず锛�
+    if (full && !isEmptyList(d.bags)) {
       var bBody = d.bags.map(function (b) {
         return li('<span class="li-ttl">' + text(b.code) + '</span>' +
           (b.statusName ? '<span class="badge gray">' + esc(b.statusName) + '</span>' : ''),
@@ -630,8 +672,8 @@
       html += sec('琚嬬爜鍒楄〃', bagCountText, '<div class="scroll-list">' + bBody + '</div>', { collapsible: true, open: false });
     }
 
-    // 閲囪喘鏉ユ簮
-    if (!isEmptyList(d.purchaseSources)) {
+    // 閲囪喘鏉ユ簮锛堜粎瀹屾暣妯″紡灞曠ず锛�
+    if (full && !isEmptyList(d.purchaseSources)) {
       var psRows = d.purchaseSources.map(function (s) {
         return [text(s.receiptCode), text(s.vendorName), nw(fmtNum(s.receivedQuantity)), nw(fmtDay(s.receiptDate))];
       });
@@ -697,6 +739,9 @@
     });
   })();
 
+  // 鍏堟媺鍙栭〉闈㈤厤缃紙鐢熶骇缁忚惀鑰呭悕绉般�佽拷婧綉鍧�锛夛紝鍐嶆墽琛屾煡璇紱鏌ヨ缁撴灉涓悓鏍峰甫鏈夎繖涓ら」锛屼簰涓哄厹搴�
+  loadTraceConfig();
+
   var initialCode = new URLSearchParams(location.search).get('code') || '';
   CODE_INPUT.value = initialCode;
   if (initialCode) {

--
Gitblit v1.9.3