2026-04-01 02:17:36 +08:00
|
|
|
|
/**
|
2026-04-04 08:01:45 +08:00
|
|
|
|
* UserManager - 用户管理核心模块(重构版)
|
2026-04-01 02:17:36 +08:00
|
|
|
|
*
|
2026-04-04 08:01:45 +08:00
|
|
|
|
* 使用 Zustand store 进行状态管理
|
|
|
|
|
|
* 保持与旧 API 的向后兼容
|
2026-04-01 02:17:36 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
import type { UserDTO } from '../../types/dto';
|
2026-04-13 01:30:37 +08:00
|
|
|
|
import { authService } from '@/services/auth';
|
2026-04-04 08:01:45 +08:00
|
|
|
|
import { userCacheRepository } from '@/database';
|
2026-04-01 02:17:36 +08:00
|
|
|
|
import { useUserManagerStore } from './userStore';
|
|
|
|
|
|
import { createCacheEntry, isCacheExpired, DEFAULT_TTL } from '../utils/cache';
|
2026-04-12 18:14:29 +08:00
|
|
|
|
import { createDedupe } from '../utils/requestDedupe';
|
|
|
|
|
|
|
|
|
|
|
|
const dedupe = createDedupe(() => useUserManagerStore);
|
2026-04-01 02:17:36 +08:00
|
|
|
|
|
2026-06-21 17:17:15 +08:00
|
|
|
|
// 将 fetchCurrentUserFromAPI 的判别结果转为 User | null。
|
|
|
|
|
|
// 网络故障/认证失败时返回 null,由上层缓存兜底(避免空数据覆盖缓存)。
|
|
|
|
|
|
// 注意:UserManager 只负责缓存,不触发登出,登出由 authStore 统一处理。
|
|
|
|
|
|
async function fetchCurrentUserOrNull(): Promise<UserDTO | null> {
|
|
|
|
|
|
const result = await authService.fetchCurrentUserFromAPI();
|
|
|
|
|
|
return result.kind === 'user' ? result.user : null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-04 08:01:45 +08:00
|
|
|
|
// ==================== UserManager 类 ====================
|
2026-04-01 02:17:36 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!forceRefresh && entry && isCacheExpired(entry)) {
|
|
|
|
|
|
this.refreshCurrentUserInBackground();
|
|
|
|
|
|
return entry.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!forceRefresh) {
|
2026-04-04 08:01:45 +08:00
|
|
|
|
const cachedFromDb = await userCacheRepository.getCurrent();
|
2026-04-01 02:17:36 +08:00
|
|
|
|
if (cachedFromDb) {
|
|
|
|
|
|
const newEntry = createCacheEntry(cachedFromDb, DEFAULT_TTL.USER);
|
|
|
|
|
|
store.setCurrentUserEntry(newEntry);
|
|
|
|
|
|
this.refreshCurrentUserInBackground();
|
|
|
|
|
|
return cachedFromDb;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 18:14:29 +08:00
|
|
|
|
return dedupe('users:current', async () => {
|
2026-06-21 17:17:15 +08:00
|
|
|
|
const user = await fetchCurrentUserOrNull();
|
|
|
|
|
|
// 仅在拿到真实用户时刷新缓存;网络/认证失败时保留旧缓存,避免误清空
|
2026-04-01 02:17:36 +08:00
|
|
|
|
if (user) {
|
2026-06-21 17:17:15 +08:00
|
|
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|
|
|
|
|
store.setCurrentUserEntry(newEntry);
|
2026-04-04 08:01:45 +08:00
|
|
|
|
userCacheRepository.saveCurrent(user).catch(() => {});
|
|
|
|
|
|
userCacheRepository.save(user).catch(() => {});
|
2026-04-01 02:17:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
return user;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private refreshCurrentUserInBackground(): void {
|
2026-04-12 18:14:29 +08:00
|
|
|
|
dedupe('users:current:bg', async () => {
|
2026-04-01 02:17:36 +08:00
|
|
|
|
const store = useUserManagerStore.getState();
|
2026-06-21 17:17:15 +08:00
|
|
|
|
const user = await fetchCurrentUserOrNull();
|
2026-04-01 02:17:36 +08:00
|
|
|
|
if (user) {
|
2026-06-21 17:17:15 +08:00
|
|
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|
|
|
|
|
store.setCurrentUserEntry(newEntry);
|
2026-04-04 08:01:45 +08:00
|
|
|
|
userCacheRepository.saveCurrent(user).catch(() => {});
|
|
|
|
|
|
userCacheRepository.save(user).catch(() => {});
|
2026-04-01 02:17:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
return user;
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
console.error('[UserManager] refreshCurrentUserInBackground error:', error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!forceRefresh && entry && isCacheExpired(entry)) {
|
|
|
|
|
|
this.refreshUserByIdInBackground(userId);
|
|
|
|
|
|
return entry.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!forceRefresh) {
|
2026-04-04 08:01:45 +08:00
|
|
|
|
const cachedFromDb = await userCacheRepository.get(userId);
|
2026-04-01 02:17:36 +08:00
|
|
|
|
if (cachedFromDb) {
|
|
|
|
|
|
const newEntry = createCacheEntry(cachedFromDb, DEFAULT_TTL.USER);
|
|
|
|
|
|
store.setUser(userId, cachedFromDb);
|
|
|
|
|
|
this.refreshUserByIdInBackground(userId);
|
|
|
|
|
|
return cachedFromDb;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-12 18:14:29 +08:00
|
|
|
|
return dedupe(`users:detail:${userId}`, async () => {
|
2026-04-01 02:17:36 +08:00
|
|
|
|
const store = useUserManagerStore.getState();
|
|
|
|
|
|
const user = await authService.fetchUserByIdFromAPI(userId);
|
|
|
|
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|
|
|
|
|
store.setUser(userId, user);
|
|
|
|
|
|
|
|
|
|
|
|
if (user) {
|
2026-04-04 08:01:45 +08:00
|
|
|
|
userCacheRepository.save(user).catch(() => {});
|
2026-04-01 02:17:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
return user;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private refreshUserByIdInBackground(userId: string): void {
|
2026-04-12 18:14:29 +08:00
|
|
|
|
dedupe(`users:detail:bg:${userId}`, async () => {
|
2026-04-01 02:17:36 +08:00
|
|
|
|
const store = useUserManagerStore.getState();
|
|
|
|
|
|
const user = await authService.fetchUserByIdFromAPI(userId);
|
|
|
|
|
|
const newEntry = createCacheEntry(user, DEFAULT_TTL.USER);
|
|
|
|
|
|
store.setUser(userId, user);
|
|
|
|
|
|
|
|
|
|
|
|
if (user) {
|
2026-04-04 08:01:45 +08:00
|
|
|
|
userCacheRepository.save(user).catch(() => {});
|
2026-04-01 02:17:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
return user;
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
console.error('[UserManager] refreshUserByIdInBackground error:', error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
invalidateUsers(): void {
|
|
|
|
|
|
useUserManagerStore.getState().invalidateAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const userManager = new UserManager();
|
|
|
|
|
|
|
|
|
|
|
|
export { UserManager };
|
|
|
|
|
|
|
2026-04-04 08:01:45 +08:00
|
|
|
|
export default userManager;
|