ソースを参照

登录后把 StoreUser 直存 Redis,token 即 key,凭 token 取用户信息

存储结构:
- RedisTokenStore 重写:value 不再包 {user, store_id, is_login},顶层直接是用户字段
  (storeUserId/userName/realName/isSuper/isDelete/sort/storeId/createTime/updateTime)
- 不缓存 password;createTime/updateTime 存时间戳,出参由实体 getter 格式化成日期串
- 对外方法收敛为 getUser(token) / save(token,user[,ttl]) / delete / remainingTtlSeconds,TTL 仍默认 7 天
调用链:
- 删除 LoginInfo 类,拦截器改把 StoreUser 放进 request attribute loginUser(另带 storeId)
- 13 个控制器私有 storeId() 统一收敛到 StoreContext.storeId(request)
- StoreUserController.currentUser() 直接读缓存用户,/store/store.user/info 不再回查数据库
- UserController 取操作人 userName 改走 StoreUser;删除无用的 StoreUserService.getById
- renew 改资料后刷新缓存并沿用剩余 TTL,logout 删 key
验证:
- 新增 tools/test_token_user.py(20 项断言):落库结构、TTL、篡改缓存验证不回查库、
  11 个业务接口、renew 刷新、登出后 401 全通过
- 删除已被替代的 tools/test_redis_auth.py
- 长期记忆已记录:登录用户以缓存为准,直接改库不会即时生效
tanlie 9 時間 前
親
コミット
fd50b46e18
24 ファイル変更、387 行追加、335 行削除
  1. 35 0
      tools/patch_login_user.py
  2. 20 0
      tools/sort_imports.py
  3. 0 155
      tools/test_redis_auth.py
  4. 202 0
      tools/test_token_user.py
  5. 0 63
      yoshop-java/src/main/java/cn/yoshop/common/auth/LoginInfo.java
  6. 75 21
      yoshop-java/src/main/java/cn/yoshop/common/auth/RedisTokenStore.java
  7. 8 6
      yoshop-java/src/main/java/cn/yoshop/common/auth/StoreAuthInterceptor.java
  8. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/ClientH5SettingController.java
  9. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/GoodsCommentController.java
  10. 1 1
      yoshop-java/src/main/java/cn/yoshop/store/controller/GoodsController.java
  11. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/MarketCouponController.java
  12. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/MarketRechargePlanController.java
  13. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/OrderDeliveryController.java
  14. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/OrderEventController.java
  15. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/OrderExportController.java
  16. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/SettingDeliveryController.java
  17. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/SettingExpressController.java
  18. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/SettingPaymentController.java
  19. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/SettingPaymentTemplateController.java
  20. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/SettingPrinterController.java
  21. 8 11
      yoshop-java/src/main/java/cn/yoshop/store/controller/StoreUserController.java
  22. 7 7
      yoshop-java/src/main/java/cn/yoshop/store/controller/UserController.java
  23. 2 3
      yoshop-java/src/main/java/cn/yoshop/store/controller/UserGradeController.java
  24. 5 32
      yoshop-java/src/main/java/cn/yoshop/store/service/StoreUserService.java

+ 35 - 0
tools/patch_login_user.py

@@ -0,0 +1,35 @@
+import re
+from pathlib import Path
+
+ROOT = Path(r"F:/shop/java-rewrite/yoshop-java/src/main/java/cn/yoshop/store/controller")
+
+files = [
+    "ClientH5SettingController.java", "GoodsCommentController.java",
+    "MarketCouponController.java", "MarketRechargePlanController.java",
+    "OrderDeliveryController.java", "OrderEventController.java",
+    "OrderExportController.java", "SettingDeliveryController.java",
+    "SettingExpressController.java", "SettingPaymentController.java",
+    "SettingPaymentTemplateController.java", "SettingPrinterController.java",
+    "UserGradeController.java",
+]
+
+body_re = re.compile(
+    r"LoginInfo info = \(LoginInfo\) request\.getAttribute\(\"loginInfo\"\);\s*\n\s*return info == null \? 0 : \(int\) info\.getStoreId\(\);"
+)
+
+for name in files:
+    p = ROOT / name
+    text = p.read_text(encoding="utf-8")
+    new, n_body = body_re.subn("return StoreContext.storeId(request);", text)
+    if n_body != 1:
+        print(f"WARN {name}: body replaced {n_body} times")
+    new, n_imp = re.subn(
+        r"import cn\.yoshop\.common\.auth\.LoginInfo;",
+        "import cn.yoshop.common.web.StoreContext;",
+        new,
+    )
+    if n_imp != 1:
+        print(f"WARN {name}: import replaced {n_imp} times")
+    if new != text:
+        p.write_text(new, encoding="utf-8")
+        print(f"OK {name}")

+ 20 - 0
tools/sort_imports.py

@@ -0,0 +1,20 @@
+import re
+from pathlib import Path
+
+ROOT = Path(r"F:/shop/java-rewrite/yoshop-java/src/main/java/cn/yoshop/store/controller")
+
+for p in sorted(ROOT.glob("*.java")):
+    lines = p.read_text(encoding="utf-8").split("\n")
+    idx = [i for i, l in enumerate(lines) if l.startswith("import cn.yoshop.")]
+    if not idx:
+        continue
+    start, end = idx[0], idx[-1]
+    block = lines[start:end + 1]
+    if any(not b.startswith("import cn.yoshop.") for b in block):
+        print(f"SKIP {p.name}: 非连续块")
+        continue
+    ordered = sorted(block)
+    if ordered != block:
+        lines[start:end + 1] = ordered
+        p.write_text("\n".join(lines), encoding="utf-8")
+        print(f"OK {p.name}")

+ 0 - 155
tools/test_redis_auth.py

@@ -1,155 +0,0 @@
-# -*- coding: utf-8 -*-
-"""验证 /store/passport/login 的 Redis 登录态:登录写入 → 鉴权通过 → 登出删除 → 鉴权失效。
-
-前置:Java 服务运行在 18080(yoshop.auth.mode=redis)。
-"""
-import json
-import socket
-import sys
-
-import requests
-
-BASE = "http://127.0.0.1:18080"
-REDIS = ("192.168.1.177", 6379, "@@Qinys12346..")
-PREFIX = "yoshop:store:token:"
-
-S = requests.Session()
-S.trust_env = False
-
-passed = failed = 0
-
-
-def check(name, cond, extra=""):
-    global passed, failed
-    if cond:
-        passed += 1
-        print("  [ok] %s" % name)
-    else:
-        failed += 1
-        print("  [FAIL] %s %s" % (name, extra))
-
-
-# ---------------------------------------------------------------- 最小 Redis 客户端
-def resp(sock, *args):
-    data = b"*%d\r\n" % len(args)
-    for a in args:
-        b = a.encode() if isinstance(a, str) else a
-        data += b"$%d\r\n%s\r\n" % (len(b), b)
-    sock.sendall(data)
-    return read(sock)
-
-
-def read(sock):
-    line = readline(sock)
-    t, rest = line[:1], line[1:]
-    if t in (b"+", b"-"):
-        return rest.decode()
-    if t == b":":
-        return int(rest)
-    if t == b"$":
-        n = int(rest)
-        if n == -1:
-            return None
-        buf = b""
-        while len(buf) < n + 2:
-            buf += sock.recv(n + 2 - len(buf))
-        return buf[:n].decode()
-    if t == b"*":
-        n = int(rest)
-        return None if n == -1 else [read(sock) for _ in range(n)]
-    return line.decode(errors="replace")
-
-
-def readline(sock):
-    buf = b""
-    while not buf.endswith(b"\r\n"):
-        c = sock.recv(1)
-        if not c:
-            return buf
-        buf += c
-    return buf[:-2]
-
-
-class Redis:
-    def __enter__(self):
-        self.s = socket.create_connection((REDIS[0], REDIS[1]), timeout=5)
-        resp(self.s, "AUTH", REDIS[2])
-        return self
-
-    def __exit__(self, *a):
-        self.s.close()
-
-    def __call__(self, *args):
-        return resp(self.s, *args)
-
-
-def login(username, password):
-    return S.post(BASE + "/store/passport/login",
-                  headers={"Content-Type": "application/json"},
-                  data=json.dumps({"username": username, "password": password}),
-                  timeout=30).json()
-
-
-def api(token):
-    headers = {"Access-Token": token} if token else {}
-    return S.get(BASE + "/store/goods/list", headers=headers, timeout=30).json()
-
-
-def main():
-    with Redis() as r:
-        print("== 1. 登录 ==")
-        res = login("redistest_admin", "123456")
-        check("登录成功", res.get("status") == 200 and res.get("message") == "登录成功", json.dumps(res, ensure_ascii=False))
-        token = (res.get("data") or {}).get("token")
-        check("返回 token", isinstance(token, str) and len(token) == 32, str(token))
-        if not token:
-            return
-
-        key = PREFIX + token
-        print("== 2. Redis 落库 ==")
-        raw = r("GET", key)
-        check("key 存在", raw is not None, "key=%s" % key)
-        ttl = r("TTL", key)
-        check("TTL 为 7 天(604800,允许 5 秒误差)", isinstance(ttl, int) and 604795 <= ttl <= 604800, "ttl=%s" % ttl)
-        try:
-            val = json.loads(raw) if raw else {}
-            # 现版本用 StringRedisTemplate 写入,落库即纯 JSON(json.loads 直接得到 dict)。
-            # 兼容早期用 RedisTemplate(Jackson) 写入的双层编码(存成 "{\"user\":...}"),
-            # 若解出来还是字符串就再解一层。
-            if isinstance(val, str):
-                val = json.loads(val)
-        except Exception:
-            val = {}
-        check("value.user 有 store_user_id", isinstance(val.get("user"), dict) and "store_user_id" in val["user"],
-              json.dumps(val, ensure_ascii=False)[:160])
-        check("value.store_id 正确", val.get("store_id") == 10001, "store_id=%s" % val.get("store_id"))
-        check("value.is_login 为 true", val.get("is_login") is True, "is_login=%r" % val.get("is_login"))
-
-        print("== 3. 带 token 访问业务接口 ==")
-        ok = api(token)
-        check("/store/goods/list 返回 200", ok.get("status") == 200, json.dumps(ok, ensure_ascii=False)[:160])
-
-        print("== 4. 无 token 应 401 ==")
-        no = api(None)
-        check("无 token 返回 401", no.get("status") == 401 and no.get("message") == "请先登录后再访问",
-              json.dumps(no, ensure_ascii=False)[:160])
-
-        print("== 5. 登出 ==")
-        out = S.post(BASE + "/store/passport/logout", headers={"Access-Token": token}, timeout=30).json()
-        check("登出返回 200/操作成功", out.get("status") == 200 and out.get("message") == "操作成功",
-              json.dumps(out, ensure_ascii=False)[:160])
-
-        print("== 6. Redis 已清除 ==")
-        check("key 已删除", r("GET", key) is None, "key=%s" % key)
-        check("TTL 为 -2", r("TTL", key) == -2, "ttl=%s" % r("TTL", key))
-
-        print("== 7. 登出后 token 失效 ==")
-        after = api(token)
-        check("旧 token 返回 401", after.get("status") == 401, json.dumps(after, ensure_ascii=False)[:160])
-
-    print("\n通过 %d / 失败 %d" % (passed, failed))
-    return 1 if failed else 0
-
-
-if __name__ == "__main__":
-    sys.exit(main())

+ 202 - 0
tools/test_token_user.py

@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+"""验证「登录后把 StoreUser 写进 Redis,以 token 为 key,凭 token 取用户信息」。
+
+覆盖点:
+  1. 登录落库:key = yoshop:store:token:{token},value 就是用户 JSON(顶层字段,无 user/store_id/is_login 包裹,无 password)
+  2. TTL = 7 天
+  3. /store/store.user/info 的用户信息来自缓存(篡改缓存里的 userName,接口跟着变,说明没回查数据库)
+  4. 带 token 访问业务接口正常
+  5. /store/store.user/renew 改资料后缓存被刷新,且不延长登录时长
+  6. 登出后 key 删除,旧 token 再访问 401
+
+前置:Java 服务运行在 18080(yoshop.auth.mode=redis),库内有临时账号 camel_test_admin / 123456。
+"""
+import json
+import socket
+import sys
+
+import requests
+
+BASE = "http://127.0.0.1:18080"
+REDIS = ("192.168.1.177", 6379, "@@Qinys12346..")
+PREFIX = "yoshop:store:token:"
+USER_NAME = "camel_test_admin"
+PASSWORD = "123456"
+
+S = requests.Session()
+S.trust_env = False
+
+passed = failed = 0
+
+
+def check(name, cond, extra=""):
+    global passed, failed
+    if cond:
+        passed += 1
+        print("  [ok] %s" % name)
+    else:
+        failed += 1
+        print("  [FAIL] %s %s" % (name, extra))
+
+
+# ---------------------------------------------------------------- 最小 Redis 客户端
+def resp(sock, *args):
+    data = b"*%d\r\n" % len(args)
+    for a in args:
+        if isinstance(a, bytes):
+            b = a
+        elif isinstance(a, str):
+            b = a.encode()
+        else:
+            b = str(a).encode()
+        data += b"$%d\r\n%s\r\n" % (len(b), b)
+    sock.sendall(data)
+    return read(sock)
+
+
+def read(sock):
+    line = readline(sock)
+    t, rest = line[:1], line[1:]
+    if t in (b"+", b"-"):
+        return rest.decode()
+    if t == b":":
+        return int(rest)
+    if t == b"$":
+        n = int(rest)
+        if n == -1:
+            return None
+        buf = b""
+        while len(buf) < n + 2:
+            buf += sock.recv(n + 2 - len(buf))
+        return buf[:n].decode()
+    if t == b"*":
+        n = int(rest)
+        return None if n == -1 else [read(sock) for _ in range(n)]
+    return line.decode(errors="replace")
+
+
+def readline(sock):
+    buf = b""
+    while not buf.endswith(b"\r\n"):
+        c = sock.recv(1)
+        if not c:
+            return buf
+        buf += c
+    return buf[:-2]
+
+
+class Redis:
+    def __enter__(self):
+        self.s = socket.create_connection((REDIS[0], REDIS[1]), timeout=5)
+        resp(self.s, "AUTH", REDIS[2])
+        return self
+
+    def __exit__(self, *a):
+        self.s.close()
+
+    def __call__(self, *args):
+        return resp(self.s, *args)
+
+
+def login():
+    return S.post(BASE + "/store/passport/login",
+                  headers={"Content-Type": "application/json"},
+                  data=json.dumps({"username": USER_NAME, "password": PASSWORD}),
+                  timeout=30).json()
+
+
+def get(path, token):
+    headers = {"Access-Token": token} if token else {}
+    return S.get(BASE + path, headers=headers, timeout=30).json()
+
+
+def post(path, token, body=None):
+    headers = {"Content-Type": "application/json"}
+    if token:
+        headers["Access-Token"] = token
+    return S.post(BASE + path, headers=headers, json=body or {}, timeout=30).json()
+
+
+def main():
+    with Redis() as r:
+        print("== 1. 登录 ==")
+        res = login()
+        check("登录成功", res.get("status") == 200, json.dumps(res, ensure_ascii=False))
+        token = (res.get("data") or {}).get("token")
+        check("返回 token", isinstance(token, str) and len(token) == 32, str(token))
+        if not token:
+            return 1
+
+        key = PREFIX + token
+        print("== 2. 登录态落 Redis:token 即 key,value 即用户 ==")
+        raw = r("GET", key)
+        check("key = token", raw is not None, "key=%s" % key)
+        ttl = r("TTL", key)
+        check("TTL = 604800(允许 5 秒误差)", isinstance(ttl, int) and 604795 <= ttl <= 604800, "ttl=%s" % ttl)
+        try:
+            val = json.loads(raw) if raw else {}
+        except Exception:
+            val = {}
+        if isinstance(val, str):
+            val = json.loads(val)
+        check("value 顶层就是用户字段", val.get("storeUserId") == 10992 and val.get("userName") == USER_NAME,
+              json.dumps(val, ensure_ascii=False)[:200])
+        check("不再有 user / store_id / is_login 包裹",
+              not ({"user", "store_id", "is_login"} & set(val.keys())), sorted(val.keys()))
+        check("关键字段齐全",
+              all(k in val for k in ("storeUserId", "userName", "realName", "isSuper", "storeId", "createTime")),
+              sorted(val.keys()))
+        check("password 不落缓存", "password" not in val, sorted(val.keys()))
+
+        print("== 3. 用户信息取自缓存(不回查数据库)==")
+        info = get("/store/store.user/info", token)
+        user_info = (info.get("data") or {}).get("userInfo") or {}
+        check("info 返回 200", info.get("status") == 200, json.dumps(info, ensure_ascii=False)[:160])
+        check("userInfo 为当前登录用户", user_info.get("storeUserId") == 10992 and user_info.get("userName") == USER_NAME,
+              json.dumps(user_info, ensure_ascii=False)[:200])
+        check("roles.isSuper = 0", ((info.get("data") or {}).get("roles") or {}).get("isSuper") == 0,
+              json.dumps(info.get("data"), ensure_ascii=False)[:160])
+
+        sentinel = dict(val, userName="from_redis_only")
+        r("SET", key, json.dumps(sentinel, ensure_ascii=False))
+        hit = ((get("/store/store.user/info", token).get("data") or {}).get("userInfo") or {})
+        check("篡改缓存后接口跟着变(证明按 token 读缓存)", hit.get("userName") == "from_redis_only",
+              json.dumps(hit, ensure_ascii=False)[:160])
+        # 还原缓存
+        r("SET", key, json.dumps(val, ensure_ascii=False))
+        r("EXPIRE", key, 604800)
+
+        print("== 4. 带 token 访问业务接口 ==")
+        goods = get("/store/goods/list", token)
+        check("/store/goods/list 返回 200", goods.get("status") == 200, json.dumps(goods, ensure_ascii=False)[:160])
+
+        print("== 5. 改资料后缓存同步刷新,且不延长登录时长 ==")
+        before = r("TTL", key)
+        renewed = post("/store/store.user/renew", token, {"form": {"realName": "驼峰测试改"}})
+        check("renew 返回 200", renewed.get("status") == 200, json.dumps(renewed, ensure_ascii=False)[:160])
+        fresh = json.loads(r("GET", key) or "{}")
+        if isinstance(fresh, str):
+            fresh = json.loads(fresh)
+        check("缓存 realName 已更新", fresh.get("realName") == "驼峰测试改",
+              json.dumps(fresh, ensure_ascii=False)[:200])
+        after = r("TTL", key)
+        check("TTL 未被延长(沿用剩余有效期)", isinstance(after, int) and after <= before and after > before - 20,
+              "before=%s after=%s" % (before, after))
+        # 改回去,保持库内数据原样
+        post("/store/store.user/renew", token, {"form": {"realName": "驼峰测试"}})
+
+        print("== 6. 登出 ==")
+        out = post("/store/passport/logout", token)
+        check("登出返回 200", out.get("status") == 200, json.dumps(out, ensure_ascii=False)[:160])
+        check("key 已删除", r("GET", key) is None, "key=%s" % key)
+        check("TTL 为 -2", r("TTL", key) == -2, "ttl=%s" % r("TTL", key))
+        after_logout = get("/store/goods/list", token)
+        check("旧 token 再访问返回 401", after_logout.get("status") == 401,
+              json.dumps(after_logout, ensure_ascii=False)[:160])
+
+    print("\n通过 %d / 失败 %d" % (passed, failed))
+    return 1 if failed else 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())

+ 0 - 63
yoshop-java/src/main/java/cn/yoshop/common/auth/LoginInfo.java

@@ -1,63 +0,0 @@
-package cn.yoshop.common.auth;
-
-import java.util.Map;
-
-/**
- * 商家登录信息,对齐 PHP 端 app/common/service/store/User::login() 写入缓存的结构:
- * ['user' => [...], 'store_id' => int, 'is_login' => true]
- */
-public class LoginInfo {
-
-    /** 商家用户完整信息(store_user_id、user_name 等) */
-    private Map<String, Object> user;
-    /** 所属商城 ID */
-    private long storeId;
-    /** 是否已登录(PHP 侧恒为 true,防伪标记) */
-    private boolean login;
-
-    @SuppressWarnings("unchecked")
-    public static LoginInfo fromCacheValue(Map<String, Object> cacheValue) {
-        LoginInfo info = new LoginInfo();
-        Object user = cacheValue.get("user");
-        if (user instanceof Map) {
-            info.user = (Map<String, Object>) user;
-        }
-        Object storeId = cacheValue.get("store_id");
-        info.storeId = storeId instanceof Number ? ((Number) storeId).longValue() : 0L;
-        Object isLogin = cacheValue.get("is_login");
-        info.login = Boolean.TRUE.equals(isLogin) || (isLogin instanceof Number && ((Number) isLogin).longValue() == 1);
-        return info;
-    }
-
-    public long getStoreUserId() {
-        if (user == null) {
-            return 0L;
-        }
-        Object id = user.get("store_user_id");
-        return id instanceof Number ? ((Number) id).longValue() : 0L;
-    }
-
-    public Map<String, Object> getUser() {
-        return user;
-    }
-
-    public void setUser(Map<String, Object> user) {
-        this.user = user;
-    }
-
-    public long getStoreId() {
-        return storeId;
-    }
-
-    public void setStoreId(long storeId) {
-        this.storeId = storeId;
-    }
-
-    public boolean isLogin() {
-        return login;
-    }
-
-    public void setLogin(boolean login) {
-        this.login = login;
-    }
-}

+ 75 - 21
yoshop-java/src/main/java/cn/yoshop/common/auth/RedisTokenStore.java

@@ -1,5 +1,6 @@
 package cn.yoshop.common.auth;
 
+import cn.yoshop.common.entity.StoreUser;
 import cn.yoshop.utils.RedisUtils;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -14,18 +15,22 @@ import java.util.Map;
 /**
  * 商家后台登录态存储(Redis)。
  *
- * <p>不再兼容 PHP 的 File 缓存格式,key 与 value 均由 Java 端自定义:
+ * <p>登录成功后把 {@link StoreUser} 直接写进 Redis,<b>token 就是 key</b>:
  * <ul>
- *   <li>key = {@code ${yoshop.auth.token-prefix} + token}</li>
- *   <li>value = JSON {@code {"user":{...},"store_id":10001,"is_login":true}}</li>
- *   <li>有效期默认 7 天(对齐原 PHP 端 {@code Cache::set($token, $value, 86400 * 7)})</li>
+ *   <li>key = {@code ${yoshop.auth.token-prefix} + token},默认 {@code yoshop:store:token:{token}}</li>
+ *   <li>value = 商家用户 JSON(camelCase,字段与 {@link StoreUser} 一致,不含 password)</li>
+ *   <li>有效期默认 7 天({@code ${yoshop.auth.token-ttl}})</li>
  * </ul>
  *
+ * <p>因此拿到 token 就能用 {@link #getUser(String)} 取回完整的用户信息,
+ * 后续接口无需再回查 {@code yoshop_store_user} 表。
+ *
  * <p>读写走 {@link RedisUtils} 的 string 专用方法(底层 {@code StringRedisTemplate}),
- * 落库就是纯 JSON 文本,不会被 Jackson 再编码成带引号的字符串,便于 redis-cli 直接查看。
+ * 落库就是纯 JSON 文本,不会被 Jackson 再编码成带引号的字符串,redis-cli 可直接查看。
  *
- * <p>字段名沿用 PHP 的 snake_case({@code store_id} / {@code is_login}),
- * 这样 {@link LoginInfo#fromCacheValue(Map)} 可直接复用,无需再写一套解析。
+ * <p>刻意不缓存 password:登录校验始终以数据库为准,缓存里留口令只会放大泄露面。
+ * create_time / update_time 在缓存中保存为时间戳(与库内一致),
+ * 输出给前端时再由 {@link StoreUser} 的 getter 格式化成日期字符串。
  */
 @Component
 public class RedisTokenStore {
@@ -34,7 +39,7 @@ public class RedisTokenStore {
 
     private final RedisUtils redis;
 
-    /** 登录态 JSON 的读写用自己的 mapper,避免受全局 SNAKE_CASE 配置影响 */
+    /** 登录态 JSON 的读写用自己的 mapper,避免受全局 Jackson 配置影响 */
     private final ObjectMapper objectMapper = new ObjectMapper();
 
     @Value("${yoshop.auth.token-prefix:yoshop:store:token:}")
@@ -49,15 +54,16 @@ public class RedisTokenStore {
     }
 
     /**
-     * 读取登录态。token 为空、键不存在或内容损坏时返回 null(按未登录处理)。
-     * Redis 不可用同样返回 null,避免异常把整个后台打挂。
+     * 按 token 取当前登录用户。
+     *
+     * <p>token 为空、键不存在、内容损坏或 Redis 不可用时一律返回 null(按未登录处理),
+     * 避免异常把整个后台打挂。
      */
-    public LoginInfo getLoginInfo(String token) {
+    public StoreUser getUser(String token) {
         if (token == null || token.isBlank()) {
             return null;
         }
         try {
-            // 用 string 专用读取,避免 Jackson 序列化把 JSON 文本再包一层引号
             String json = redis.getString(key(token));
             if (json == null) {
                 return null;
@@ -65,7 +71,9 @@ public class RedisTokenStore {
             Map<String, Object> value = objectMapper.readValue(
                     json, new TypeReference<LinkedHashMap<String, Object>>() {
                     });
-            return LoginInfo.fromCacheValue(value);
+            StoreUser user = fromCacheMap(value);
+            // 没有主键视为脏数据,等同未登录
+            return user.getStoreUserId() == null ? null : user;
         } catch (Exception e) {
             log.warn("读取登录态失败 token={} : {}", token, e.getMessage());
             return null;
@@ -73,8 +81,8 @@ public class RedisTokenStore {
     }
 
     /** 写入登录态(有效期取配置 yoshop.auth.token-ttl,默认 7 天) */
-    public void save(String token, LoginInfo info) {
-        save(token, info, defaultTtlSeconds);
+    public void save(String token, StoreUser user) {
+        save(token, user, defaultTtlSeconds);
     }
 
     /**
@@ -82,14 +90,13 @@ public class RedisTokenStore {
      *
      * @param ttlSeconds 有效期秒数,<=0 表示不设过期
      */
-    public void save(String token, LoginInfo info, long ttlSeconds) {
-        Map<String, Object> value = new LinkedHashMap<>();
-        value.put("user", info.getUser());
-        value.put("store_id", info.getStoreId());
-        value.put("is_login", info.isLogin());
+    public void save(String token, StoreUser user, long ttlSeconds) {
+        if (token == null || token.isBlank() || user == null) {
+            return;
+        }
         String json;
         try {
-            json = objectMapper.writeValueAsString(value);
+            json = objectMapper.writeValueAsString(toCacheMap(user));
         } catch (Exception e) {
             throw new IllegalStateException("登录态序列化失败: " + e.getMessage(), e);
         }
@@ -131,4 +138,51 @@ public class RedisTokenStore {
     private String key(String token) {
         return prefix + token;
     }
+
+    /** 落库的字段集合:除 password 外的全部用户字段 */
+    private Map<String, Object> toCacheMap(StoreUser user) {
+        Map<String, Object> m = new LinkedHashMap<>();
+        m.put("storeUserId", user.getStoreUserId());
+        m.put("userName", user.getUserName());
+        m.put("realName", user.getRealName());
+        m.put("isSuper", user.getIsSuper());
+        m.put("isDelete", user.getIsDelete());
+        m.put("sort", user.getSort());
+        m.put("storeId", user.getStoreId());
+        m.put("createTime", user.getCreateTime());
+        m.put("updateTime", user.getUpdateTime());
+        return m;
+    }
+
+    private StoreUser fromCacheMap(Map<String, Object> map) {
+        StoreUser user = new StoreUser();
+        user.setStoreUserId(toInt(map.get("storeUserId")));
+        user.setUserName(toStr(map.get("userName")));
+        user.setRealName(toStr(map.get("realName")));
+        user.setIsSuper(toInt(map.get("isSuper")));
+        user.setIsDelete(toInt(map.get("isDelete")));
+        user.setSort(toInt(map.get("sort")));
+        user.setStoreId(toInt(map.get("storeId")));
+        user.setCreateTime(toInt(map.get("createTime")));
+        user.setUpdateTime(toInt(map.get("updateTime")));
+        return user;
+    }
+
+    private static Integer toInt(Object value) {
+        if (value instanceof Number n) {
+            return n.intValue();
+        }
+        if (value instanceof String s && !s.isBlank()) {
+            try {
+                return Integer.valueOf(s.trim());
+            } catch (NumberFormatException ignored) {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    private static String toStr(Object value) {
+        return value == null ? null : String.valueOf(value);
+    }
 }

+ 8 - 6
yoshop-java/src/main/java/cn/yoshop/common/auth/StoreAuthInterceptor.java

@@ -1,6 +1,8 @@
 package cn.yoshop.common.auth;
 
 import cn.yoshop.common.api.Status;
+import cn.yoshop.common.entity.StoreUser;
+import cn.yoshop.common.web.StoreContext;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
@@ -21,8 +23,7 @@ import java.util.Set;
  *   <li>token 取自请求头 Access-Token(PHP 在 is_debug 时还接受请求参数,生产关闭故不复现)</li>
  *   <li>白名单 routeUri:passport/login、passport/logout、map/transfer</li>
  *   <li>未登录响应:{"status":401,"message":"请先登录后再访问","data":{"isPrompt":true}},HTTP 恒 200</li>
- *   <li>登录信息放入 request attribute "loginInfo",供控制器使用</li>
- *   <li>登录态存 Redis(见 {@link RedisTokenStore}),不再读取 PHP 的 File 缓存</li>
+ *   <li>登录用户直接按 token 从 Redis 取(见 {@link RedisTokenStore}),放入 request attribute "loginUser"</li>
  * </ul>
  *
  * <p>注意:PHP 端 initialize() 还有 checkPrivilege()(按角色校验 API 权限,查 store_api 表),
@@ -67,14 +68,15 @@ public class StoreAuthInterceptor implements HandlerInterceptor {
         }
 
         String token = request.getHeader("Access-Token");
-        LoginInfo loginInfo = tokenStore.getLoginInfo(token);
-        if (loginInfo == null || !loginInfo.isLogin()) {
+        StoreUser loginUser = tokenStore.getUser(token);
+        if (loginUser == null) {
             writeUnauthorized(response);
             return false;
         }
 
-        request.setAttribute("loginInfo", loginInfo);
-        request.setAttribute("storeId", loginInfo.getStoreId() > 0 ? loginInfo.getStoreId() : 10001L);
+        request.setAttribute("loginUser", loginUser);
+        Integer storeId = loginUser.getStoreId();
+        request.setAttribute("storeId", storeId != null && storeId > 0 ? storeId : StoreContext.DEFAULT_STORE_ID);
         return true;
     }
 

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/ClientH5SettingController.java

@@ -1,8 +1,8 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.ClientSettingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -59,7 +59,6 @@ public class ClientH5SettingController {
     }
 
     static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/GoodsCommentController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.Comment;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.CommentService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -66,7 +66,6 @@ public class GoodsCommentController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 1 - 1
yoshop-java/src/main/java/cn/yoshop/store/controller/GoodsController.java

@@ -2,8 +2,8 @@ package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
 import cn.yoshop.common.api.Views;
-import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.entity.Goods;
+import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
 import cn.yoshop.store.service.GoodsWriteService;
 import cn.yoshop.store.service.StoreGoodsService;

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/MarketCouponController.java

@@ -1,11 +1,11 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.Coupon;
 import cn.yoshop.common.entity.UserCoupon;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.MarketingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -75,7 +75,6 @@ public class MarketCouponController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/MarketRechargePlanController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.RechargePlan;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.MarketingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -58,7 +58,6 @@ public class MarketRechargePlanController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/OrderDeliveryController.java

@@ -1,8 +1,8 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.OrderDeliveryService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -65,7 +65,6 @@ public class OrderDeliveryController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/OrderEventController.java

@@ -1,8 +1,8 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.OrderEventService;
 import cn.yoshop.store.service.PrinterService;
 import jakarta.servlet.http.HttpServletRequest;
@@ -108,7 +108,6 @@ public class OrderEventController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/OrderExportController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.OrderExport;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.OrderExportService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -72,7 +72,6 @@ public class OrderExportController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/SettingDeliveryController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.Delivery;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.SettingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -74,7 +74,6 @@ public class SettingDeliveryController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/SettingExpressController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.Express;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.SettingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -64,7 +64,6 @@ public class SettingExpressController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/SettingPaymentController.java

@@ -1,8 +1,8 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.PaymentSettingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -35,7 +35,6 @@ public class SettingPaymentController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/SettingPaymentTemplateController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.PaymentTemplate;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.PaymentSettingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -84,7 +84,6 @@ public class SettingPaymentTemplateController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/SettingPrinterController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.Printer;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.SettingService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -64,7 +64,6 @@ public class SettingPrinterController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 8 - 11
yoshop-java/src/main/java/cn/yoshop/store/controller/StoreUserController.java

@@ -1,7 +1,6 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.StoreUser;
 import cn.yoshop.common.exception.BizException;
 import cn.yoshop.common.page.PageVO;
@@ -93,18 +92,16 @@ public class StoreUserController {
         return id != null ? id : Params.intOrNull(params, "storeUserId");
     }
 
-    /** auth.mode=off 时拦截器不注入登录信息,双跑期默认取超管,便于前端直接查看 */
+    /** 拦截器已按 token 从 Redis 取出登录用户;auth.mode=off 时未注入,兜底取超管便于前端联调 */
     private StoreUser currentUser(HttpServletRequest request) {
-        LoginInfo loginInfo = (LoginInfo) request.getAttribute("loginInfo");
-        Integer storeUserId = loginInfo != null && loginInfo.getStoreUserId() > 0
-                ? (int) loginInfo.getStoreUserId()
-                : null;
-        StoreUser user = storeUserId != null
-                ? storeUserService.getById(storeUserId)
-                : storeUserService.getSuperAdmin();
-        if (user == null) {
+        StoreUser loginUser = (StoreUser) request.getAttribute("loginUser");
+        if (loginUser != null && loginUser.getStoreUserId() != null) {
+            return loginUser;
+        }
+        StoreUser fallback = storeUserService.getSuperAdmin();
+        if (fallback == null) {
             throw new BizException("请先登录后再访问", 401);
         }
-        return user;
+        return fallback;
     }
 }

+ 7 - 7
yoshop-java/src/main/java/cn/yoshop/store/controller/UserController.java

@@ -1,10 +1,11 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
+import cn.yoshop.common.entity.StoreUser;
 import cn.yoshop.common.entity.User;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.MemberService;
 import cn.yoshop.store.service.MemberWriteService;
 import jakarta.servlet.http.HttpServletRequest;
@@ -52,11 +53,11 @@ public class UserController {
         if (userId == null) {
             return R.fail("参数错误:userId");
         }
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        Object userName = info == null || info.getUser() == null ? null : info.getUser().get("userName");
+        StoreUser loginUser = (StoreUser) request.getAttribute("loginUser");
+        String userName = loginUser == null ? null : loginUser.getUserName();
         memberWriteService.recharge(userId, target, Params.form(body),
-                userName == null ? "" : String.valueOf(userName),
-                info == null ? 0 : (int) info.getStoreId());
+                userName == null ? "" : userName,
+                StoreContext.storeId(request));
         return R.okMsg("操作成功");
     }
 
@@ -67,8 +68,7 @@ public class UserController {
         if (userId == null) {
             return R.fail("参数错误:userId");
         }
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        memberWriteService.updateGrade(userId, Params.form(body), info == null ? 0 : (int) info.getStoreId());
+        memberWriteService.updateGrade(userId, Params.form(body), StoreContext.storeId(request));
         return R.okMsg("操作成功");
     }
 }

+ 2 - 3
yoshop-java/src/main/java/cn/yoshop/store/controller/UserGradeController.java

@@ -1,10 +1,10 @@
 package cn.yoshop.store.controller;
 
 import cn.yoshop.common.api.R;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.entity.UserGrade;
 import cn.yoshop.common.page.PageVO;
 import cn.yoshop.common.util.Params;
+import cn.yoshop.common.web.StoreContext;
 import cn.yoshop.store.service.UserGradeService;
 import jakarta.servlet.http.HttpServletRequest;
 import lombok.RequiredArgsConstructor;
@@ -64,7 +64,6 @@ public class UserGradeController {
     }
 
     private static int storeId(HttpServletRequest request) {
-        LoginInfo info = (LoginInfo) request.getAttribute("loginInfo");
-        return info == null ? 0 : (int) info.getStoreId();
+        return StoreContext.storeId(request);
     }
 }

+ 5 - 32
yoshop-java/src/main/java/cn/yoshop/store/service/StoreUserService.java

@@ -2,7 +2,6 @@ package cn.yoshop.store.service;
 
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.crypto.digest.DigestUtil;
-import cn.yoshop.common.auth.LoginInfo;
 import cn.yoshop.common.auth.RedisTokenStore;
 import cn.yoshop.common.entity.Store;
 import cn.yoshop.common.entity.StoreUser;
@@ -66,13 +65,9 @@ public class StoreUserService {
         }
 
         String token = makeToken(user.getStoreUserId());
-        LoginInfo info = new LoginInfo();
-        info.setUser(toCacheUser(user));
-        info.setStoreId(user.getStoreId() == null ? 0L : user.getStoreId());
-        info.setLogin(true);
         try {
-            // 有效期取 yoshop.auth.token-ttl,默认 7 天
-            tokenStore.save(token, info);
+            // 登录用户整条写进 Redis,key 就是 token,有效期取 yoshop.auth.token-ttl(默认 7 天)
+            tokenStore.save(token, user);
         } catch (Exception e) {
             throw new BizException("登录失败, 登录态写入失败: " + e.getMessage());
         }
@@ -97,16 +92,14 @@ public class StoreUserService {
             return;
         }
         try {
-            LoginInfo info = tokenStore.getLoginInfo(token);
-            if (info == null) {
+            if (tokenStore.getUser(token) == null) {
                 return;
             }
             long ttl = tokenStore.remainingTtlSeconds(token);
-            info.setUser(toCacheUser(user));
             if (ttl > 0) {
-                tokenStore.save(token, info, ttl);
+                tokenStore.save(token, user, ttl);
             } else {
-                tokenStore.save(token, info);
+                tokenStore.save(token, user);
             }
         } catch (Exception ignored) {
             // 缓存刷新失败不阻断资料更新
@@ -126,10 +119,6 @@ public class StoreUserService {
         return data;
     }
 
-    public StoreUser getById(Integer storeUserId) {
-        return storeUserMapper.selectById(storeUserId);
-    }
-
     /** 双跑/开发模式(auth.mode=off)下无登录态时兜底取超管,仅用于本地联调 */
     public StoreUser getSuperAdmin() {
         return storeUserMapper.selectOne(Wrappers.<StoreUser>lambdaQuery()
@@ -156,20 +145,4 @@ public class StoreUserService {
         String raw = System.nanoTime() + "_" + userId + "_" + IdUtil.fastSimpleUUID() + "_store_user_salt_";
         return DigestUtil.md5Hex(raw);
     }
-
-    /** 写入缓存 user 节点的字段集合与 PHP 模型 toArray() 保持一致 */
-    private Map<String, Object> toCacheUser(StoreUser user) {
-        Map<String, Object> m = new LinkedHashMap<>();
-        m.put("storeUserId", user.getStoreUserId());
-        m.put("userName", user.getUserName());
-        m.put("password", user.getPassword());
-        m.put("realName", user.getRealName());
-        m.put("isSuper", user.getIsSuper());
-        m.put("isDelete", user.getIsDelete());
-        m.put("sort", user.getSort());
-        m.put("storeId", user.getStoreId());
-        m.put("createTime", user.getCreateTime());
-        m.put("updateTime", user.getUpdateTime());
-        return m;
-    }
 }