From 87733eb3623593da247c051593d64b8ff9e1d3a4 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 06 十一月 2026 19:00:39 +0800
Subject: [PATCH] feat(crm,mes): 扩展客户字段与重构批次号生成逻辑

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/redis/MesCodeRedisDAO.java |   91 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 91 insertions(+), 0 deletions(-)

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 5a805eb..6217e44 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
@@ -7,6 +7,7 @@
 import org.springframework.data.redis.core.script.RedisScript;
 import org.springframework.stereotype.Repository;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -24,6 +25,34 @@
     private static final String BAG_NO_SEQ_KEY = "mes:bag_no:seq";
 
     /**
+     * 鐢熶骇鎵规娴佹按鍙疯嚜澧� KEY 鍓嶇紑锛堟寜骞翠唤闅旂锛�
+     */
+    private static final String BATCH_NO_SEQ_KEY_PREFIX = "mes:pro_batch_code:seq:";
+
+    /**
+     * 鍗曟鎵归噺鍙栫爜鐨勬渶澶т釜鏁�
+     * <p>
+     * 鍙� Redis 鍗曟潯鍛戒护鍙傛暟瑙勬ā涓庡搷搴斾綋澶у皬绾︽潫锛岃繃澶у弽鑰屽鍔犲崟娆¢樆濉炴椂闂淬��
+     */
+    public static final int MAX_BATCH_SIZE = 5000;
+
+    /**
+     * 鎵归噺鍐欏叆骞惰繑鍥炲疄闄呯敓鏁堢殑缂栫爜銆�
+     * <p>
+     * 閫愪釜 SADD 鍦ㄥ悓涓�鑴氭湰鍐呭畬鎴愶細涓庤皟鐢ㄦ柟閫愭潯 SADD 鐩告瘮锛岀綉缁滃線杩斾粠 N 娆¢檷涓� 1 娆★紱
+     * 鍚屾椂鍥犱负鑴氭湰鍘熷瓙鎵ц锛屽苟鍙戣皟鐢ㄤ箣闂翠篃涓嶄細浜х敓閲嶅缂栫爜銆�
+     */
+    private static final RedisScript<List> ADD_UNIQUE_CODES_SCRIPT = new DefaultRedisScript<>(
+            "local res = {} "
+                    + "for i = 1, #ARGV do "
+                    + "  if redis.call('SADD', KEYS[1], ARGV[i]) == 1 then "
+                    + "    res[#res + 1] = ARGV[i] "
+                    + "  end "
+                    + "end "
+                    + "return res",
+            List.class);
+
+    /**
      * 鍘熷瓙鍒嗛厤琚嬪簭鍙峰尯闂寸殑 Lua 鑴氭湰銆�
      * <p>
      * 鍏堟寜搴撳唴鏈�澶у簭鍙锋牎姝e熀鍑嗗�硷紙Redis 棣栨浣跨敤銆佹垨搴撳唴鏁版嵁琚洿鎺ュ鍏ュ鑷村熀鍑嗚惤鍚庢椂锛夛紝
@@ -36,6 +65,17 @@
                     + "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);
+
+    /**
+     * 鍘熷瓙鍒嗛厤鐢熶骇鎵规娴佹按鍙凤細鍏堟寜搴撳唴褰撳墠鏈�澶ф祦姘存牎姝e熀鍑嗗�硷紝鍐嶈嚜澧炪��
+     * 鎵嬪姩褰曞叆杈冨ぇ鎵规鍙锋垨鐩存帴瀵煎叆鏁版嵁鍚庯紝涓嬩竴娆¤嚜鍔ㄥ彇鍙蜂笉浼氫笌涔嬮噸澶嶃��
+     */
+    private static final RedisScript<Long> ALLOCATE_BATCH_NO_SCRIPT = new DefaultRedisScript<>(
+            "local cur = redis.call('GET', KEYS[1]) "
+                    + "local base = tonumber(ARGV[1]) "
+                    + "if (not cur) or (tonumber(cur) < base) then redis.call('SET', KEYS[1], base) end "
+                    + "return redis.call('INCR', KEYS[1])",
             Long.class);
 
     @Resource
@@ -59,6 +99,57 @@
     }
 
     /**
+     * 鎵归噺鐢熸垚 count 涓敮涓�鐨� 16 浣嶇函鏁板瓧涓氬姟缂栫爜锛堝叏绯荤粺鍞竴锛�
+     * <p>
+     * 澶ф壒閲忕敓鎴愭椂涓嶈寰幆璋冪敤 {@link #generate()}锛氶偅浼氫骇鐢� N 娆� Redis 寰�杩旓紝
+     * 鍑犲崄涓囨潯鍗冲彲鎶婃帴鍙f嫋鍒板垎閽熺骇銆傝繖閲屾瘡鎵规渶澶� {@link #MAX_BATCH_SIZE} 涓�欓�夌爜
+     * 涓�娆¤剼鏈啓鍏ワ紝纰版挒锛堟鐜囨瀬浣庯級鐨勭己棰濆啀琛ヤ竴鎵广��
+     *
+     * @param count 闇�瑕佺殑缂栫爜涓暟
+     * @return 鍞竴缂栫爜鍒楄〃锛岄『搴忔棤淇濊瘉浣嗘暟閲忕瓑浜� count
+     */
+    public List<String> generateBatch(int count) {
+        if (count <= 0) {
+            return List.of();
+        }
+        List<String> result = new ArrayList<>(Math.min(count, 1024));
+        // 16 浣嶉殢鏈虹爜绌洪棿涓� 10^16锛屾甯告儏鍐典笅涓�杞嵆鍙叏閮ㄥ啓鍏ワ紱闄愬埗杞鍙敤浜庡厹搴曞紓甯�
+        for (int round = 0; round < 100 && result.size() < count; round++) {
+            int batchSize = Math.min(count - result.size(), MAX_BATCH_SIZE);
+            List<String> candidates = new ArrayList<>(batchSize);
+            for (int i = 0; i < batchSize; i++) {
+                candidates.add(BizNoUtils.randomCode());
+            }
+            List<?> added = stringRedisTemplate.execute(ADD_UNIQUE_CODES_SCRIPT,
+                    List.of(BizNoUtils.USED_SET_KEY), candidates.toArray(new String[0]));
+            if (added != null) {
+                added.forEach(code -> result.add(String.valueOf(code)));
+            }
+        }
+        if (result.size() < count) {
+            throw new IllegalStateException("鎵归噺鐢熸垚鍞竴涓氬姟缂栫爜澶辫触锛岃閲嶈瘯");
+        }
+        return result;
+    }
+
+    /**
+     * 鍘熷瓙鍒嗛厤鐢熶骇鎵规娴佹按鍙凤細鍏堟寜搴撳唴褰撳墠鏈�澶ф祦姘存牎姝e熀鍑嗗�硷紝鍐嶈嚜澧炪��
+     *
+     * @param year         鎵规鍙峰勾浠斤紙4 浣嶏級
+     * @param dbMaxSerial  鏁版嵁搴撳綋骞村凡鐢ㄦ渶澶ф祦姘村彿锛屼紶 null 瑙嗕负 0
+     * @return 涓嬩竴涓祦姘村彿
+     */
+    public int allocateBatchNo(String year, Integer dbMaxSerial) {
+        Long next = stringRedisTemplate.execute(ALLOCATE_BATCH_NO_SCRIPT,
+                List.of(BATCH_NO_SEQ_KEY_PREFIX + year),
+                String.valueOf(dbMaxSerial == null ? 0 : dbMaxSerial));
+        if (next == null) {
+            throw new IllegalStateException("鍒嗛厤鐢熶骇鎵规娴佹按鍙峰け璐ワ紝璇烽噸璇�");
+        }
+        return next.intValue();
+    }
+
+    /**
      * 鍘熷瓙鍒嗛厤涓�娈佃繛缁殑琚嬪簭鍙凤紝杩斿洖鍖洪棿璧峰鍊笺��
      * <p>
      * 琚嬪簭鍙峰師鍏堢敱銆屾煡搴撳唴鏈�澶у�煎啀 +1銆嶅緱鍒帮紝骞跺彂鐢熸垚鏃朵袱涓姹備細璇诲埌鍚屼竴涓渶澶у�硷紝

--
Gitblit v1.9.3