From 4f52a145d9a7776c820b075168443e398df99886 Mon Sep 17 00:00:00 2001
From: 陈昶聿 <chychen@nbjetron.com>
Date: 星期五, 06 十一月 2026 14:01:19 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/smartor_ls_261103' into smartor_ls_261103

---
 ruoyi-common/src/main/java/com/ruoyi/common/utils/ShortCode.java        |  181 +++++++++++++++++++++++++++++++++++++++++++++
 ruoyi-common/src/main/java/com/ruoyi/common/config/ShortCodeConfig.java |   28 +++++++
 2 files changed, 209 insertions(+), 0 deletions(-)

diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/ShortCodeConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/ShortCodeConfig.java
new file mode 100644
index 0000000..657c25a
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/ShortCodeConfig.java
@@ -0,0 +1,28 @@
+package com.ruoyi.common.config;
+
+import com.ruoyi.common.utils.ShortCode;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 鎶婇厤缃噷鐨勫閾剧煭鐮佸瘑閽ユ敞鍏� {@link ShortCode}銆�
+ *
+ * <p>鐭爜绠楁硶鏄�"涓婚敭 鈫� 6 浣�"鐨勫甫瀵嗛挜鍙屽皠銆傚瘑閽ュ彧鍦ㄦ湇鍔$鍙備笌璁$畻锛屼笉鍑虹幇鍦ㄩ摼鎺ラ噷锛�
+ * <b>涓婄嚎鍚庝笉瑕佷慨鏀�</b>锛屽惁鍒欏凡鍙戝嚭鐨� 6 浣嶇煭閾炬棤娉曞弽瑙o紙浼氬洖钀藉埌 {@code service_out_path.radix}
+ * 鍏滃簳锛岃�岃鍒楀彲鑳藉凡琚埅鏂級銆傞厤缃」缂虹渷鏃� {@link ShortCode} 浣跨敤鍐呯疆缂虹渷瀵嗛挜銆�
+ *
+ * @author smartor
+ */
+@Component
+public class ShortCodeConfig {
+
+    @Value("${shortcode.secret:}")
+    private String secret;
+
+    @PostConstruct
+    public void init() {
+        ShortCode.setSecret(secret);
+    }
+}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ShortCode.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ShortCode.java
new file mode 100644
index 0000000..6687739
--- /dev/null
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ShortCode.java
@@ -0,0 +1,181 @@
+package com.ruoyi.common.utils;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 澶栭摼鐭爜锛氭妸 {@code service_out_path.id} 鏄犲皠鎴愬浐瀹� 6 浣� base62 瀛楃涓层��
+ *
+ * <p><b>涓轰粈涔堥渶瑕佸畠</b>锛氬師鏉ョ殑鐭爜鏄� {@code String.format("%03X", id)}锛岀瓑浜庢妸鑷涓婚敭鐩存帴鍐欏湪
+ * 鐭俊閾炬帴閲岋紙{@code /wt?p=1F4}锛夈��3~4 浣嶅崄鍏繘鍒剁殑绌洪棿鍙湁 4096 / 65536锛岃�岃繖寮犺〃宸叉湁涓婁竾鏉¤褰曪紝
+ * 绌洪棿鍑犱箮琚崰婊� 鈥斺�� 鏀诲嚮鑰呴�愪釜璇曞氨鑳芥妸鎵�鏈夐殢璁胯褰曟媺涓�閬嶃��2026-11-05 娴欐睙鐪佺珛鍚屽痉鍖婚櫌鐨�
+ * 缃戠粶瀹夊叏閫氭姤锛圫20262767锛夊氨鏄墦鐨勮繖涓偣銆�
+ *
+ * <p><b>鍋氫簡浠�涔�</b>锛氬涓婚敭鍋氫竴娆�<b>甯﹀瘑閽ョ殑浼殢鏈哄弻灏�</b>锛�6 杞� Feistel 缃戠粶锛岃疆鍑芥暟鐢�
+ * HmacSHA256锛夛紝杈撳嚭浠嶆槸 6 浣嶃�佷粛鏄�"涓婚敭鐨勭函鍑芥暟"锛屽洜姝わ細
+ * <ul>
+ *   <li>鍚屼竴涓� id 鍦ㄤ换浣曟満鍣ㄣ�佷换浣曟椂鍊欓兘绠楀嚭鍚屼竴涓爜锛堜笌 {@code %03X} 涓�鏍锋槸纭畾鎬х殑锛夛紝
+ *       鎵�浠ユ暎钀藉湪 RyTask / RedisMqReceiver / ServiceSubtaskServiceImpl 绛� 14 澶勭殑
+ *       "浠� id 閲嶇畻鐭爜"閫昏緫涓嶉渶瑕佹敼鏁版嵁娴侊紱</li>
+ *   <li>涓嶉渶瑕佹柊澧炴暟鎹簱鍒楋紝涔熶笉闇�瑕佷负瀛橀噺鏁版嵁鍋氳縼绉伙紱</li>
+ *   <li>鐮佺┖闂� 62^6 = 56,800,235,584锛堢害 568 浜匡級锛屼笂涓囨潯璁板綍涓嬮殢鏈哄懡涓巼绾� 1/710 涓囷紝
+ *       閫愪釜閬嶅巻鍦ㄩ檺娴佷笅涓嶅彲琛屻��</li>
+ * </ul>
+ *
+ * <p><b>涓轰粈涔堜笉鐩存帴鐢ㄩ殢鏈虹爜 + 钀藉簱</b>锛氶殢鏈虹爜瑕佹眰"鐢熸垚涓�娆°�佷箣鍚庡澶勮搴�"锛岃�岀幇鍦ㄦ湁 14 澶�
+ * 鏄粠 id 鐜扮畻鐭爜鐨勶紙濡� {@code RyTask} 琛ュ伩鍙戦�併�亄@code RedisMqReceiver} 鍚勭被鍙戦�侀�氶亾锛夛紝
+ * 鏀规垚璇诲簱瑕佸姩鏁版嵁娴侊紝涓斾换浣曚竴澶勬紡鏀归兘浼氬鑷�"閾炬帴鎵撲笉寮�"銆傚弻灏勬柟妗堟槸绾嚱鏁帮紝澶╃劧涓嶄細鏈夎繖涓棶棰樸��
+ *
+ * <p><b>涓轰粈涔堜笉鐢� AES</b>锛�6 浣� base62 鐨勭┖闂存槸 238328虏锛屾濂藉彲浠ュ仛鎴愬钩琛� Feistel锛�
+ * 鑰屼笉鍍� AES 閭f牱瑕佹眰 2 鐨勫箓娆″垎缁勩�傜敤 Feistel 鍙互鍋氬埌"杈撳嚭闀垮害瀹屽叏鍙帶"銆�
+ *
+ * <p><b>瀵嗛挜</b>锛氱敱 {@code shortcode.secret} 閰嶇疆娉ㄥ叆锛堣 {@link ShortCodeConfig}锛夈��
+ * <b>涓婄嚎鍚庝笉瑕佸啀鏀�</b> 鈥斺�� 鏀逛簡涔嬪悗宸插彂鍑虹殑 6 浣嶇煭閾炬棤娉曞弽瑙o紝鍙兘闈� {@code radix} 鍒楀厹搴曘��
+ *
+ * @author smartor
+ */
+public final class ShortCode {
+
+    /** 鐭爜闀垮害锛�6 浣嶃�� */
+    public static final int LENGTH = 6;
+
+    /** base62 瀛楃琛紝椤哄簭鍗崇紪鐮侀『搴忋�� */
+    private static final char[] ALPHABET =
+            "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
+
+    /** 杩涘埗锛�62銆� */
+    private static final int BASE = ALPHABET.length;
+
+    /** 鍗婂尯澶у皬锛�62^3 = 238328銆� */
+    private static final int HALF = 238328;
+
+    /** 鏁翠釜鐮佺┖闂达細62^6 = 238328 * 238328 = 56800235584銆� */
+    private static final long DOMAIN = 56800235584L;
+
+    /** Feistel 杞暟銆�6 杞厤鍚� HMAC 杞嚱鏁帮紝瀵规湰鍦烘櫙锛堝彧鑳藉悜鏈嶅姟绔�"鐚滀竴娆� yes/no"锛夎冻澶熴�� */
+    private static final int ROUNDS = 6;
+
+    private static final String ALGORITHM = "HmacSHA256";
+
+    /** 缂虹渷瀵嗛挜锛氫笌 application.yml 閲岀殑 {@code shortcode.secret} 淇濇寔涓�鑷淬�� */
+    private static final String DEFAULT_SECRET = "smartor-outpath-shortcode-2026";
+
+    private static volatile byte[] secret = DEFAULT_SECRET.getBytes(StandardCharsets.UTF_8);
+
+    private ShortCode() {
+    }
+
+    /**
+     * 娉ㄥ叆瀵嗛挜锛堢敱 {@link ShortCodeConfig} 鍦ㄥ惎鍔ㄦ椂璋冪敤锛夈�備紶绌哄�煎垯淇濇寔缂虹渷瀵嗛挜涓嶅彉銆�
+     */
+    public static void setSecret(String value) {
+        if (value != null && value.trim().length() > 0) {
+            secret = value.trim().getBytes(StandardCharsets.UTF_8);
+        }
+    }
+
+    /**
+     * 涓婚敭 鈫� 6 浣嶇煭鐮併��
+     *
+     * @param id {@code service_out_path.id}锛屽彇鍊艰寖鍥� [0, 56800235584)
+     * @return 瀹氶暱 6 浣� base62 瀛楃涓�
+     */
+    public static String encode(long id) {
+        if (id < 0 || id >= DOMAIN) {
+            throw new IllegalArgumentException("涓婚敭瓒呭嚭鐭爜鍙〃绀鸿寖鍥� [0, " + DOMAIN + ")锛�" + id);
+        }
+        long left = id / HALF;
+        long right = id % HALF;
+        Mac mac = newMac();
+        for (int round = 0; round < ROUNDS; round++) {
+            long f = round(mac, round, right);
+            long next = (left + f) % HALF;
+            left = right;
+            right = next;
+        }
+        return toBase62(left * HALF + right);
+    }
+
+    /**
+     * 6 浣嶇煭鐮� 鈫� 涓婚敭銆備笉鏄悎娉曠煭鐮侊紙闀垮害涓嶄负 6銆佸惈闈� base62 瀛楃锛夋椂杩斿洖 {@code null}锛�
+     * 璋冪敤鏂规嵁姝ゅ洖钀藉埌鍘嗗彶鐨� {@code radix} 鏌ユ硶銆�
+     *
+     * @param code 鐭爜
+     * @return 涓婚敭锛涗笉鍚堟硶鏃朵负 {@code null}
+     */
+    public static Long decode(String code) {
+        if (code == null || code.length() != LENGTH) {
+            return null;
+        }
+        long value = 0L;
+        for (int i = 0; i < LENGTH; i++) {
+            int index = indexOf(code.charAt(i));
+            if (index < 0) {
+                return null;
+            }
+            value = value * BASE + index;
+        }
+        // Feistel 鍙嶆紨锛氭鍚戞槸 L'=R, R'=(L+F(R)) mod n锛涢�嗗悜鍙� R=L'銆丩=(R'-F(L')) mod n
+        long left = value / HALF;
+        long right = value % HALF;
+        Mac mac = newMac();
+        for (int round = ROUNDS - 1; round >= 0; round--) {
+            long f = round(mac, round, left);
+            long previousLeft = (right - f + HALF) % HALF;
+            right = left;
+            left = previousLeft;
+        }
+        return left * HALF + right;
+    }
+
+    private static Mac newMac() {
+        try {
+            Mac mac = Mac.getInstance(ALGORITHM);
+            mac.init(new SecretKeySpec(secret, ALGORITHM));
+            return mac;
+        } catch (Exception e) {
+            // HmacSHA256 鏄� JDK 鏍囧噯绠楁硶锛岃蛋鍒拌繖閲岃鏄庤繍琛岀幆澧冨紓甯革紝鐩存帴鏆撮湶鍑烘潵
+            throw new IllegalStateException("鐭爜璁$畻澶辫触锛氭棤娉曞垵濮嬪寲 " + ALGORITHM, e);
+        }
+    }
+
+    /** 杞嚱鏁� F(round, block) = HmacSHA256(secret, round||block) 鍙栧墠 4 瀛楄妭锛屾ā HALF銆� */
+    private static long round(Mac mac, int round, long block) {
+        byte[] data = new byte[9];
+        data[0] = (byte) round;
+        data[1] = (byte) (block >>> 56);
+        data[2] = (byte) (block >>> 48);
+        data[3] = (byte) (block >>> 40);
+        data[4] = (byte) (block >>> 32);
+        data[5] = (byte) (block >>> 24);
+        data[6] = (byte) (block >>> 16);
+        data[7] = (byte) (block >>> 8);
+        data[8] = (byte) block;
+        byte[] digest = mac.doFinal(data);
+        int value = ((digest[0] & 0xFF) << 24)
+                | ((digest[1] & 0xFF) << 16)
+                | ((digest[2] & 0xFF) << 8)
+                | (digest[3] & 0xFF);
+        return (value & 0x7FFFFFFFL) % HALF;
+    }
+
+    /** 瀹氶暱 6 浣� base62锛堝乏渚цˉ '0'锛夈�� */
+    private static String toBase62(long value) {
+        char[] out = new char[LENGTH];
+        for (int i = LENGTH - 1; i >= 0; i--) {
+            out[i] = ALPHABET[(int) (value % BASE)];
+            value /= BASE;
+        }
+        return new String(out);
+    }
+
+    private static int indexOf(char c) {
+        for (int i = 0; i < ALPHABET.length; i++) {
+            if (ALPHABET[i] == c) {
+                return i;
+            }
+        }
+        return -1;
+    }
+}

--
Gitblit v1.9.3