100 lines
2.7 KiB
JavaScript
100 lines
2.7 KiB
JavaScript
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<Object|null>} 用户信息或 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);
|
||
}
|