From 7580d02bd3f9399143a6a388732658bea2610bc4 Mon Sep 17 00:00:00 2001
From: tuqin <tuqin@tuqin.cn>
Date: 星期二, 29 九月 2026 15:05:19 +0800
Subject: [PATCH] feat: 业务编码统一为16位纯数字并保证全系统唯一

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java                               |   30 +++
 yudao-server/src/main/java/cn/iocoder/yudao/server/framework/bizno/BizNoSeqInitializer.java                             |  102 ++++++++++++
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java                              |   26 +--
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/batch/MesProBatchServiceImpl.java                |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/bagcode/MesProBagCodeServiceImpl.java            |    8 
 yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/BizNoUtils.java                |   43 +++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/trace/MesProTraceResolveController.java |   53 ++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImpl.java      |   26 +--
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/pallet/MesProPalletServiceImpl.java              |   11 +
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java                              |   26 +--
 yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/redis/HrmNoRedisDAO.java                                 |   23 +-
 yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java                          |   45 +----
 yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/redis/no/AfterSaleNoRedisDAO.java          |   23 +-
 13 files changed, 300 insertions(+), 122 deletions(-)

diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/BizNoUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/BizNoUtils.java
new file mode 100644
index 0000000..c1045ac
--- /dev/null
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/number/BizNoUtils.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.framework.common.util.number;
+
+/**
+ * 涓氬姟缂栫爜锛堝崟鎹彿 / 涓绘暟鎹紪鐮侊級鐢熸垚鐨勫叕鍏辩害瀹氥��
+ *
+ * <p>闇�姹傦細绯荤粺涓墍鏈夎嚜鍔ㄧ敓鎴愮殑涓氬姟缂栫爜缁熶竴涓恒��16 浣嶇函鏁板瓧銆嶏紝涓斿叏绯荤粺鍞竴锛堜换鎰忎袱鏉$紪鐮侀兘涓嶉噸澶嶏級銆�
+ *
+ * <p>瀹炵幇绾﹀畾锛氬叏绯荤粺鍏辩敤鍚屼竴涓� Redis 鑷 KEY锛坽@link #GLOBAL_SEQ_KEY}锛夛紝
+ * 姣忔鐢熸垚鏃跺鍏舵墽琛� INCR 寰楀埌鍗曡皟閫掑鐨勫簭鍒楀彿锛屽啀宸﹁ˉ '0' 鍒� {@link #CODE_LENGTH} 浣嶃��
+ * 鐢变簬鎵�鏈夌紪鐮侀兘鏉ヨ嚜鍚屼竴鏉¢�掑搴忓垪锛屾晠澶╃劧淇濊瘉銆屽叏绯荤粺鍞竴銆嶃��
+ *
+ * <p>娉ㄦ剰锛�
+ * <ul>
+ *     <li>璇� KEY 涓嶈缃繃鏈熸椂闂达紝閬垮厤杩囨湡褰掗浂瀵艰嚧閲嶅彿锛�</li>
+ *     <li>Redis 鑻ュ彂鐢熸暟鎹涪澶� / 娓呯┖锛岄渶瑕佷粠鏁版嵁搴撶幇鏈夌紪鐮佺殑鏈�澶у�兼仮澶嶈鏁板櫒锛屽惁鍒欏彲鑳介噸鍙枫��</li>
+ * </ul>
+ */
+public class BizNoUtils {
+
+    /**
+     * 鍏ㄧ郴缁熶笟鍔$紪鐮佺粺涓�浣跨敤鐨� Redis 鑷 KEY銆�
+     *
+     * <p>鎵�鏈夋ā鍧楋紙erp / crm / trade / aftersales / hrm / bpm / mes锛夊叡鐢ㄦ KEY锛�
+     * 浠ヤ繚璇併�屽叏绯荤粺鍞竴銆嶃��
+     */
+    public static final String GLOBAL_SEQ_KEY = "biz:seq_no:global";
+
+    /**
+     * 涓氬姟缂栫爜鐨勬�婚暱搴︼細16 浣�
+     */
+    public static final int CODE_LENGTH = 16;
+
+    /**
+     * 灏嗚嚜澧炲簭鍒楀彿鏍煎紡鍖栦负 16 浣嶇函鏁板瓧缂栫爜锛堝乏渚цˉ '0'锛�
+     *
+     * @param seq 鑷搴忓垪鍙�
+     * @return 16 浣嶇函鏁板瓧缂栫爜
+     */
+    public static String format(Long seq) {
+        return String.format("%0" + CODE_LENGTH + "d", seq);
+    }
+
+}
diff --git a/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/redis/no/AfterSaleNoRedisDAO.java b/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/redis/no/AfterSaleNoRedisDAO.java
index 856c862..f945f06 100644
--- a/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/redis/no/AfterSaleNoRedisDAO.java
+++ b/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/redis/no/AfterSaleNoRedisDAO.java
@@ -1,14 +1,9 @@
 package cn.iocoder.yudao.module.aftersales.dal.redis.no;
 
-import cn.hutool.core.date.DatePattern;
-import cn.hutool.core.date.DateUtil;
-import cn.iocoder.yudao.module.aftersales.dal.redis.RedisKeyConstants;
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
 import jakarta.annotation.Resource;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Repository;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
 
 /**
  * 鍞悗妯″潡鍗曞彿 Redis DAO
@@ -30,17 +25,17 @@
     private StringRedisTemplate stringRedisTemplate;
 
     /**
-     * 鐢熸垚搴忓彿锛屼娇鐢ㄥ綋鍓嶆棩鏈燂紝鏍煎紡涓� {PREFIX} + yyyyMMdd + 6 浣嶈嚜澧�
+     * 鐢熸垚 16 浣嶇函鏁板瓧涓氬姟缂栫爜锛堝叏绯荤粺鍞竴锛夈��
      *
-     * @param prefix 鍓嶇紑
-     * @return 搴忓彿
+     * <p>涓哄吋瀹规棦鏈夎皟鐢ㄦ柟锛屼繚鐣� {@code prefix} 鍏ュ弬锛屼絾涓嶅啀鍙備笌缂栫爜鎷兼帴銆�
+     *
+     * @param prefix 鍗曟嵁绫诲瀷鍓嶇紑锛堝凡涓嶅啀鍙備笌缂栫爜鎷兼帴锛�
+     * @return 16 浣嶇函鏁板瓧缂栫爜
      */
     public String generate(String prefix) {
-        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);
-        String key = RedisKeyConstants.NO + noPrefix;
-        Long no = stringRedisTemplate.opsForValue().increment(key);
-        stringRedisTemplate.expire(key, Duration.ofDays(1L));
-        return noPrefix + String.format("%06d", no);
+        // 鍏ㄧ郴缁熷叡鐢ㄥ悓涓�鑷搴忓垪锛屼繚璇佺紪鐮併��16 浣嶇函鏁板瓧銆嶄笖鍏ㄧ郴缁熷敮涓�
+        Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY);
+        return BizNoUtils.format(no);
     }
 
 }
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java
index 2a14f51..4f4fcb1 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/redis/BpmProcessIdRedisDAO.java
@@ -1,16 +1,10 @@
 package cn.iocoder.yudao.module.bpm.dal.redis;
 
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
 import jakarta.annotation.Resource;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Repository;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
-
-import static cn.hutool.core.date.DatePattern.*;
 
 /**
  * BPM 娴佺▼ Id 缂栫爜鐨� Redis DAO
@@ -24,38 +18,17 @@
     private StringRedisTemplate stringRedisTemplate;
 
     /**
-     * 鐢熸垚搴忓彿锛屼娇鐢ㄥ畾涔夌殑 processIdRule 瑙勫垯鐢熸垚
+     * 鐢熸垚 16 浣嶇函鏁板瓧涓氬姟缂栫爜锛堝叏绯荤粺鍞竴锛夈��
      *
-     * @param processIdRule 瑙勫垯
-     * @return 搴忓彿
+     * <p>涓哄吋瀹规棦鏈夎皟鐢ㄦ柟锛屼繚鐣� {@code processIdRule} 鍏ュ弬锛屼絾涓嶅啀鍙備笌缂栫爜鎷兼帴銆�
+     *
+     * @param processIdRule 娴佺▼ Id 瑙勫垯锛堝凡涓嶅啀鍙備笌缂栫爜鎷兼帴锛�
+     * @return 16 浣嶇函鏁板瓧缂栫爜
      */
     public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) {
-        // 鐢熸垚鏃ユ湡鍓嶇紑
-        String infix = "";
-        switch (processIdRule.getInfix()) {
-            case "DAY":
-                infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN);
-                break;
-            case "HOUR":
-                infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HH");
-                break;
-            case "MINUTE":
-                infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HHmm");
-                break;
-            case "SECOND":
-                infix = DateUtil.format(LocalDateTime.now(), PURE_DATETIME_PATTERN);
-                break;
-        }
-
-        // 鐢熸垚搴忓彿
-        String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix();
-        String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix;
-        Long no = stringRedisTemplate.opsForValue().increment(key);
-        if (StrUtil.isEmpty(infix)) {
-            // 鐗规畩锛氭病鏈夊墠缂�锛屽垯涓嶈兘杩囨湡锛屼笉鑳芥瘡娆¢兘鏄粠 0 寮�濮嬨�傚彲瑙� https://t.zsxq.com/MU1E2 璁ㄨ
-            stringRedisTemplate.expire(key, Duration.ofDays(1L));
-        }
-        return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no);
+        // 鍏ㄧ郴缁熷叡鐢ㄥ悓涓�鑷搴忓垪锛屼繚璇佺紪鐮併��16 浣嶇函鏁板瓧銆嶄笖鍏ㄧ郴缁熷敮涓�
+        Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY);
+        return BizNoUtils.format(no);
     }
 
 }
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java
index 7303e0c..23dc84f 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/redis/no/CrmNoRedisDAO.java
@@ -1,14 +1,9 @@
 package cn.iocoder.yudao.module.crm.dal.redis.no;
 
-import cn.hutool.core.date.DatePattern;
-import cn.hutool.core.date.DateUtil;
-import cn.iocoder.yudao.module.crm.dal.redis.RedisKeyConstants;
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
 import jakarta.annotation.Resource;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Repository;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
 
 
 /**
@@ -43,20 +38,17 @@
     private StringRedisTemplate stringRedisTemplate;
 
     /**
-     * 鐢熸垚搴忓彿锛屼娇鐢ㄥ綋鍓嶆棩鏈燂紝鏍煎紡涓� {PREFIX} + yyyyMMdd + 6 浣嶈嚜澧�
-     * 渚嬪璇达細QTRK 202109 000001 锛堟病鏈変腑闂寸┖鏍硷級
+     * 鐢熸垚 16 浣嶇函鏁板瓧涓氬姟缂栫爜锛堝叏绯荤粺鍞竴锛夈��
      *
-     * @param prefix 鍓嶇紑
-     * @return 搴忓彿
+     * <p>涓哄吋瀹规棦鏈夎皟鐢ㄦ柟锛屼繚鐣� {@code prefix} 鍏ュ弬锛屼絾涓嶅啀鍙備笌缂栫爜鎷兼帴銆�
+     *
+     * @param prefix 鍗曟嵁绫诲瀷鍓嶇紑锛堝凡涓嶅啀鍙備笌缂栫爜鎷兼帴锛�
+     * @return 16 浣嶇函鏁板瓧缂栫爜
      */
     public String generate(String prefix) {
-        // 閫掑搴忓彿
-        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);
-        String key = RedisKeyConstants.NO + noPrefix;
-        Long no = stringRedisTemplate.opsForValue().increment(key);
-        // 璁剧疆杩囨湡鏃堕棿
-        stringRedisTemplate.expire(key, Duration.ofDays(1L));
-        return noPrefix + String.format("%06d", no);
+        // 鍏ㄧ郴缁熷叡鐢ㄥ悓涓�鑷搴忓垪锛屼繚璇佺紪鐮併��16 浣嶇函鏁板瓧銆嶄笖鍏ㄧ郴缁熷敮涓�
+        Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY);
+        return BizNoUtils.format(no);
     }
 
 }
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
index bdce48c..1c332a1 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/redis/no/ErpNoRedisDAO.java
@@ -1,14 +1,9 @@
 package cn.iocoder.yudao.module.erp.dal.redis.no;
 
-import cn.hutool.core.date.DatePattern;
-import cn.hutool.core.date.DateUtil;
-import cn.iocoder.yudao.module.erp.dal.redis.RedisKeyConstants;
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
 import jakarta.annotation.Resource;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Repository;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
 
 
 /**
@@ -52,20 +47,17 @@
     private StringRedisTemplate stringRedisTemplate;
 
     /**
-     * 鐢熸垚搴忓彿锛屼娇鐢ㄥ綋鍓嶆棩鏈燂紝鏍煎紡涓� {PREFIX} + yyyyMMdd + 6 浣嶈嚜澧�
-     * 渚嬪璇达細CGDD 202109 000001 锛堟病鏈変腑闂寸┖鏍硷級
+     * 鐢熸垚 16 浣嶇函鏁板瓧涓氬姟缂栫爜锛堝叏绯荤粺鍞竴锛夈��
      *
-     * @param prefix 鍓嶇紑
-     * @return 搴忓彿
+     * <p>涓哄吋瀹规棦鏈夎皟鐢ㄦ柟锛屼繚鐣� {@code prefix} 鍏ュ弬锛屼絾涓嶅啀鍙備笌缂栫爜鎷兼帴銆�
+     *
+     * @param prefix 鍗曟嵁绫诲瀷鍓嶇紑锛堝凡涓嶅啀鍙備笌缂栫爜鎷兼帴锛�
+     * @return 16 浣嶇函鏁板瓧缂栫爜
      */
     public String generate(String prefix) {
-        // 閫掑搴忓彿
-        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);
-        String key = RedisKeyConstants.NO + noPrefix;
-        Long no = stringRedisTemplate.opsForValue().increment(key);
-        // 璁剧疆杩囨湡鏃堕棿
-        stringRedisTemplate.expire(key, Duration.ofDays(1L));
-        return noPrefix + String.format("%06d", no);
+        // 鍏ㄧ郴缁熷叡鐢ㄥ悓涓�鑷搴忓垪锛屼繚璇佺紪鐮併��16 浣嶇函鏁板瓧銆嶄笖鍏ㄧ郴缁熷敮涓�
+        Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY);
+        return BizNoUtils.format(no);
     }
 
 }
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/redis/HrmNoRedisDAO.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/redis/HrmNoRedisDAO.java
index 44009fd..fe10bb3 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/redis/HrmNoRedisDAO.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/redis/HrmNoRedisDAO.java
@@ -1,13 +1,9 @@
 package cn.iocoder.yudao.module.hrm.dal.redis;
 
-import cn.hutool.core.date.DatePattern;
-import cn.hutool.core.date.DateUtil;
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
 import jakarta.annotation.Resource;
 import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Repository;
-
-import java.time.Duration;
-import java.time.LocalDateTime;
 
 /**
  * HRM 缂栧彿鐢熸垚鐨� Redis DAO
@@ -131,19 +127,16 @@
     }
 
     /**
-     * 鐢熸垚缂栧彿
+     * 鐢熸垚 16 浣嶇函鏁板瓧涓氬姟缂栫爜锛堝叏绯荤粺鍞竴锛夈��
      *
-     * @param prefix 鍓嶇紑
-     * @param digits 浣嶆暟
-     * @return 缂栧彿
+     * <p>涓哄吋瀹规棦鏈夎皟鐢ㄦ柟锛屼繚鐣欏叆鍙傦紝浣嗕笉鍐嶅弬涓庣紪鐮佹嫾鎺ャ��
+     *
+     * @return 16 浣嶇函鏁板瓧缂栫爜
      */
     public String generate(String prefix, int digits) {
-        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATE_PATTERN);
-        String key = HrmRedisKeyConstants.NO + noPrefix;
-        Long no = stringRedisTemplate.opsForValue().increment(key);
-        // 璁剧疆杩囨湡鏃堕棿 1 澶�
-        stringRedisTemplate.expire(key, Duration.ofDays(1L));
-        return noPrefix + String.format("%0" + digits + "d", no);
+        // 鍏ㄧ郴缁熷叡鐢ㄥ悓涓�鑷搴忓垪锛屼繚璇佺紪鐮併��16 浣嶇函鏁板瓧銆嶄笖鍏ㄧ郴缁熷敮涓�
+        Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY);
+        return BizNoUtils.format(no);
     }
 
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/trace/MesProTraceResolveController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/trace/MesProTraceResolveController.java
new file mode 100644
index 0000000..0061b4b
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/trace/MesProTraceResolveController.java
@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.trace;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.mes.service.pro.bagcode.MesProBagCodeService;
+import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService;
+import cn.iocoder.yudao.module.mes.service.pro.pallet.MesProPalletService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+/**
+ * MES 鎵爜杩芥函 - 缂栫爜绫诲瀷瑙f瀽銆�
+ *
+ * <p>缂栫爜缁熶竴涓恒��16 浣嶇函鏁板瓧銆嶅悗锛屾棤娉曞啀鎸夊墠缂�鍒ゆ柇绫诲瀷锛圔AG-/PL-/PB- 绛夊墠缂�宸插彇娑堬級锛�
+ * 鍓嶇鎵爜鍚庡厛璋冪敤鏈帴鍙f寜缂栫爜鍙嶆煡鍏舵墍灞炵被鍨嬶紝鍐嶈皟鐢ㄥ搴旂被鍨嬬殑杩芥函鎺ュ彛銆�
+ */
+@Tag(name = "绠$悊鍚庡彴 - MES 鎵爜婧簮锛堢被鍨嬭В鏋愶級")
+@RestController
+@RequestMapping("/mes/pro/trace")
+@Validated
+public class MesProTraceResolveController {
+
+    @Resource
+    private MesProBagCodeService bagCodeService;
+    @Resource
+    private MesProPalletService palletService;
+    @Resource
+    private MesProBatchService batchService;
+
+    @GetMapping("/resolve-type")
+    @Operation(summary = "鏍规嵁缂栫爜瑙f瀽婧簮绫诲瀷锛歜ag / pallet / batch锛涘潎涓嶅尮閰嶈繑鍥炵┖")
+    @Parameter(name = "code", description = "缂栫爜锛堜簩缁寸爜鍐呭锛�", required = true)
+    @PreAuthorize("@ss.hasPermission('mes:pro-bag-code:query')")
+    public CommonResult<String> resolveType(@RequestParam("code") String code) {
+        if (bagCodeService.getBagCodeByCode(code) != null) {
+            return success("bag");
+        }
+        if (palletService.getPalletByCode(code) != null) {
+            return success("pallet");
+        }
+        if (batchService.getBatchByCode(code) != null) {
+            return success("batch");
+        }
+        return success(null);
+    }
+
+}
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
new file mode 100644
index 0000000..41564e1
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.mes.dal.redis;
+
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Repository;
+
+/**
+ * MES 涓氬姟缂栫爜鐢熸垚鐨� Redis DAO锛堝疄渚嬬爜锛氳鐮� / 鎵樼洏鐮� / 涓存椂鐮佺瓑锛夈��
+ *
+ * <p>闇�姹傦細鎵�鏈変笟鍔$紪鐮佺粺涓�涓恒��16 浣嶇函鏁板瓧銆嶏紝涓斿叏绯荤粺鍞竴銆�
+ * 杩欓噷澶嶇敤鍏ㄧ郴缁熺粺涓�鐨� Redis 鑷 KEY锛屼繚璇佷笌鍏跺畠妯″潡鐨勭紪鐮佷篃涓嶉噸澶嶃��
+ */
+@Repository
+public class MesCodeRedisDAO {
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 鐢熸垚涓�涓� 16 浣嶇函鏁板瓧涓氬姟缂栫爜锛堝叏绯荤粺鍞竴锛�
+     *
+     * @return 16 浣嶇函鏁板瓧缂栫爜
+     */
+    public String generate() {
+        Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY);
+        return BizNoUtils.format(no);
+    }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImpl.java
index 983dff0..c4daea0 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImpl.java
@@ -4,6 +4,7 @@
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodePartDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodeRecordDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.autocode.MesMdAutoCodeRuleDO;
@@ -13,6 +14,7 @@
 import cn.iocoder.yudao.module.mes.service.md.autocode.strategy.MesMdAutoCodePartStrategy;
 import jakarta.annotation.PostConstruct;
 import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -41,6 +43,9 @@
     private MesMdAutoCodeRuleService ruleService;
     @Resource
     private MesMdAutoCodePartService partService;
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
 
     @Resource
     private List<MesMdAutoCodePartStrategy> strategyList;
@@ -93,23 +98,10 @@
      */
     private String generateCode(MesMdAutoCodeRuleDO rule, List<MesMdAutoCodePartDO> parts,
                                 MesMdAutoCodeContext context) {
-        StringBuilder codeBuilder = new StringBuilder();
-        for (MesMdAutoCodePartDO part : parts) {
-            MesMdAutoCodePartStrategy strategy = strategyMap.get(part.getType());
-            Assert.notNull(strategy, () -> exception(AUTO_CODE_GENERATE_FAILED));
-            // 鐢熸垚鍒嗘缂栫爜
-            String partCode = strategy.generate(part, context);
-            // 缁熶竴鎴彇鎸囧畾闀垮害
-            partCode = StrUtil.sub(partCode, 0, part.getLength());
-            // 鎷兼帴鍒嗘缂栫爜
-            codeBuilder.append(partCode);
-        }
-        // 琛ラ綈澶勭悊
-        String result = codeBuilder.toString();
-        if (Boolean.TRUE.equals(rule.getPadded()) && rule.getMaxLength() != null) {
-            result = padCode(result, rule);
-        }
-        return result;
+        // 闇�姹傦細鎵�鏈変笟鍔$紪鐮佺粺涓�涓恒��16 浣嶇函鏁板瓧銆嶏紝涓斿叏绯荤粺鍞竴銆�
+        // 鍥犳涓嶅啀鎸夎鍒欏垎娈垫嫾瑁咃紙鍥哄畾瀛楃/鏃ユ湡娈典細甯﹀叆瀛楁瘝锛夛紝鏀逛负鍙栧叏绯荤粺缁熶竴鑷搴忓垪銆�
+        Long no = stringRedisTemplate.opsForValue().increment(BizNoUtils.GLOBAL_SEQ_KEY);
+        return BizNoUtils.format(no);
     }
 
     /**
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 c8f11c4..591eaf2 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
@@ -25,6 +25,7 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeReplaceMapper;
 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.redis.MesCodeRedisDAO;
 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.MesProPalletStatusEnum;
@@ -85,6 +86,9 @@
      * 璁¢噺鍗曚綅绫诲瀷銆孨鍗冨厠銆嶏細姣忔墭鐩樿 N 涓鐮�
      */
     private static final Pattern KG_TYPE_PATTERN = Pattern.compile("^(\\d{1,5})鍗冨厠$");
+
+    @Resource
+    private MesCodeRedisDAO codeRedisDao;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -163,7 +167,7 @@
             int bagNo = startNo + i;
             bagCodes.add(new MesProBagCodeDO()
                     .setUuid(uuid)
-                    .setCode("BAG-" + StrUtil.padPre(String.valueOf(bagNo), 8, '0'))
+                    .setCode(codeRedisDao.generate())
                     .setBagNo(bagNo)
                     .setItemId(item.getId())
                     .setItemCode(item.getCode())
@@ -339,7 +343,7 @@
             MesProPalletDO pallet = oldBagCode.getPalletId() == null ? null : palletMap.get(oldBagCode.getPalletId());
             MesProBagCodeDO newBagCode = new MesProBagCodeDO()
                     .setUuid(replaceUuid)
-                    .setCode(batch.getCode() + "-" + StrUtil.padPre(String.valueOf(newBagNo), 4, '0'))
+                    .setCode(codeRedisDao.generate())
                     .setBagNo(newBagNo)
                     .setBatchId(batch.getId())
                     .setBatchCode(batch.getCode())
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 7b63e97..a5d4fbe 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.MesProBatchMapper;
 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.dal.redis.MesCodeRedisDAO;
 import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum;
 import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
@@ -60,6 +61,9 @@
 
     @Resource
     private MesProPalletService palletService;
+
+    @Resource
+    private MesCodeRedisDAO codeRedisDao;
 
     @Resource
     private MesMdAutoCodeRecordService autoCodeRecordService;
@@ -109,7 +113,7 @@
         List<MesProBatchDO> list = new ArrayList<>(reqVO.getCount());
         for (int i = 0; i < reqVO.getCount(); i++) {
             list.add(new MesProBatchDO()
-                    .setTempCode("PB-" + StrUtil.padPre(String.valueOf(startSeq + i), 8, '0'))
+                    .setTempCode(codeRedisDao.generate())
                     .setBagQuantity(0)
                     .setPalletQuantity(0)
                     .setStatus(MesProBatchStatusEnum.DRAFT.getStatus())
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 50385bb..65d8be9 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
@@ -13,6 +13,7 @@
 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.redis.MesCodeRedisDAO;
 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.MesProPalletStatusEnum;
@@ -48,6 +49,9 @@
     @Resource
     private MesProBatchMapper batchMapper;
 
+    @Resource
+    private MesCodeRedisDAO codeRedisDao;
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public List<Long> batchCreatePallet(MesProPalletBatchCreateReqVO createReqVO) {
@@ -71,7 +75,7 @@
         for (int i = 0; i < createReqVO.getCount(); i++) {
             int palletNo = startNo + i;
             pallets.add(new MesProPalletDO()
-                    .setCode(batch.getCode() + "-P" + StrUtil.padPre(String.valueOf(palletNo), 2, '0'))
+                    .setCode(codeRedisDao.generate())
                     .setBatchId(batch.getId())
                     .setBatchCode(batch.getCode())
                     .setPalletNo(palletNo)
@@ -97,7 +101,8 @@
         List<MesProPalletDO> list = new ArrayList<>(reqVO.getCount());
         for (int i = 0; i < reqVO.getCount(); i++) {
             list.add(new MesProPalletDO()
-                    .setTempCode("PL-" + StrUtil.padPre(String.valueOf(startSeq + i), 8, '0'))
+                    .setTempCode(codeRedisDao.generate())
+                    .setCapacity(reqVO.getCapacity())
                     .setBagCount(0)
                     .setAutoGenerated(false)
                     .setStatus(MesProPalletStatusEnum.IN_USE.getStatus())
@@ -164,7 +169,7 @@
             int palletNo = startNo + i;
             palletMapper.updateById(new MesProPalletDO()
                     .setId(pallets.get(i).getId())
-                    .setCode(batch.getCode() + "-P" + StrUtil.padPre(String.valueOf(palletNo), 2, '0'))
+                    .setCode(codeRedisDao.generate())
                     .setBatchId(batch.getId())
                     .setBatchCode(batch.getCode())
                     .setPalletNo(palletNo));
diff --git a/yudao-server/src/main/java/cn/iocoder/yudao/server/framework/bizno/BizNoSeqInitializer.java b/yudao-server/src/main/java/cn/iocoder/yudao/server/framework/bizno/BizNoSeqInitializer.java
new file mode 100644
index 0000000..826b58d
--- /dev/null
+++ b/yudao-server/src/main/java/cn/iocoder/yudao/server/framework/bizno/BizNoSeqInitializer.java
@@ -0,0 +1,102 @@
+package cn.iocoder.yudao.server.framework.bizno;
+
+import cn.iocoder.yudao.framework.common.util.number.BizNoUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.sql.DataSource;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 涓氬姟缂栫爜璁℃暟鍣ㄥ厹搴曞垵濮嬪寲銆�
+ *
+ * <p>鍏ㄧ郴缁熺紪鐮佺粺涓�鐢� Redis 鐨勫叏灞�鑷 KEY锛坽@link BizNoUtils#GLOBAL_SEQ_KEY}锛夌敓鎴愶紝
+ * 鐢ㄤ簬淇濊瘉銆�16 浣嶇函鏁板瓧 + 鍏ㄧ郴缁熷敮涓�銆嶃�備絾 Redis 鏄唴瀛樺瓨鍌紝鑻ヨ娓呯┖ / 涓㈠け锛�
+ * 璇� KEY 浼氬綊闆讹紝浠� 1 閲嶆柊寮�濮嬶紝瀵艰嚧涓庡巻鍙茬紪鐮侀噸澶嶃��
+ *
+ * <p>鍥犳锛氬綋鍚姩鏃跺彂鐜拌 KEY **涓嶅瓨鍦�**锛堣鏄� Redis 涓㈠け杩囪鏁帮級锛�
+ * 浠庢暟鎹簱涓壂鎻忔墍鏈変笟鍔$紪鐮佸垪鐨勬渶澶у�硷紝鎶婅鏁板櫒鎭㈠鍒拌鏈�澶у�硷紝閬垮厤閲嶅彿銆�
+ *
+ * <p>璇存槑锛�
+ * <ul>
+ *     <li>浠呭湪 KEY 缂哄け鏃舵壂鎻忥紙姝e父鍚姩鏃犻澶栧紑閿�锛夛紱</li>
+ *     <li>鎵弿鏄� best-effort锛屽け璐ュ彧鍛婅銆佷笉褰卞搷鍚姩锛�</li>
+ *     <li>鍙粺璁°��16 浣嶇函鏁板瓧銆嶇殑缂栫爜锛屾棫鐨勫甫瀛楁瘝缂栫爜浼氳蹇界暐銆�</li>
+ * </ul>
+ */
+@Component
+@Slf4j
+public class BizNoSeqInitializer implements ApplicationRunner {
+
+    private final StringRedisTemplate stringRedisTemplate;
+    private final JdbcTemplate jdbcTemplate;
+
+    public BizNoSeqInitializer(StringRedisTemplate stringRedisTemplate, DataSource dataSource) {
+        this.stringRedisTemplate = stringRedisTemplate;
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+    }
+
+    @Override
+    public void run(ApplicationArguments args) {
+        String current = stringRedisTemplate.opsForValue().get(BizNoUtils.GLOBAL_SEQ_KEY);
+        if (current != null) {
+            log.info("[run][涓氬姟缂栫爜璁℃暟鍣ㄥ厹搴昡 Redis 璁℃暟宸插瓨鍦�({})锛岃烦杩囧簱鍐呮壂鎻�", current);
+            return;
+        }
+        long start = System.currentTimeMillis();
+        try {
+            long maxDb = computeMaxCodeInDb();
+            stringRedisTemplate.opsForValue().set(BizNoUtils.GLOBAL_SEQ_KEY, String.valueOf(maxDb));
+            log.info("[run][涓氬姟缂栫爜璁℃暟鍣ㄥ厹搴昡 Redis 璁℃暟缂哄け锛屽凡浠庡簱鍐呮渶澶х紪鐮佹仮澶嶄负 {}", maxDb);
+        } catch (Exception e) {
+            log.warn("[run][涓氬姟缂栫爜璁℃暟鍣ㄥ厹搴昡 鍒濆鍖栧け璐ワ紙涓嶅奖鍝嶅惎鍔級锛岃鎺掓煡", e);
+        } finally {
+            log.info("[run][涓氬姟缂栫爜璁℃暟鍣ㄥ厹搴昡 搴撳唴鎵弿鑰楁椂 {} ms", System.currentTimeMillis() - start);
+        }
+    }
+
+    /**
+     * 鎵弿搴撳唴鎵�鏈夊彲鑳藉瓨鏀句笟鍔$紪鐮佺殑鍒楋紝杩斿洖銆�16 浣嶇函鏁板瓧銆嶇紪鐮佺殑鏈�澶у�笺��
+     */
+    private long computeMaxCodeInDb() {
+        String schema = jdbcTemplate.queryForObject("SELECT DATABASE()", String.class);
+        // 鎵惧嚭鎵�鏈� varchar/char 涓斿悕瀛楀儚缂栫爜鐨勫垪锛坣o / code / *_no / *_code / temp_code锛�
+        List<Map<String, Object>> columns = jdbcTemplate.queryForList(
+                "SELECT table_name, column_name FROM information_schema.columns " +
+                        "WHERE table_schema = ? AND data_type IN ('varchar', 'char') " +
+                        "AND (column_name = 'no' OR column_name = 'code' OR column_name = 'temp_code' " +
+                        "     OR column_name LIKE '%\\_no' OR column_name LIKE '%\\_code')",
+                schema);
+        if (columns.isEmpty()) {
+            return 0L;
+        }
+        StringBuilder sql = new StringBuilder("SELECT MAX(v) FROM (");
+        boolean first = true;
+        for (Map<String, Object> column : columns) {
+            String table = String.valueOf(column.get("table_name"));
+            String col = String.valueOf(column.get("column_name"));
+            // 璺宠繃 Quartz 妗嗘灦琛紝閬垮厤鏃犲叧鎵弿
+            if (table.toLowerCase().startsWith("qrtz_") || table.toLowerCase().startsWith("act_")) {
+                continue;
+            }
+            if (!first) {
+                sql.append(" UNION ALL ");
+            }
+            first = false;
+            sql.append("SELECT MAX(CAST(`").append(col).append("` AS UNSIGNED)) AS v FROM `").append(table)
+                    .append("` WHERE `").append(col).append("` REGEXP '^[0-9]{").append(BizNoUtils.CODE_LENGTH).append("}$'");
+        }
+        if (first) {
+            return 0L;
+        }
+        sql.append(") t");
+        Long max = jdbcTemplate.queryForObject(sql.toString(), Long.class);
+        return max == null ? 0L : max;
+    }
+
+}

--
Gitblit v1.9.3