From 476d70b2f294f36a9dbd8d0c3cf5d1b75ba34c66 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 24 八月 2026 09:12:29 +0800
Subject: [PATCH] feat(mes): 新增包装袋型管理和托盘自动生成功能

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java                    |   29 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletService.java                      |    9 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java                                     |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchSaveReqVO.java          |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/MesProBagCodeController.java        |    5 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeService.java                    |    3 
 sql/mysql/mes_pro_pallet_auto_threshold.sql                                                                                 |   44 ++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java                |   33 ++-
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchTraceRespVO.java        |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java                    |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchRespVO.java             |    5 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletServiceImpl.java                  |   87 +++++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java                   |   10 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/batch/MesProBatchDO.java                      |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java                                    |    2 
 docs/mes_pro_pallet_auto_generation_integration.md                                                                          |  144 ++++++++++++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/vo/MesProBagCodeGenerateRespVO.java |   18 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletRespVO.java           |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagTypeEnum.java                                 |   30 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletAutoPrintRespVO.java  |   46 +++++
 20 files changed, 470 insertions(+), 15 deletions(-)

diff --git a/docs/mes_pro_pallet_auto_generation_integration.md b/docs/mes_pro_pallet_auto_generation_integration.md
new file mode 100644
index 0000000..ab94b06
--- /dev/null
+++ b/docs/mes_pro_pallet_auto_generation_integration.md
@@ -0,0 +1,144 @@
+# MES 鎵樼洏鐮佽嚜鍔ㄧ敓鎴� - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 鐢熶骇鎵规绠$悊锛氭柊澧�/缂栬緫鎵规鏃堕�夋嫨鍖呰琚嬪瀷銆�
+- 琚嬬爜绠$悊锛氱敓鎴愯鐮佹垚鍔熷悗鎺ユ敹鑷姩鎵樼洏鏍囩鏁版嵁銆�
+- 鎵樼洏鐮佺鐞嗭細鏌ョ湅鑷姩鐢熸垚鎵樼洏鍙婂叾缁戝畾琚嬫暟銆�
+- 浜岀淮鐮佹墦鍗板尯鍩燂細鏍规嵁鏍囩鏁版嵁涓�娆℃覆鏌撳悓涓�鎵樼洏鐨� 3 浠芥爣绛惧苟瑙﹀彂涓�娆℃祻瑙堝櫒鎵撳嵃銆�
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 鍒涘缓鎴栫紪杈戞壒娆℃椂锛屽繀椤婚�夋嫨鍖呰琚嬪瀷锛氬皬琚嬶紙鍊� 10锛夋垨澶ц锛堝�� 20锛夈�傚皬琚嬭嚜鍔ㄦ墭鐩橀槇鍊间负 100 琚嬶紝澶ц鑷姩鎵樼洏闃堝�间负 48 琚嬨��
+2. 鎵规杩涘叆鐢熶骇涓悗锛岃皟鐢ㄧ敓鎴愯鐮佹帴鍙c�傝鐮佺殑鎵规鍙枫�佸搧绉嶃�佷骇绾裤�佺敓浜ф棩鏈熷拰璐d换浜虹敱鎵规鑷姩甯﹀叆銆�
+3. 鍚庣鍦ㄥ悓涓�浜嬪姟涓粺璁¤鎵规鏈夋晥琚嬬爜锛氭湭閫昏緫鍒犻櫎涓旂姸鎬佷笉鏄凡浣滃簾锛屽苟鎸夎搴忓彿鍗囧簭鍙栧畬鏁撮槇鍊肩粍銆�
+4. 姣忎釜瀹屾暣闃堝�肩粍鑷姩鍒涘缓涓�涓墭鐩樼爜锛屽苟灏嗙粍鍐呭叏閮ㄨ鐮佸啓鍏ュ悓涓�鎵樼洏鐨勬墭鐩樼紪鍙峰拰鎵樼洏鐮併�傛湭杈惧埌瀹屾暣闃堝�肩殑浣欐暟琚嬬爜淇濇寔鏈粦瀹氥��
+5. 鑷姩鎵樼洏鐢熸垚鍚庯紝鐢熸垚琚嬬爜鎺ュ彛鍝嶅簲涓殑 `autoPallets` 杩斿洖鏈鏂板缓鎵樼洏鏍囩鏁版嵁銆傛瘡涓爣绛剧殑 `copies` 鍥哄畾涓� 3锛岃〃绀哄悓涓�鎵樼洏浜岀淮鐮佹墦鍗� 3 浠斤紝涓嶆槸鐢熸垚 3 涓墭鐩樸��
+6. 鍓嶇鏀跺埌 `autoPallets` 鍚庯紝涓烘瘡涓墭鐩樻爣绛炬覆鏌� 3 涓墦鍗板壇鏈紝骞跺彧璋冪敤涓�娆℃祻瑙堝櫒鎵撳嵃銆傛櫘閫氱綉椤垫墦鍗颁細鎵撳紑娴忚鍣ㄦ墦鍗版祦绋嬶紝鍚庣涓嶈繑鍥為潤榛樻墦鍗版垚鍔熺姸鎬併��
+
+## API
+
+### 1. 鎵规鍒涘缓/鏇存柊
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|---|---|---|
+| POST | `/mes/pro/batch/create` | 鍒涘缓鐢熶骇鎵规锛屽寘瑁呰鍨嬪繀濉� |
+| PUT | `/mes/pro/batch/update` | 鏇存柊鐢熶骇鎵规锛涚敓鎴愯鐮佹垨鎵樼洏鍚庝笉鍏佽淇敼琚嬪瀷 |
+
+璇锋眰鍙傛暟鏂板锛�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| bagType | Integer | 鏄� | 10 灏忚锛堥槇鍊�100锛夛紝20 澶ц锛堥槇鍊�48锛� |
+
+鍝嶅簲瀛楁鏂板锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|---|---|---|
+| bagType | Integer | 褰撳墠鎵规鍖呰琚嬪瀷 |
+
+### 2. 鐢熸垚琚嬬爜骞惰嚜鍔ㄧ敓鎴愭墭鐩�
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|---|---|---|
+| POST | `/mes/pro/bag-code/generate` | 鐢熸垚琚嬬爜锛涙湁鏁堣鐮佽揪鍒板畬鏁撮槇鍊兼椂鑷姩鐢熸垚鎵樼洏骞剁粦瀹� |
+
+璇锋眰鍙傛暟锛�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| batchId | Long | 鏄� | 鐢熶骇涓壒娆$紪鍙� |
+| count | Integer | 鏄� | 鏈鐢熸垚琚嬬爜鏁伴噺锛屽ぇ浜�0 |
+| remark | String | 鍚� | 琚嬬爜澶囨敞 |
+
+鍝嶅簲 `data`锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|---|---|---|
+| uuid | String | 鏈琚嬬爜鐢熸垚缁� UUID |
+| autoPallets | Array | 鏈鑷姩鐢熸垚鎵樼洏鏍囩鍒楄〃锛涙湭璺ㄨ繃瀹屾暣闃堝�兼椂涓虹┖ |
+
+`autoPallets` 瀛楁锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|---|---|---|
+| id | Long | 鎵樼洏缂栧彿 |
+| code | String | 鎵樼洏浜岀淮鐮佸唴瀹� |
+| batchId | Long | 鐢熶骇鎵规缂栧彿 |
+| batchCode | String | 鎵规鍙� |
+| palletNo | Integer | 鎵规鍐呮墭鐩樺簭鍙� |
+| capacity | Integer | 鎵樼洏瀹归噺锛岀瓑浜庤鍨嬮槇鍊� |
+| bagCount | Integer | 宸茶琚嬫暟锛岃嚜鍔ㄦ墭鐩樼敓鎴愬悗绛変簬瀹归噺 |
+| copies | Integer | 鏍囩鎵撳嵃浠芥暟锛屽浐瀹氫负3 |
+| itemId/itemCode/itemName/itemSpecification | 鈥� | 鍝佺淇℃伅 |
+| lineCode/lineName | String | 浜х嚎淇℃伅 |
+| produceDate | String | 鐢熶骇鏃ユ湡 |
+| ownerUserId/ownerUserName | 鈥� | 璐d换浜轰俊鎭� |
+
+鍝嶅簲绀轰緥锛�
+
+```json
+{
+  "code": 0,
+  "data": {
+    "uuid": "鐢熸垚缁刄UID",
+    "autoPallets": [
+      {
+        "id": 1001,
+        "code": "PC20260821L01A001-P01",
+        "batchId": 1024,
+        "batchCode": "PC20260821L01A001",
+        "palletNo": 1,
+        "capacity": 100,
+        "bagCount": 100,
+        "copies": 3,
+        "itemId": 2001,
+        "itemCode": "ITEM001",
+        "itemName": "浜у搧A",
+        "itemSpecification": "瑙勬牸A",
+        "lineCode": "L01",
+        "lineName": "鍖呰涓�绾�",
+        "produceDate": "2026-08-21 08:00:00",
+        "ownerUserId": 1,
+        "ownerUserName": "寮犱笁"
+      }
+    ]
+  }
+}
+```
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|---|---|---|
+| bagType | 鎵规鏂板/缂栬緫銆佹壒娆″垪琛ㄣ�佹壒娆¤鎯� | 閫氳繃瀛楀吀鏄剧ず灏忚/澶ц锛涙壒娆″凡鏈夎鐮佹垨鎵樼洏鍚庡彧璇� |
+| bagQuantity | 鎵规鍒楄〃/璇︽儏 | 鏈夋晥琚嬬爜绱鏁伴噺 |
+| palletQuantity | 鎵规鍒楄〃/璇︽儏 | 宸茬敓鎴愭墭鐩樻暟閲忥紝鍖呭惈鎵嬪伐鍜岃嚜鍔ㄦ墭鐩� |
+| autoGenerated | 鎵樼洏鍒楄〃/璇︽儏 | 鏍囪瘑璇ユ墭鐩樻槸鍚︾敱闃堝�艰嚜鍔ㄧ敓鎴� |
+| autoPallets[].code | 鎵撳嵃鏍囩 | 浣滀负浜岀淮鐮佺紪鐮佸唴瀹癸紝鍚屾椂鏄剧ず鎵樼洏鐮佹枃鏈� |
+| autoPallets[].copies | 鎵撳嵃鎺у埗 | 鍚屼竴鎵樼洏鏍囩鎵撳嵃3浠斤紱涓嶈杩炵画璋冪敤涓夋鎵撳嵃 |
+| autoPallets[].batchCode | 鎵撳嵃鏍囩 | 鏄剧ず鍏宠仈鎵规鍙� |
+| autoPallets[].bagCount/capacity | 鎵撳嵃鏍囩鎴栬鎯� | 鑷姩鎵樼洏姝e父鎯呭喌涓嬬浉绛夛紝鍧囦负瀵瑰簲闃堝�� |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|---|---|
+| 灏忚 | 鏈夋晥琚嬬爜姣忕疮璁�100涓嚜鍔ㄧ敓鎴愪竴涓墭鐩� |
+| 澶ц | 鏈夋晥琚嬬爜姣忕疮璁�48涓嚜鍔ㄧ敓鎴愪竴涓墭鐩� |
+| 鏈夋晥琚嬬爜 | `deleted = 0` 涓� `status <> 30`锛屽凡缁戝畾浜哄伐鎵樼洏鐨勮鐮佷笉鍙備笌鑷姩褰掗泦 |
+| 琚嬬爜鍒嗙粍 | 鎸� `bagNo` 鍗囧簭锛屾瘡缁勫繀椤昏揪鍒板畬鏁撮槇鍊硷紱浣欐暟涓嶇粦瀹� |
+| 鑷姩鎵樼洏瀹归噺 | 鍥哄畾涓哄綋鍓嶆壒娆¢槇鍊硷紝涓嶄娇鐢ㄦ墜宸ユ墭鐩樺閲� |
+| 閲嶅鐢熸垚 | 鍚屼竴鎵规璇锋眰涓茶閿佸畾锛涘凡缁戝畾琚嬬爜涓嶄細鍐嶆鍊欓�夛紝閲嶅璋冪敤涓嶉噸澶嶅垱寤� |
+| 浜哄伐缁戝畾 | 鑷姩娴佺▼涓嶆敼缁戝凡浜哄伐缁戝畾琚嬬爜锛涗汉宸ョ粦瀹氬拰瑙g粦闇�瑕佸湪鏈嶅姟绔姞閿� |
+| 鐮存崯鏇挎崲 | 鏂拌鐮佺户鎵挎棫琚嬬爜宸叉湁鎵樼洏锛涙湁鏁堟暟閲忎笉澧炲姞锛屼笉閲嶅瑙﹀彂鏂版墭鐩� |
+| 鍒犻櫎/浣滃簾 | 浣滃簾琚嬬爜涓嶈鍏ユ湁鏁堟暟锛涘凡缁戝畾琚嬬爜涓嶅厑璁搁�氳繃鐢熸垚缁勫垹闄� |
+| 鎵撳嵃 | 鍚庣鍙繑鍥炴爣绛炬暟鎹紱娴忚鍣ㄧ涓�娆℃覆鏌�3浠藉苟璋冪敤涓�娆� `window.print()`锛屾棤娉曚繚璇侀潤榛樻墦鍗� |
+
+## 娉ㄦ剰浜嬮」
+
+- 鍘嗗彶鎵规鐨� `bagType` 闇�瑕佸湪鍚敤鑷姩鎵樼洏鍓嶈ˉ榻愶紝涓嶈兘鐢卞悗绔寽娴嬨��
+- 鍙湁鐢熶骇涓壒娆″厑璁哥敓鎴愯鐮佸拰鑷姩鎵樼洏銆�
+- 鏅�氭祻瑙堝櫒鏃犳硶缁曡繃绯荤粺鎵撳嵃瀵硅瘽妗嗭紱鑻ヨ姹傛棤浜哄�煎畧鎵撳嵃锛岄渶瑕佸彟琛屾帴鍏ユ湰鍦版墦鍗颁唬鐞嗘垨鎵撳嵃鎺т欢銆�
+- 涓嶈鎶� ZIP 涓嬭浇鍝嶅簲褰撲綔鑷姩鎵撳嵃缁撴灉锛涚幇鏈� ZIP 鎺ュ彛缁х画鐢ㄤ簬鎵归噺瀵煎嚭銆�
+- 鑷姩鎵樼洏涓殑琚嬬爜瀹屾暣鍏宠仈浠ヨ鐮佽褰曠殑 `palletId`銆乣palletCode` 鍜屾墭鐩樼殑 `bagCount` 涓哄噯銆�
diff --git a/sql/mysql/mes_pro_pallet_auto_threshold.sql b/sql/mysql/mes_pro_pallet_auto_threshold.sql
new file mode 100644
index 0000000..874007a
--- /dev/null
+++ b/sql/mysql/mes_pro_pallet_auto_threshold.sql
@@ -0,0 +1,44 @@
+-- =============================================
+-- MES 鎵樼洏鐮佽嚜鍔ㄧ敓鎴愶紙鎸夋湁鏁堣鐮佺疮璁¢槇鍊硷級杩佺Щ鑴氭湰
+-- 1. mes_pro_batch 澧炲姞鍖呰琚嬪瀷 bag_type锛堝皬琚� 10 / 澶ц 20锛�
+-- 2. mes_pro_pallet 澧炲姞 auto_generated 鑷姩鐢熸垚鏍囪瘑
+-- 3. 澧炲姞鍖呰琚嬪瀷瀛楀吀
+-- MySQL 8.4 閫傜敤锛屼笉鍖呭惈 tenant_id
+-- =============================================
+
+SET NAMES utf8mb4;
+
+-- ----------------------------
+-- 1. 鐢熶骇鎵规澧炲姞鍖呰琚嬪瀷
+-- ----------------------------
+SET @batch_bag_type_exists = (SELECT COUNT(*) FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'mes_pro_batch' AND column_name = 'bag_type');
+SET @batch_bag_type_sql = IF(@batch_bag_type_exists = 0,
+    'ALTER TABLE mes_pro_batch ADD COLUMN bag_type TINYINT DEFAULT NULL COMMENT ''鍖呰琚嬪瀷锛堝瓧鍏� mes_pro_bag_type锛�10 灏忚 20 澶ц锛夛紝鐢ㄤ簬鑷姩褰掗泦鎵樼洏'' AFTER item_id',
+    'SELECT 1');
+PREPARE batch_bag_type_stmt FROM @batch_bag_type_sql;
+EXECUTE batch_bag_type_stmt;
+DEALLOCATE PREPARE batch_bag_type_stmt;
+
+-- ----------------------------
+-- 2. 鎵樼洏鐮佽〃澧炲姞鑷姩鐢熸垚鏍囪瘑
+-- ----------------------------
+SET @pallet_auto_generated_exists = (SELECT COUNT(*) FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'mes_pro_pallet' AND column_name = 'auto_generated');
+SET @pallet_auto_generated_sql = IF(@pallet_auto_generated_exists = 0,
+    'ALTER TABLE mes_pro_pallet ADD COLUMN auto_generated BIT(1) NOT NULL DEFAULT b''0'' COMMENT ''鏄惁鑷姩鐢熸垚锛堟寜鏈夋晥琚嬬爜绱闃堝�肩敓鎴愶紝瀹归噺鍥哄畾涓洪槇鍊硷級'' AFTER bag_count',
+    'SELECT 1');
+PREPARE pallet_auto_generated_stmt FROM @pallet_auto_generated_sql;
+EXECUTE pallet_auto_generated_stmt;
+DEALLOCATE PREPARE pallet_auto_generated_stmt;
+
+-- ----------------------------
+-- 3. 鍖呰琚嬪瀷瀛楀吀
+-- ----------------------------
+INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 'MES 鍖呰琚嬪瀷', 'mes_pro_bag_type', 0, '鎵樼洏鑷姩褰掗泦闃堝�煎垽瀹�', '1', NOW(), '1', NOW(), b'0' FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM (SELECT id FROM system_dict_type WHERE `type` = 'mes_pro_bag_type' AND deleted = 0) t);
+
+INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
+(1065364, 1, '灏忚', '10', 'mes_pro_bag_type', 0, 'primary', '', '1', NOW(), '1', NOW(), b'0'),
+(1065365, 2, '澶ц', '20', 'mes_pro_bag_type', 0, 'warning', '', '1', NOW(), '1', NOW(), b'0');
\ No newline at end of file
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 49cb688..37c315b 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
@@ -10,6 +10,7 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeBindReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceReqVO;
@@ -71,9 +72,9 @@
     private MesMdmItemService mdmItemService;
 
     @PostMapping("/generate")
-    @Operation(summary = "鐢熸垚琚嬬爜鏁版嵁鍖咃紙涓�琚嬩竴鐮侊級")
+    @Operation(summary = "鐢熸垚琚嬬爜鏁版嵁鍖咃紙涓�琚嬩竴鐮侊級锛岀疮璁¤揪鍒伴槇鍊兼椂鑷姩鐢熸垚鎵樼洏鐮�")
     @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:create')")
-    public CommonResult<String> generateBagCodes(@Valid @RequestBody MesProBagCodeGenerateReqVO generateReqVO) {
+    public CommonResult<MesProBagCodeGenerateRespVO> generateBagCodes(@Valid @RequestBody MesProBagCodeGenerateReqVO generateReqVO) {
         return success(bagCodeService.generateBagCodes(generateReqVO));
     }
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/vo/MesProBagCodeGenerateRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/vo/MesProBagCodeGenerateRespVO.java
new file mode 100644
index 0000000..1fd1c6a
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/bagcode/vo/MesProBagCodeGenerateRespVO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo;
+
+import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletAutoPrintRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 琚嬬爜鐢熸垚鍝嶅簲")
+@Data
+public class MesProBagCodeGenerateRespVO {
+
+    @Schema(description = "鐢熸垚缁� UUID")
+    private String uuid;
+
+    @Schema(description = "鏈鑷姩鐢熸垚鐨勬墭鐩樻爣绛撅紱姣忎釜鏍囩 copies 鍥哄畾涓� 3")
+    private List<MesProPalletAutoPrintRespVO> autoPallets;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchRespVO.java
index 371eac9..3f8657e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchRespVO.java
@@ -30,6 +30,11 @@
     @ExcelIgnore
     private Long itemId;
 
+    @Schema(description = "鍖呰琚嬪瀷锛堝瓧鍏� mes_pro_bag_type锛�", example = "10")
+    @ExcelProperty("鍖呰琚嬪瀷")
+    @DictFormat(DictTypeConstants.MES_PRO_BAG_TYPE)
+    private Integer bagType;
+
     @Schema(description = "鍝佺缂栫爜")
     @ExcelProperty("鍝佺缂栫爜")
     private String itemCode;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchSaveReqVO.java
index 6b5cd8d..fcfa2c3 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchSaveReqVO.java
@@ -24,6 +24,10 @@
     @NotNull(message = "鍝佺涓嶈兘涓虹┖")
     private Long itemId;
 
+    @Schema(description = "鍖呰琚嬪瀷锛堝瓧鍏� mes_pro_bag_type锛�10 灏忚銆�20 澶ц锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    @NotNull(message = "鍖呰琚嬪瀷涓嶈兘涓虹┖")
+    private Integer bagType;
+
     @Schema(description = "浜х嚎缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @NotNull(message = "浜х嚎涓嶈兘涓虹┖")
     private Long lineId;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchTraceRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchTraceRespVO.java
index 105822e..b20faf9 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchTraceRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/batch/vo/MesProBatchTraceRespVO.java
@@ -52,6 +52,9 @@
 
     // ========== 鍝佺淇℃伅 ==========
 
+    @Schema(description = "鍖呰琚嬪瀷锛堝瓧鍏� mes_pro_bag_type锛�", example = "10")
+    private Integer bagType;
+
     @Schema(description = "鍝佺锛堢墿鏂欙級缂栧彿", example = "1024")
     private Long itemId;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletAutoPrintRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletAutoPrintRespVO.java
new file mode 100644
index 0000000..21d9ee7
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletAutoPrintRespVO.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 鑷姩鎵樼洏鎵撳嵃鏍囩")
+@Data
+public class MesProPalletAutoPrintRespVO {
+
+    @Schema(description = "鎵樼洏缂栧彿")
+    private Long id;
+    @Schema(description = "鎵樼洏鐮�")
+    private String code;
+    @Schema(description = "鎵规缂栧彿")
+    private Long batchId;
+    @Schema(description = "鎵规鍙�")
+    private String batchCode;
+    @Schema(description = "鎵规鍐呮墭鐩樺簭鍙�")
+    private Integer palletNo;
+    @Schema(description = "鏍囩鎵撳嵃浠芥暟")
+    private Integer copies;
+    @Schema(description = "鍙琚嬫暟")
+    private Integer capacity;
+    @Schema(description = "宸茶琚嬫暟")
+    private Integer bagCount;
+    @Schema(description = "鍝佺缂栧彿")
+    private Long itemId;
+    @Schema(description = "鍝佺缂栫爜")
+    private String itemCode;
+    @Schema(description = "鍝佺鍚嶇О")
+    private String itemName;
+    @Schema(description = "鍝佺瑙勬牸")
+    private String itemSpecification;
+    @Schema(description = "浜х嚎缂栫爜")
+    private String lineCode;
+    @Schema(description = "浜х嚎鍚嶇О")
+    private String lineName;
+    @Schema(description = "鐢熶骇鏃ユ湡")
+    private LocalDateTime produceDate;
+    @Schema(description = "璐d换浜虹敤鎴风紪鍙�")
+    private Long ownerUserId;
+    @Schema(description = "璐d换浜烘樀绉�")
+    private String ownerUserName;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletRespVO.java
index 0747066..120b3d0 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/pallet/vo/MesProPalletRespVO.java
@@ -45,6 +45,10 @@
     @ExcelProperty("宸茶琚嬫暟")
     private Integer bagCount;
 
+    @Schema(description = "鏄惁鑷姩鐢熸垚", example = "false")
+    @ExcelIgnore
+    private Boolean autoGenerated;
+
     @Schema(description = "鐘舵�侊紙瀛楀吀 mes_pro_pallet_status锛�", example = "10")
     @ExcelProperty(value = "鐘舵��", converter = DictConvert.class)
     @DictFormat(DictTypeConstants.MES_PRO_PALLET_STATUS)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/batch/MesProBatchDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/batch/MesProBatchDO.java
index e21cb52..43afa78 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/batch/MesProBatchDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/batch/MesProBatchDO.java
@@ -45,6 +45,10 @@
      */
     private Long itemId;
     /**
+     * 鍖呰琚嬪瀷锛堝瓧鍏� mes_pro_bag_type锛�
+     */
+    private Integer bagType;
+    /**
      * 浜х嚎缂栧彿
      * <p>
      * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.pro.line.MesProLineDO#getId()}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java
index eadac1c..4cdc673 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java
@@ -58,6 +58,10 @@
      */
     private Integer bagCount;
     /**
+     * 鏄惁鑷姩鐢熸垚锛堟寜鏈夋晥琚嬬爜绱闃堝�肩敓鎴愶紝瀹归噺鍥哄畾涓洪槇鍊硷級
+     */
+    private Boolean autoGenerated;
+    /**
      * 鐘舵��
      * <p>
      * 瀛楀吀 {@link DictTypeConstants#MES_PRO_PALLET_STATUS}
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 ca31481..2e12a2b 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;
@@ -39,6 +40,15 @@
         return selectList(MesProBagCodeDO::getBatchId, batchId);
     }
 
+    default List<MesProBagCodeDO> selectUnboundEffectiveListByBatchId(Long batchId) {
+        return selectList(new LambdaQueryWrapperX<MesProBagCodeDO>()
+                .eq(MesProBagCodeDO::getBatchId, batchId)
+                .isNull(MesProBagCodeDO::getPalletId)
+                .ne(MesProBagCodeDO::getStatus, MesProBagCodeStatusEnum.CANCELED.getStatus())
+                .orderByAsc(MesProBagCodeDO::getBagNo)
+                .last("FOR UPDATE"));
+    }
+
     default Long selectCountByBatchId(Long batchId) {
         return selectCount(MesProBagCodeDO::getBatchId, batchId);
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
index 5b53458..ac15b5d 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
@@ -31,6 +31,7 @@
     String MES_PRO_TASK_STATUS = "mes_pro_task_status"; // MES 浠诲姟鐘舵��
     String MES_PRO_BATCH_STATUS = "mes_pro_batch_status"; // MES 鐢熶骇鎵规鐘舵��
     String MES_PRO_BATCH_WORKER_TYPE = "mes_pro_batch_worker_type"; // MES 鎵规浜哄憳宀椾綅绫诲瀷
+    String MES_PRO_BAG_TYPE = "mes_pro_bag_type"; // MES 鍖呰琚嬪瀷
     String MES_PRO_BAG_CODE_STATUS = "mes_pro_bag_code_status"; // MES 琚嬬爜鐘舵��
     String MES_PRO_PALLET_STATUS = "mes_pro_pallet_status"; // MES 鎵樼洏鐮佺姸鎬�
 
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 02e4dab..e46269b 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
@@ -809,6 +809,8 @@
     ErrorCode PRO_QR_EXPORT_TOO_MANY = new ErrorCode(1_040_750_007, "浜岀淮鐮佹壒閲忓鍑烘暟閲忚秴杩囦笂闄�");
     ErrorCode PRO_QR_IMAGE_GENERATE_ERROR = new ErrorCode(1_040_750_008, "浜岀淮鐮佸浘鐗囩敓鎴愬け璐�");
     ErrorCode PRO_QR_EXPORT_PARAMS_INVALID = new ErrorCode(1_040_750_009, "浜岀淮鐮佹壒閲忓鍑虹己灏戞煡璇㈡潯浠�");
+    ErrorCode PRO_BATCH_BAG_TYPE_INVALID = new ErrorCode(1_040_750_010, "鍖呰琚嬪瀷鏃犳晥");
+    ErrorCode PRO_BATCH_BAG_TYPE_NOT_ALLOW_CHANGE = new ErrorCode(1_040_750_011, "鎵规宸茬敓鎴愯鐮佹垨鎵樼洏鐮侊紝涓嶅厑璁镐慨鏀瑰寘瑁呰鍨�");
 
     // ========== MES 鐢熶骇绠$悊-琚嬬爜璧嬬爜锛�1-040-752-000锛� ==========
     ErrorCode PRO_BAG_CODE_NOT_EXISTS = new ErrorCode(1_040_752_000, "琚嬬爜涓嶅瓨鍦�");
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagTypeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagTypeEnum.java
new file mode 100644
index 0000000..6824151
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagTypeEnum.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.mes.enums.pro;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+@Getter
+@AllArgsConstructor
+public enum MesProBagTypeEnum implements ArrayValuable<Integer> {
+
+    SMALL(10, "灏忚", 100),
+    LARGE(20, "澶ц", 48);
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProBagTypeEnum::getType).toArray(Integer[]::new);
+
+    private final Integer type;
+    private final String name;
+    private final int threshold;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+    public static MesProBagTypeEnum valueOfType(Integer type) {
+        return Arrays.stream(values()).filter(item -> item.type.equals(type)).findFirst().orElse(null);
+    }
+}
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 c75dadf..1769303 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
@@ -3,6 +3,7 @@
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeBindReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceRespVO;
@@ -27,7 +28,7 @@
      * @param generateReqVO 鐢熸垚淇℃伅
      * @return 鐢熸垚缁� UUID锛堟爣璁板悓涓�娆$敓鎴愮殑鏁版嵁鍖咃級
      */
-    String generateBagCodes(@Valid MesProBagCodeGenerateReqVO generateReqVO);
+    MesProBagCodeGenerateRespVO generateBagCodes(@Valid MesProBagCodeGenerateReqVO generateReqVO);
 
     /**
      * 浣滃簾鐮存崯琚嬬爜骞跺湪鍘熸壒娆$敓鎴愮瓑閲忔浛鎹㈣鐮�
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 d346513..8966276 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
@@ -6,6 +6,7 @@
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeBindReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeGenerateRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceItemRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.bagcode.vo.MesProBagCodeReplaceReqVO;
@@ -62,7 +63,7 @@
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public String generateBagCodes(MesProBagCodeGenerateReqVO generateReqVO) {
+    public MesProBagCodeGenerateRespVO generateBagCodes(MesProBagCodeGenerateReqVO generateReqVO) {
         // 1. 鏍¢獙鎵规瀛樺湪涓斿浜庣敓浜т腑鐘舵��
         MesProBatchDO batch = batchMapper.selectByIdForUpdate(generateReqVO.getBatchId());
         if (batch == null) {
@@ -98,7 +99,9 @@
         batchMapper.updateById(new MesProBatchDO().setId(batch.getId())
                 .setBagQuantity((batch.getBagQuantity() == null ? 0 : batch.getBagQuantity())
                         + generateReqVO.getCount()));
-        return uuid;
+        return new MesProBagCodeGenerateRespVO()
+                .setUuid(uuid)
+                .setAutoPallets(palletService.autoCreatePallets(batch.getId()));
     }
 
     @Override
@@ -242,10 +245,13 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void bindPallet(MesProBagCodeBindReqVO bindReqVO) {
-        // 1. 鏍¢獙鎵樼洏瀛樺湪
-        MesProPalletDO pallet = palletService.validatePalletExists(bindReqVO.getPalletId());
-        // 2. 鏍¢獙琚嬬爜
-        List<MesProBagCodeDO> bagCodes = validateBagCodesForBind(bindReqVO.getIds(), pallet);
+        // 1. 閿佸畾鎵樼洏锛岄伩鍏嶄笌鑷姩褰掗泦骞跺彂淇敼琚嬫暟
+        MesProPalletDO pallet = palletMapper.selectByIdForUpdate(bindReqVO.getPalletId());
+        if (pallet == null) {
+            throw exception(PRO_PALLET_NOT_EXISTS);
+        }
+        // 2. 閿佸畾骞舵牎楠岃鐮�
+        List<MesProBagCodeDO> bagCodes = validateBagCodesForBind(bindReqVO.getIds(), pallet, true);
         // 3. 鏍¢獙涓嶈秴杩囨墭鐩樺彲瑁呰鏁�
         int currentCount = pallet.getBagCount() == null ? 0 : pallet.getBagCount();
         if (pallet.getCapacity() != null && pallet.getCapacity() > 0
@@ -260,10 +266,13 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void unbindPallet(MesProBagCodeBindReqVO bindReqVO) {
-        // 1. 鏍¢獙鎵樼洏瀛樺湪
-        MesProPalletDO pallet = palletService.validatePalletExists(bindReqVO.getPalletId());
-        // 2. 鏍¢獙琚嬬爜鍧囩粦瀹氬湪褰撳墠鎵樼洏涓�
-        List<MesProBagCodeDO> bagCodes = bagCodeMapper.selectListByIds(bindReqVO.getIds());
+        // 1. 閿佸畾鎵樼洏锛岄伩鍏嶄笌鑷姩褰掗泦骞跺彂淇敼琚嬫暟
+        MesProPalletDO pallet = palletMapper.selectByIdForUpdate(bindReqVO.getPalletId());
+        if (pallet == null) {
+            throw exception(PRO_PALLET_NOT_EXISTS);
+        }
+        // 2. 閿佸畾骞舵牎楠岃鐮佸潎缁戝畾鍦ㄥ綋鍓嶆墭鐩樹笂
+        List<MesProBagCodeDO> bagCodes = bagCodeMapper.selectListByIdsForUpdate(bindReqVO.getIds());
         if (bagCodes.size() != bindReqVO.getIds().size()) {
             throw exception(PRO_BAG_CODE_NOT_EXISTS);
         }
@@ -339,8 +348,8 @@
     /**
      * 鏍¢獙寰呯粦瀹氱殑琚嬬爜锛氬瓨鍦ㄣ�佷笌鎵樼洏鍚屾壒娆°�佹湭缁戝畾杩囨墭鐩樸�佺姸鎬佹湁鏁堬紙宸茬敓鎴�/宸插鍑哄嵃鍒凤級
      */
-    private List<MesProBagCodeDO> validateBagCodesForBind(List<Long> ids, MesProPalletDO pallet) {
-        List<MesProBagCodeDO> bagCodes = bagCodeMapper.selectListByIds(ids);
+    private List<MesProBagCodeDO> validateBagCodesForBind(List<Long> ids, MesProPalletDO pallet, boolean lock) {
+        List<MesProBagCodeDO> bagCodes = lock ? bagCodeMapper.selectListByIdsForUpdate(ids) : bagCodeMapper.selectListByIds(ids);
         if (bagCodes.size() != ids.size()) {
             throw exception(PRO_BAG_CODE_NOT_EXISTS);
         }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java
index de727f9..e50290c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java
@@ -15,6 +15,7 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchWorkerMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper;
 import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProBagTypeEnum;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum;
 import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
 import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
@@ -67,6 +68,7 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createBatch(MesProBatchSaveReqVO createReqVO) {
+        validateBagType(createReqVO.getBagType());
         // 1. 鏍¢獙鍏宠仈鏁版嵁锛堝搧绉嶃�佷骇绾裤�佺敓浜т汉鍛橈級
         MesMdmItemDO item = mdmItemService.validateItemExists(createReqVO.getItemId());
         MesProLineDO line = lineService.validateLineExistsAndEnable(createReqVO.getLineId());
@@ -80,6 +82,7 @@
                 .setCode(code)
                 .setLineCode(line.getCode())
                 .setLineName(line.getName())
+                .setBagType(createReqVO.getBagType())
                 .setBagQuantity(0)
                 .setPalletQuantity(0)
                 .setStatus(MesProBatchStatusEnum.DRAFT.getStatus());
@@ -96,6 +99,12 @@
         // 1. 鏍¢獙瀛樺湪 + 鐘舵�侊紙鑽夌/鐢熶骇涓墠鍏佽淇敼锛�
         MesProBatchDO oldBatch = validateBatchExists(updateReqVO.getId());
         validateBatchEditable(oldBatch);
+        // 2. 宸茬敓鎴愯鐮佹垨鎵樼洏鐮佸悗涓嶅厑璁镐慨鏀硅鍨嬶紙閬垮厤鍚屼竴鎵规闃堝�煎彉鍖栧鑷村綊闆嗛敊涔憋級
+        if (updateReqVO.getBagType() != null && !Objects.equals(oldBatch.getBagType(), updateReqVO.getBagType())
+                && (bagQuantityOrPalletCreated(oldBatch))) {
+            throw exception(PRO_BATCH_BAG_TYPE_NOT_ALLOW_CHANGE);
+        }
+        validateBagType(updateReqVO.getBagType());
         // 2. 鏍¢獙鍏宠仈鏁版嵁锛堢敓浜т汉鍛橈級
         validateRelations(updateReqVO);
 
@@ -157,6 +166,9 @@
         }
         if (!valid) {
             throw exception(PRO_BATCH_STATUS_INVALID);
+        }
+        if (MesProBatchStatusEnum.PRODUCING.getStatus().equals(status)) {
+            validateBagType(batch.getBagType());
         }
         // 3. 鏇存柊鐘舵��
         batchMapper.updateById(new MesProBatchDO().setId(id).setStatus(status));
@@ -246,6 +258,23 @@
     }
 
     /**
+     * 鏍¢獙鍖呰琚嬪瀷鏈夋晥
+     */
+    private void validateBagType(Integer bagType) {
+        if (bagType != null && MesProBagTypeEnum.valueOfType(bagType) == null) {
+            throw exception(PRO_BATCH_BAG_TYPE_INVALID);
+        }
+    }
+
+    /**
+     * 鍒ゆ柇鎵规鏄惁宸茬敓鎴愯鐮佹垨鎵樼洏鐮侊紙鐢ㄤ簬绂佹淇敼琚嬪瀷锛�
+     */
+    private boolean bagQuantityOrPalletCreated(MesProBatchDO batch) {
+        return (batch.getBagQuantity() != null && batch.getBagQuantity() > 0)
+                || (batch.getPalletQuantity() != null && batch.getPalletQuantity() > 0);
+    }
+
+    /**
      * 鎻掑叆鎵规鐢熶骇浜哄憳閰嶇疆
      */
     private void insertBatchWorkers(Long batchId, List<MesProBatchSaveReqVO.Worker> workers) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletService.java
index 4f104c9..2dfb6e6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletService.java
@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.mes.service.pro.pallet;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletAutoPrintRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletBatchCreateReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletPageReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO;
@@ -24,6 +25,14 @@
     List<Long> batchCreatePallet(@Valid MesProPalletBatchCreateReqVO createReqVO);
 
     /**
+     * 鎸夋壒娆℃湁鏁堣鐮佹暟閲忚嚜鍔ㄧ敓鎴愬苟缁戝畾鏁存墭琚嬬爜
+     *
+     * @param batchId 鎵规缂栧彿
+     * @return 鏈鏂扮敓鎴愮殑鎵樼洏鎵撳嵃鏁版嵁
+     */
+    List<MesProPalletAutoPrintRespVO> autoCreatePallets(Long batchId);
+
+    /**
      * 鍒犻櫎鎵樼洏鐮侊紙鏈粦瀹氳鐮佹墠鍏佽鍒犻櫎锛�
      *
      * @param id 缂栧彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletServiceImpl.java
index 1ba2901..285f8f7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletServiceImpl.java
@@ -2,6 +2,7 @@
 
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletAutoPrintRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletBatchCreateReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.pallet.vo.MesProPalletPageReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
@@ -9,8 +10,13 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper;
+import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProBagTypeEnum;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProPalletStatusEnum;
+import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -18,6 +24,7 @@
 import org.springframework.validation.annotation.Validated;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -39,6 +46,10 @@
     private MesProBagCodeMapper bagCodeMapper;
     @Resource
     private MesProBatchMapper batchMapper;
+    @Resource
+    private MesMdmItemService mdmItemService;
+    @Resource
+    private AdminUserApi adminUserApi;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -69,6 +80,7 @@
                     .setPalletNo(palletNo)
                     .setCapacity(createReqVO.getCapacity())
                     .setBagCount(0)
+                    .setAutoGenerated(false)
                     .setStatus(MesProPalletStatusEnum.IN_USE.getStatus())
                     .setRemark(createReqVO.getRemark()));
         }
@@ -79,6 +91,57 @@
                 .setPalletQuantity((batch.getPalletQuantity() == null ? 0 : batch.getPalletQuantity())
                         + createReqVO.getCount()));
         return pallets.stream().map(MesProPalletDO::getId).toList();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public List<MesProPalletAutoPrintRespVO> autoCreatePallets(Long batchId) {
+        MesProBatchDO batch = batchMapper.selectByIdForUpdate(batchId);
+        if (batch == null) {
+            throw exception(PRO_BATCH_NOT_EXISTS);
+        }
+        if (!MesProBatchStatusEnum.PRODUCING.getStatus().equals(batch.getStatus())) {
+            throw exception(PRO_BATCH_STATUS_NOT_GENERATE_CODE);
+        }
+        MesProBagTypeEnum bagType = MesProBagTypeEnum.valueOfType(batch.getBagType());
+        if (bagType == null) {
+            throw exception(PRO_BATCH_BAG_TYPE_INVALID);
+        }
+        List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO> candidates =
+                bagCodeMapper.selectUnboundEffectiveListByBatchId(batchId);
+        int threshold = bagType.getThreshold();
+        int palletCount = candidates.size() / threshold;
+        if (palletCount == 0) {
+            return Collections.emptyList();
+        }
+
+        int startNo = palletMapper.selectMaxPalletNo(batchId) + 1;
+        List<MesProPalletDO> pallets = new ArrayList<>(palletCount);
+        List<List<Long>> bagIdGroups = new ArrayList<>(palletCount);
+        for (int i = 0; i < palletCount; i++) {
+            int palletNo = startNo + i;
+            MesProPalletDO pallet = new MesProPalletDO()
+                    .setCode(batch.getCode() + "-P" + StrUtil.padPre(String.valueOf(palletNo), 2, '0'))
+                    .setBatchId(batchId)
+                    .setBatchCode(batch.getCode())
+                    .setPalletNo(palletNo)
+                    .setCapacity(threshold)
+                    .setBagCount(threshold)
+                    .setAutoGenerated(true)
+                    .setStatus(MesProPalletStatusEnum.IN_USE.getStatus())
+                    .setRemark("鎸夋湁鏁堣鐮侀槇鍊艰嚜鍔ㄧ敓鎴�");
+            pallets.add(pallet);
+            bagIdGroups.add(candidates.subList(i * threshold, (i + 1) * threshold).stream()
+                    .map(cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO::getId).toList());
+        }
+        palletMapper.insertBatch(pallets);
+        for (int i = 0; i < pallets.size(); i++) {
+            MesProPalletDO pallet = pallets.get(i);
+            bagCodeMapper.bindPalletByIds(bagIdGroups.get(i), pallet.getId(), pallet.getCode());
+        }
+        batchMapper.updateById(new MesProBatchDO().setId(batchId)
+                .setPalletQuantity((batch.getPalletQuantity() == null ? 0 : batch.getPalletQuantity()) + pallets.size()));
+        return buildAutoPrintVOs(pallets, batch);
     }
 
     @Override
@@ -100,6 +163,30 @@
         }
     }
 
+    private List<MesProPalletAutoPrintRespVO> buildAutoPrintVOs(List<MesProPalletDO> pallets, MesProBatchDO batch) {
+        MesMdmItemDO item = batch.getItemId() == null ? null : mdmItemService.getItem(batch.getItemId());
+        AdminUserRespDTO owner = batch.getOwnerUserId() == null ? null : adminUserApi.getUser(batch.getOwnerUserId());
+        return pallets.stream().map(pallet -> new MesProPalletAutoPrintRespVO()
+                .setId(pallet.getId())
+                .setCode(pallet.getCode())
+                .setBatchId(pallet.getBatchId())
+                .setBatchCode(pallet.getBatchCode())
+                .setPalletNo(pallet.getPalletNo())
+                .setCopies(3)
+                .setCapacity(pallet.getCapacity())
+                .setBagCount(pallet.getBagCount())
+                .setItemId(batch.getItemId())
+                .setItemCode(item == null ? null : item.getCode())
+                .setItemName(item == null ? null : item.getName())
+                .setItemSpecification(item == null ? null : item.getSpecification())
+                .setLineCode(batch.getLineCode())
+                .setLineName(batch.getLineName())
+                .setProduceDate(batch.getProduceDate())
+                .setOwnerUserId(batch.getOwnerUserId())
+                .setOwnerUserName(owner == null ? null : owner.getNickname()))
+                .toList();
+    }
+
     @Override
     public MesProPalletDO getPallet(Long id) {
         return palletMapper.selectById(id);

--
Gitblit v1.9.3