From 006c5bc4c13edafc7605d9affd9fbfb100dcc169 Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Sat, 27 Jun 2026 19:57:06 +0800 Subject: [PATCH] fix: cache yuser login info by user id --- frontend-web/src/core/auth/yuser-login.ts | 102 ++++++++++++++++++---- 1 file changed, 87 insertions(+), 15 deletions(-) diff --git a/frontend-web/src/core/auth/yuser-login.ts b/frontend-web/src/core/auth/yuser-login.ts index e5217ece..a43405bb 100644 --- a/frontend-web/src/core/auth/yuser-login.ts +++ b/frontend-web/src/core/auth/yuser-login.ts @@ -9,6 +9,8 @@ export const EXTERNAL_USER_ID_STORAGE_KEY = "userId"; export const EXTERNAL_USER_INFO_STORAGE_KEY = "userInfo"; export const GENERAL_QA_ROUTE = "/page/workspace/chats/new"; +const YUSER_INFO_CACHE_KEY = "deerflow.yuserInfoByYUserId"; + type UserDetailResponse = { data?: { id?: string; @@ -34,6 +36,84 @@ function readStoredYUserId(info: Record): string { return value == null ? "" : String(value).trim(); } +function isPlainObject(value: unknown): value is Record { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); +} + +function isUsableExternalValue(value: unknown): boolean { + if (value == null) return false; + if (typeof value !== "string") return true; + const trimmed = value.trim(); + if (!trimmed) return false; + const lowered = trimmed.toLowerCase(); + return lowered !== "null" && lowered !== "undefined"; +} + +function readYUserInfoCache(): Record> { + if (typeof window === "undefined") return {}; + try { + const raw = window.localStorage.getItem(YUSER_INFO_CACHE_KEY); + if (!raw) return {}; + const parsed = JSON.parse(raw); + if (!isPlainObject(parsed)) return {}; + const cache: Record> = {}; + for (const [key, value] of Object.entries(parsed)) { + if (isPlainObject(value)) cache[key] = value; + } + return cache; + } catch { + return {}; + } +} + +function readCachedYUserInfo(yUserId: string): Record { + return readYUserInfoCache()[yUserId] ?? {}; +} + +function writeCachedYUserInfo(yUserId: string, info: Record): void { + if (typeof window === "undefined") return; + try { + const cache = readYUserInfoCache(); + cache[yUserId] = info; + window.localStorage.setItem(YUSER_INFO_CACHE_KEY, JSON.stringify(cache)); + } catch { + /* noop */ + } +} + +function buildExternalUserInfo( + yUserId: string, + result: Record, + previous: Record, + cached: Record, +): Record { + const next: Record = { ...cached }; + for (const [key, value] of Object.entries(result)) { + if (isUsableExternalValue(value)) { + next[key] = value; + } + } + next.yUserId = yUserId; + + const incomingAccessToken = normalizeExternalAccessToken(result.accessToken); + if (incomingAccessToken) { + next.accessToken = incomingAccessToken; + return next; + } + + const previousYUserId = readStoredYUserId(previous); + const previousAccessToken = + previousYUserId === yUserId ? normalizeExternalAccessToken(previous.accessToken) : ""; + const cachedAccessToken = normalizeExternalAccessToken(cached.accessToken); + const fallbackAccessToken = previousAccessToken || cachedAccessToken; + if (fallbackAccessToken) { + next.accessToken = fallbackAccessToken; + } else { + delete next.accessToken; + } + return next; +} + /** * External login via yUserId + optional token (same flow as web-code Login.tsx): * persist tools_token, resolve user via consumer API, store userId/userInfo. @@ -58,22 +138,10 @@ export async function loginByYUserId( // access_token,故登录流程随后会用 persistAccessToken 覆盖 userInfo.accessToken。 const result = res.data ?? {}; const previous = readExternalUserInfo(); - const next: Record = { ...result, yUserId }; - const incomingAccessToken = normalizeExternalAccessToken(result.accessToken); - if (incomingAccessToken) { - next.accessToken = incomingAccessToken; - } else { - const previousAccessToken = normalizeExternalAccessToken(previous.accessToken); - const previousYUserId = readStoredYUserId(previous); - // Do not let an unstable upstream empty/null token wipe a usable token for - // the same external user. Avoid carrying it across different yUserId values. - if (previousAccessToken && previousYUserId === yUserId) { - next.accessToken = previousAccessToken; - } else { - delete next.accessToken; - } - } + const cached = readCachedYUserInfo(yUserId); + const next = buildExternalUserInfo(yUserId, result, previous, cached); authStorageSet(EXTERNAL_USER_INFO_STORAGE_KEY, JSON.stringify(next)); + writeCachedYUserInfo(yUserId, next); } export function persistToolsToken(token: string): void { @@ -93,4 +161,8 @@ export function persistAccessToken(token: string): void { const info = readExternalUserInfo(); info.accessToken = trimmed; authStorageSet(EXTERNAL_USER_INFO_STORAGE_KEY, JSON.stringify(info)); + const yUserId = readStoredYUserId(info); + if (yUserId) { + writeCachedYUserInfo(yUserId, info); + } }