|
|
@@ -31,6 +31,39 @@ const request = async <T>(url: string, options: RequestInit & { isToken?: boolea
|
|
|
return data;
|
|
|
};
|
|
|
|
|
|
+// 全局用户信息缓存 - 参考react-ui的缓存机制
|
|
|
+let cachedUserInfo: GetInfoResult | null = null;
|
|
|
+let lastFetchTime = 0;
|
|
|
+const CACHE_DURATION = 5 * 60 * 1000; // 5分钟缓存
|
|
|
+
|
|
|
+// 获取用户信息(带缓存)
|
|
|
+const getUserInfoWithCache = async (): Promise<GetInfoResult> => {
|
|
|
+ const now = Date.now();
|
|
|
+
|
|
|
+ // 如果缓存有效且未过期,直接返回缓存
|
|
|
+ if (cachedUserInfo && now - lastFetchTime < CACHE_DURATION) {
|
|
|
+ return cachedUserInfo;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从服务器获取最新用户信息
|
|
|
+ const userInfo = await request<GetInfoResult>('/getInfo');
|
|
|
+ cachedUserInfo = userInfo;
|
|
|
+ lastFetchTime = now;
|
|
|
+
|
|
|
+ // 更新本地存储
|
|
|
+ localStorage.setItem('ruoyi_user', JSON.stringify(userInfo.user));
|
|
|
+ localStorage.setItem('ruoyi_roles', JSON.stringify(userInfo.roles || []));
|
|
|
+ localStorage.setItem('ruoyi_permissions', JSON.stringify(userInfo.permissions || []));
|
|
|
+
|
|
|
+ return userInfo;
|
|
|
+};
|
|
|
+
|
|
|
+// 清除用户信息缓存
|
|
|
+const clearUserInfoCache = (): void => {
|
|
|
+ cachedUserInfo = null;
|
|
|
+ lastFetchTime = 0;
|
|
|
+};
|
|
|
+
|
|
|
export const ruoyiAuthProvider: AuthProvider = {
|
|
|
// 登录
|
|
|
login: async (params: LoginParams) => {
|
|
|
@@ -41,6 +74,13 @@ export const ruoyiAuthProvider: AuthProvider = {
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
+ // 登录前先清除所有旧缓存,避免权限混淆
|
|
|
+ localStorage.removeItem('access_token');
|
|
|
+ localStorage.removeItem('ruoyi_user');
|
|
|
+ localStorage.removeItem('ruoyi_roles');
|
|
|
+ localStorage.removeItem('ruoyi_permissions');
|
|
|
+ clearUserInfoCache();
|
|
|
+
|
|
|
// 获取验证码(如果需要)
|
|
|
const captchaResponse = await request('/captchaImage');
|
|
|
|
|
|
@@ -58,17 +98,20 @@ export const ruoyiAuthProvider: AuthProvider = {
|
|
|
// 存储token - 统一使用access_token键名
|
|
|
localStorage.setItem('access_token', loginData.token);
|
|
|
|
|
|
- // 获取用户信息
|
|
|
- const userInfo = await request<GetInfoResult>('/getInfo');
|
|
|
- localStorage.setItem('ruoyi_user', JSON.stringify(userInfo.user));
|
|
|
- localStorage.setItem('ruoyi_roles', JSON.stringify(userInfo.roles || []));
|
|
|
- localStorage.setItem('ruoyi_permissions', JSON.stringify(userInfo.permissions || []));
|
|
|
+ // 获取用户信息并同步到本地存储(仅登录时调用一次)
|
|
|
+ const userInfo = await getUserInfoWithCache();
|
|
|
|
|
|
return Promise.resolve();
|
|
|
} else {
|
|
|
throw new Error(loginData.msg || '登录失败');
|
|
|
}
|
|
|
} catch (error) {
|
|
|
+ // 登录失败时确保清除所有缓存
|
|
|
+ localStorage.removeItem('access_token');
|
|
|
+ localStorage.removeItem('ruoyi_user');
|
|
|
+ localStorage.removeItem('ruoyi_roles');
|
|
|
+ localStorage.removeItem('ruoyi_permissions');
|
|
|
+ clearUserInfoCache();
|
|
|
throw new Error(error instanceof Error ? error.message : '登录失败');
|
|
|
}
|
|
|
},
|
|
|
@@ -80,11 +123,12 @@ export const ruoyiAuthProvider: AuthProvider = {
|
|
|
} catch (error) {
|
|
|
console.warn('登出请求失败:', error);
|
|
|
} finally {
|
|
|
- // 清除本地存储
|
|
|
+ // 清除本地存储和缓存
|
|
|
localStorage.removeItem('access_token');
|
|
|
localStorage.removeItem('ruoyi_user');
|
|
|
localStorage.removeItem('ruoyi_roles');
|
|
|
localStorage.removeItem('ruoyi_permissions');
|
|
|
+ clearUserInfoCache();
|
|
|
}
|
|
|
return Promise.resolve();
|
|
|
},
|
|
|
@@ -96,8 +140,35 @@ export const ruoyiAuthProvider: AuthProvider = {
|
|
|
throw new Error('未认证');
|
|
|
}
|
|
|
|
|
|
- // 可以添加token有效性检查
|
|
|
- return Promise.resolve();
|
|
|
+ // 验证token有效性(使用缓存,避免重复调用)
|
|
|
+ try {
|
|
|
+ const userInfo = await getUserInfoWithCache();
|
|
|
+
|
|
|
+ // 检查当前存储的用户信息是否与服务器一致
|
|
|
+ const storedUserStr = localStorage.getItem('ruoyi_user');
|
|
|
+ if (storedUserStr) {
|
|
|
+ const storedUser = JSON.parse(storedUserStr);
|
|
|
+ if (storedUser.userId !== userInfo.user.userId) {
|
|
|
+ // 用户已变更,清除所有缓存并重新登录
|
|
|
+ localStorage.removeItem('access_token');
|
|
|
+ localStorage.removeItem('ruoyi_user');
|
|
|
+ localStorage.removeItem('ruoyi_roles');
|
|
|
+ localStorage.removeItem('ruoyi_permissions');
|
|
|
+ clearUserInfoCache();
|
|
|
+ throw new Error('用户已变更,请重新登录');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return Promise.resolve();
|
|
|
+ } catch (error) {
|
|
|
+ // 如果获取用户信息失败,清除token强制重新登录
|
|
|
+ localStorage.removeItem('access_token');
|
|
|
+ localStorage.removeItem('ruoyi_user');
|
|
|
+ localStorage.removeItem('ruoyi_roles');
|
|
|
+ localStorage.removeItem('ruoyi_permissions');
|
|
|
+ clearUserInfoCache();
|
|
|
+ throw new Error('认证已过期,请重新登录');
|
|
|
+ }
|
|
|
},
|
|
|
|
|
|
// 检查错误
|
|
|
@@ -105,6 +176,7 @@ export const ruoyiAuthProvider: AuthProvider = {
|
|
|
const status = error.status;
|
|
|
if (status === 401 || status === 403) {
|
|
|
localStorage.removeItem('access_token');
|
|
|
+ clearUserInfoCache();
|
|
|
return Promise.reject();
|
|
|
}
|
|
|
return Promise.resolve();
|
|
|
@@ -113,18 +185,18 @@ export const ruoyiAuthProvider: AuthProvider = {
|
|
|
// 获取用户权限
|
|
|
getPermissions: async () => {
|
|
|
try {
|
|
|
- const permissions = localStorage.getItem('ruoyi_permissions');
|
|
|
- if (permissions) {
|
|
|
- return Promise.resolve(JSON.parse(permissions));
|
|
|
- }
|
|
|
-
|
|
|
- // 如果没有缓存,重新获取
|
|
|
- const userInfo = await request<GetInfoResult>('/getInfo');
|
|
|
+ // 使用缓存获取权限信息,避免重复调用
|
|
|
+ const userInfo = await getUserInfoWithCache();
|
|
|
const perms = userInfo.permissions || [];
|
|
|
- localStorage.setItem('ruoyi_permissions', JSON.stringify(perms));
|
|
|
|
|
|
return Promise.resolve(perms);
|
|
|
} catch (error) {
|
|
|
+ // 如果获取失败,尝试使用本地缓存(但标记为可能过期)
|
|
|
+ const permissions = localStorage.getItem('ruoyi_permissions');
|
|
|
+ if (permissions) {
|
|
|
+ console.warn('使用本地缓存的权限信息,可能已过期');
|
|
|
+ return Promise.resolve(JSON.parse(permissions));
|
|
|
+ }
|
|
|
return Promise.resolve([]);
|
|
|
}
|
|
|
},
|
|
|
@@ -132,29 +204,32 @@ export const ruoyiAuthProvider: AuthProvider = {
|
|
|
// 获取用户身份
|
|
|
getIdentity: async () => {
|
|
|
try {
|
|
|
+ // 使用缓存获取用户信息,避免重复调用
|
|
|
+ const userInfo = await getUserInfoWithCache();
|
|
|
+ const user = userInfo.user;
|
|
|
+
|
|
|
+ return Promise.resolve({
|
|
|
+ id: user.userId?.toString() || 'unknown',
|
|
|
+ fullName: user.nickName || user.userName || '用户',
|
|
|
+ avatar: user.avatar,
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ // 如果获取失败,尝试使用本地缓存(但标记为可能过期)
|
|
|
const userStr = localStorage.getItem('ruoyi_user');
|
|
|
if (userStr) {
|
|
|
const user: CurrentUser = JSON.parse(userStr);
|
|
|
+ console.warn('使用本地缓存的用户信息,可能已过期');
|
|
|
return Promise.resolve({
|
|
|
id: user.userId?.toString() || 'unknown',
|
|
|
fullName: user.nickName || user.userName || '用户',
|
|
|
avatar: user.avatar,
|
|
|
});
|
|
|
}
|
|
|
-
|
|
|
- // 如果没有缓存,重新获取
|
|
|
- const userInfo = await request<GetInfoResult>('/getInfo');
|
|
|
- const user = userInfo.user;
|
|
|
-
|
|
|
- localStorage.setItem('ruoyi_user', JSON.stringify(user));
|
|
|
-
|
|
|
return Promise.resolve({
|
|
|
- id: user.userId?.toString() || 'unknown',
|
|
|
- fullName: user.nickName || user.userName || '用户',
|
|
|
- avatar: user.avatar,
|
|
|
+ id: 'unknown',
|
|
|
+ fullName: '用户',
|
|
|
+ avatar: undefined,
|
|
|
});
|
|
|
- } catch (error) {
|
|
|
- return Promise.reject(error);
|
|
|
}
|
|
|
},
|
|
|
};
|