179 lines
5.1 KiB
TypeScript
179 lines
5.1 KiB
TypeScript
|
|
/**
|
|||
|
|
* UserManager - 用户管理核心模块(重构版<EFBFBD><EFBFBD>?
|
|||
|
|
*
|
|||
|
|
* 使用 Zustand store 进行状态管<EFBFBD><EFBFBD>?
|
|||
|
|
* 保持与旧 API 的向后兼<EFBFBD><EFBFBD>?
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import type { UserDTO } from '../../types/dto';
|
|||
|
|
import { authService } from '../../services/authService';
|
|||
|
|
import {
|
|||
|
|
getCurrentUserCache,
|
|||
|
|
getUserCache,
|
|||
|
|
saveCurrentUserCache,
|
|||
|
|
saveUserCache,
|
|||
|
|
} from '../../services/database';
|
|||
|
|
import { useUserManagerStore } from './userStore';
|
|||
|
|
import { createCacheEntry, isCacheExpired, DEFAULT_TTL } from '../utils/cache';
|
|||
|
|
|
|||
|
|
// ==================== UserManager <20><>?====================
|
|||
|
|
|
|||
|
|
class UserManager {
|
|||
|
|
/**
|
|||
|
|
* 获取当前用户
|
|||
|
|
* 支持缓存过期检查和后台刷新
|
|||
|
|
*/
|
|||
|
|
async getCurrentUser(forceRefresh = false): Promise<UserDTO | null> {
|
|||
|
|
const store = useUserManagerStore.getState();
|
|||
|
|
const entry = store.currentUserEntry;
|
|||
|
|
|
|||
|
|
// 有效缓存
|
|||
|
|
if (!forceRefresh && entry && !isCacheExpired(entry)) {
|
|||
|
|
return entry.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 过期缓存:后台刷新,立即返回旧数<E697A7><E695B0>?
|
|||
|
|
if (!forceRefresh && entry && isCacheExpired(entry)) {
|
|||
|
|
this.refreshCurrentUserInBackground();
|
|||
|
|
return entry.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试从本地数据库加载
|
|||
|
|
if (!forceRefresh) {
|
|||
|
|
const cachedFromDb = await getCurrentUserCache();
|
|||
|
|
if (cachedFromDb) {
|
|||
|
|
const newEntry = createCacheEntry(cachedFromDb, DEFAULT_TTL.USER);
|
|||
|
|
store.setCurrentUserEntry(newEntry);
|
|||
|
|
this.refreshCurrentUserInBackground();
|
|||
|
|
return cachedFromDb;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 发起 API 请求
|
|||
|
|
return this.dedupe('users:current', async () => {
|
|||
|
|
const user = await authService.fetchCurrentUserFromAPI();
|
|||
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|||
|
|
store.setCurrentUserEntry(newEntry);
|
|||
|
|
|
|||
|
|
if (user) {
|
|||
|
|
saveCurrentUserCache(user).catch(() => {});
|
|||
|
|
saveUserCache(user).catch(() => {});
|
|||
|
|
}
|
|||
|
|
return user;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 后台刷新当前用户
|
|||
|
|
*/
|
|||
|
|
private refreshCurrentUserInBackground(): void {
|
|||
|
|
this.dedupe('users:current:bg', async () => {
|
|||
|
|
const store = useUserManagerStore.getState();
|
|||
|
|
const user = await authService.fetchCurrentUserFromAPI();
|
|||
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|||
|
|
store.setCurrentUserEntry(newEntry);
|
|||
|
|
|
|||
|
|
if (user) {
|
|||
|
|
saveCurrentUserCache(user).catch(() => {});
|
|||
|
|
saveUserCache(user).catch(() => {});
|
|||
|
|
}
|
|||
|
|
return user;
|
|||
|
|
}).catch((error) => {
|
|||
|
|
console.error('[UserManager] refreshCurrentUserInBackground error:', error);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 根据 ID 获取用户
|
|||
|
|
*/
|
|||
|
|
async getUserById(userId: string, forceRefresh = false): Promise<UserDTO | null> {
|
|||
|
|
const store = useUserManagerStore.getState();
|
|||
|
|
const entry = store.usersMap.get(userId);
|
|||
|
|
|
|||
|
|
// 有效缓存
|
|||
|
|
if (!forceRefresh && entry && !isCacheExpired(entry)) {
|
|||
|
|
return entry.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 过期缓存:后台刷新,立即返回旧数<E697A7><E695B0>?
|
|||
|
|
if (!forceRefresh && entry && isCacheExpired(entry)) {
|
|||
|
|
this.refreshUserByIdInBackground(userId);
|
|||
|
|
return entry.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试从本地数据库加载
|
|||
|
|
if (!forceRefresh) {
|
|||
|
|
const cachedFromDb = await getUserCache(userId);
|
|||
|
|
if (cachedFromDb) {
|
|||
|
|
const newEntry = createCacheEntry(cachedFromDb, DEFAULT_TTL.USER);
|
|||
|
|
store.setUser(userId, cachedFromDb);
|
|||
|
|
this.refreshUserByIdInBackground(userId);
|
|||
|
|
return cachedFromDb;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 发起 API 请求
|
|||
|
|
return this.dedupe(`users:detail:${userId}`, async () => {
|
|||
|
|
const store = useUserManagerStore.getState();
|
|||
|
|
const user = await authService.fetchUserByIdFromAPI(userId);
|
|||
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|||
|
|
store.setUser(userId, user);
|
|||
|
|
|
|||
|
|
if (user) {
|
|||
|
|
saveUserCache(user).catch(() => {});
|
|||
|
|
}
|
|||
|
|
return user;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 后台刷新用户
|
|||
|
|
*/
|
|||
|
|
private refreshUserByIdInBackground(userId: string): void {
|
|||
|
|
this.dedupe(`users:detail:bg:${userId}`, async () => {
|
|||
|
|
const store = useUserManagerStore.getState();
|
|||
|
|
const user = await authService.fetchUserByIdFromAPI(userId);
|
|||
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|||
|
|
store.setUser(userId, user);
|
|||
|
|
|
|||
|
|
if (user) {
|
|||
|
|
saveUserCache(user).catch(() => {});
|
|||
|
|
}
|
|||
|
|
return user;
|
|||
|
|
}).catch((error) => {
|
|||
|
|
console.error('[UserManager] refreshUserByIdInBackground error:', error);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 使缓存失<EFBFBD><EFBFBD>?
|
|||
|
|
*/
|
|||
|
|
invalidateUsers(): void {
|
|||
|
|
useUserManagerStore.getState().invalidateAll();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 请求去重
|
|||
|
|
*/
|
|||
|
|
private async dedupe<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
|
|||
|
|
const store = useUserManagerStore.getState();
|
|||
|
|
const pending = store.getPendingRequest<T>(key);
|
|||
|
|
if (pending) return pending;
|
|||
|
|
|
|||
|
|
const request = fetcher().finally(() => {
|
|||
|
|
useUserManagerStore.getState().deletePendingRequest(key);
|
|||
|
|
});
|
|||
|
|
store.setPendingRequest(key, request);
|
|||
|
|
return request;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 单例导出 ====================
|
|||
|
|
|
|||
|
|
export const userManager = new UserManager();
|
|||
|
|
|
|||
|
|
// 导出类以支持类型检查和测试
|
|||
|
|
export { UserManager };
|
|||
|
|
|
|||
|
|
export default userManager;
|