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