Просмотр исходного кода

feat(utils): UtilToken 加 namespace API + DynamicDDService 切到新 API

UtilToken 新增 put/get/getWithRefresh 的 namespace 重载,namespace + key 拼接为 "{namespace}:{key}",namespace 空白时退化为单参 key 模式,向后兼容原有调用方。多租户场景调用方不再需要手动 String.format 拼 key。

DynamicDDService 删除 TOKEN_KEY_FMT 常量,改用 UtilToken.get(tenantId, "dingtalk:"+appKey) 示范新 API。基座现有 4 处 hardcoded key 调用方(DDImpl/INTP/FXK/TB)保持不动,留待阶段 2 按需迁移。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
malk 1 неделя назад
Родитель
Сommit
b343a3b812

+ 5 - 5
mjava-pro/src/main/java/com/malk/pro/service/dingtalk/DynamicDDService.java

@@ -26,7 +26,6 @@ import org.springframework.stereotype.Service;
 public class DynamicDDService {
 
     private static final String VENDOR = "dingtalk";
-    private static final String TOKEN_KEY_FMT = "%s:" + VENDOR + ":%s";
     // 钉钉 access_token 官方 TTL 7200s;UtilToken.put 内部会再扣 5s 容错
     private static final long DEFAULT_TOKEN_TTL_MS = 7200_000L;
 
@@ -54,17 +53,18 @@ public class DynamicDDService {
      */
     public String getAccessToken() {
         VendorCredential credential = currentCredential();
-        String key = String.format(TOKEN_KEY_FMT, TenantContext.currentTenantId(), credential.getAppKey());
-        String token = UtilToken.get(key);
+        String tenantId = TenantContext.currentTenantId();
+        String key = VENDOR + ":" + credential.getAppKey();
+        String token = UtilToken.get(tenantId, key);
         if (StringUtils.isNotBlank(token)) {
             return token;
         }
         token = ddClient.getAccessToken(credential.getAppKey(), credential.getAppSecret());
         if (StringUtils.isBlank(token)) {
             throw new McException("DD_TOKEN_FAILED",
-                    "tenant=" + TenantContext.currentTenantId() + " 钉钉 token 获取失败");
+                    "tenant=" + tenantId + " 钉钉 token 获取失败");
         }
-        UtilToken.put(key, token, DEFAULT_TOKEN_TTL_MS);
+        UtilToken.put(tenantId, key, token, DEFAULT_TOKEN_TTL_MS);
         return token;
     }
 

+ 65 - 2
mjava/src/main/java/com/malk/utils/UtilToken.java

@@ -2,14 +2,42 @@ package com.malk.utils;
 
 import cn.hutool.cache.CacheUtil;
 import cn.hutool.cache.impl.TimedCache;
+import org.apache.commons.lang3.StringUtils;
 
 /**
- * token过期处理
+ * token 过期处理
+ *
+ * <p>提供两类 API:</p>
+ * <ol>
+ *   <li><b>单参 key</b>({@link #put(String, String, Long)} / {@link #get(String)})— 兼容原有调用方,
+ *       key 自包含命名空间(如 {@code "dingtalk:appKey123"})</li>
+ *   <li><b>namespace + key</b>({@link #put(String, String, String, Long)} / {@link #get(String, String)})—
+ *       推荐多租户 / 多应用场景使用,namespace 与 key 拼接为 {@code "{namespace}:{key}"} 后存入底层缓存。
+ *       namespace 为 null 或空时退化为单参 key 模式。</li>
+ * </ol>
+ *
+ * <p>典型用法(多租户):</p>
+ * <pre>
+ * // mjava-pro 多租户场景
+ * UtilToken.put(tenantId, "dingtalk:" + appKey, token, 7200_000L);
+ * String t = UtilToken.get(tenantId, "dingtalk:" + appKey);
+ *
+ * // 基座单租户场景
+ * UtilToken.put("dingtalk:appKey", token, 7200_000L);
+ * String t = UtilToken.get("dingtalk:appKey");
+ * </pre>
  */
 public abstract class UtilToken {
 
     private static final TimedCache<String, String> TIMED_CACHE = CacheUtil.newTimedCache(0);
 
+    /**
+     * 命名空间分隔符
+     */
+    public static final String NS_SEPARATOR = ":";
+
+    /// ---------- 单参 key API(向后兼容) ----------
+
     public static void put(String key, String value, Long timeout) {
         if (timeout > 5000) timeout -= 5000; // 避免极端情况, 冗余5s容错处理
         /** 设置消逝时间 */
@@ -25,4 +53,39 @@ public abstract class UtilToken {
         // 重新刷新消逝时间
         return TIMED_CACHE.get(key);
     }
-}
+
+    /// ---------- namespace + key API(推荐多租户场景使用) ----------
+
+    /**
+     * 按 namespace 隔离写入;namespace 为空时退化为单参 key 模式
+     *
+     * @param namespace 命名空间(如 tenantId / vendor / corp 标识);null 或空字符串退化
+     * @param key       业务 key(如 {@code "dingtalk:" + appKey})
+     * @param value     token / ticket 等待缓存的字符串值
+     * @param timeout   TTL 毫秒;put 内部会冗余扣 5s
+     */
+    public static void put(String namespace, String key, String value, Long timeout) {
+        put(buildKey(namespace, key), value, timeout);
+    }
+
+    /**
+     * 按 namespace 隔离读取;不刷新消逝时间
+     */
+    public static String get(String namespace, String key) {
+        return get(buildKey(namespace, key));
+    }
+
+    /**
+     * 按 namespace 隔离读取并刷新消逝时间
+     */
+    public static String getWithRefresh(String namespace, String key) {
+        return getWithRefresh(buildKey(namespace, key));
+    }
+
+    /**
+     * 拼接 namespace + key 为底层缓存 key;namespace 空白时直接返回 key
+     */
+    public static String buildKey(String namespace, String key) {
+        return StringUtils.isBlank(namespace) ? key : namespace + NS_SEPARATOR + key;
+    }
+}