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