import fetch from "node-fetch"; const USER_SERVICE_URL = process.env.USER_SERVICE_URL || "http://127.0.0.1:9010"; const CACHE_TTL = 5 * 60 * 1000; // 成功结果缓存 5 分钟 const NEGATIVE_TTL = 60 * 1000; // 无效 token 短缓存,防止打爆 user 服务 const MAX_CACHE_SIZE = 2000; // key -> { user, expiresAt },Map 按插入序实现简易 LRU const cache = new Map(); function cacheGet(key) { const hit = cache.get(key); if (!hit) return undefined; if (Date.now() > hit.expiresAt) { cache.delete(key); return undefined; } // 重新插入以更新 LRU 顺序 cache.delete(key); cache.set(key, hit); return hit.user; } function cacheSet(key, user, ttl) { cache.set(key, { user, expiresAt: Date.now() + ttl }); if (cache.size > MAX_CACHE_SIZE) { cache.delete(cache.keys().next().value); } } async function lookup(body) { const res = await fetch(`${USER_SERVICE_URL}/user/userInfo`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); const data = await res.json(); return data?.data?.user || null; } /** * 获取用户信息(带 5 分钟进程内缓存,token→user) * * 注意:缓存期内登出/账户锁定/用户信息变更最多延迟 5 分钟生效 * (user 服务自身接口即时生效) * * @param {string} token 登录 token * @param {string} [userId] 备用用户ID(token 缺失或失效时的兜底路径,保持原 WS 行为) * @returns {Promise} 用户信息或 null */ export async function getUserInfo(token, userId) { if (!token && !userId) return null; // 1. token 路径(带缓存) if (token) { const key = `tk:${token}`; const cached = cacheGet(key); if (cached !== undefined) return cached; try { const user = await lookup({ token }); if (user) { cacheSet(key, user, CACHE_TTL); return user; } // 无效 token 短缓存,保护 user 服务 cacheSet(key, null, NEGATIVE_TTL); } catch (err) { // 网络异常不缓存,走 userId 兜底 console.error("getUserByToken error:", err.message); } } // 2. userId 兜底路径 if (userId) { const key = `uid:${userId}`; const cached = cacheGet(key); if (cached !== undefined) return cached; try { const user = await lookup({ userId }); if (user) { cacheSet(key, user, CACHE_TTL); return user; } } catch (err) { console.error("getUserById error:", err.message); } } return null; } /** * 仅按 token 获取用户信息(REST 鉴权用,无 userId 兜底) */ export async function getUserByToken(token) { return getUserInfo(token, null); }