Browse Source

全局缓存使用清理清除侧边栏缓存机制

zhangzl 3 months ago
parent
commit
7adacad1be

+ 103 - 28
new-react-admin-ui/src/adapters/ruoyiAuthProvider.ts

@@ -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);
     }
   },
 };

+ 19 - 25
new-react-admin-ui/src/components/CustomMenu.tsx

@@ -1,4 +1,4 @@
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
 import { 
   Menu, 
   MenuItemLink, 
@@ -155,31 +155,25 @@ export const CustomMenu: React.FC<MenuProps> = (props) => {
     });
   };
 
-  // 加载菜单数据
-  useEffect(() => {
-    const loadMenuData = async () => {
-      try {
-        setLoading(true);
-        setError(null);
-        
-        // 等待权限加载完成后再获取菜单数据
-        if (permissionLoading) {
-          return;
-        }
-        
-        // menuService.getMenuData() 内部已经进行了权限过滤
-        const data = await menuService.getMenuData();
-        setMenuData(data);
-      } catch (err) {
-        setError(err instanceof Error ? err.message : '加载菜单失败');
-      } finally {
-        setLoading(false);
-      }
-    };
-
-    loadMenuData();
-  }, [permissionLoading]); // 移除 menuPermissions 依赖,避免重复加载
+  // 加载菜单数据(禁用缓存,每次都从服务器获取最新数据)
+  const loadMenuData = useCallback(async () => {
+    try {
+      // 强制禁用缓存,确保获取最新菜单数据
+      const data = await menuService.getMenuData(true);
+      setMenuData(data);
+      setLoading(false);
+    } catch (error) {
+      console.error('加载菜单数据失败:', error);
+      setError('加载菜单数据失败');
+      setLoading(false);
+    }
+  }, []);
 
+  useEffect(() => {
+    if (!permissionLoading) {
+      loadMenuData();
+    }
+  }, [permissionLoading, loadMenuData]);
   if (loading || permissionLoading) {
     return (
       <Menu {...props}>

+ 20 - 0
new-react-admin-ui/src/hooks/usePermission.ts

@@ -61,6 +61,26 @@ export const useMenuPermission = () => {
   useEffect(() => {
     const loadMenuPermissions = async () => {
       try {
+        // 首先尝试从本地存储获取缓存的权限信息
+        const userStr = localStorage.getItem('ruoyi_user');
+        const rolesStr = localStorage.getItem('ruoyi_roles');
+        const permissionsStr = localStorage.getItem('ruoyi_permissions');
+        
+        if (userStr && rolesStr && permissionsStr) {
+          try {
+            const user = JSON.parse(userStr);
+            const roles = JSON.parse(rolesStr);
+            const permissions = JSON.parse(permissionsStr);
+            
+            setMenuPermissions(permissions || []);
+            setLoading(false);
+            return;
+          } catch (error) {
+            console.error('解析本地缓存权限信息失败:', error);
+          }
+        }
+        
+        // 如果本地缓存不存在,才从服务器获取
         const permissions = await permissionService.getCurrentUserPermissions();
         setMenuPermissions(permissions.menuPermissions);
       } catch (error) {

+ 62 - 2
new-react-admin-ui/src/services/permissionService.ts

@@ -44,6 +44,32 @@ class PermissionService {
   private readonly CACHE_DURATION = 10 * 60 * 1000; // 10分钟缓存
   private lastPermissionCheck = 0;
 
+  // 从本地存储获取缓存的用户信息
+  private getCachedUserInfo(): GetInfoResult | null {
+    const userStr = localStorage.getItem('ruoyi_user');
+    const rolesStr = localStorage.getItem('ruoyi_roles');
+    const permissionsStr = localStorage.getItem('ruoyi_permissions');
+    
+    if (!userStr || !rolesStr || !permissionsStr) {
+      return null;
+    }
+    
+    try {
+      const user = JSON.parse(userStr);
+      const roles = JSON.parse(rolesStr);
+      const permissions = JSON.parse(permissionsStr);
+      
+      return {
+        user,
+        roles,
+        permissions
+      };
+    } catch (error) {
+      console.error('解析本地缓存用户信息失败:', error);
+      return null;
+    }
+  }
+
   // 获取当前用户权限信息
   async getCurrentUserPermissions(): Promise<UserPermission> {
     // 检查缓存是否有效
@@ -52,8 +78,24 @@ class PermissionService {
       return this.currentUserPermissions;
     }
 
+    // 首先尝试从本地存储获取缓存
+    const cachedUserInfo = this.getCachedUserInfo();
+    if (cachedUserInfo) {
+      this.currentUserPermissions = {
+        userId: cachedUserInfo.user.userId || 0,
+        userName: cachedUserInfo.user.userName || '',
+        roles: cachedUserInfo.roles || [],
+        permissions: cachedUserInfo.permissions || [],
+        menuPermissions: cachedUserInfo.permissions || [] // 使用permissions作为菜单权限
+      };
+      this.lastPermissionCheck = now;
+      return this.currentUserPermissions;
+    }
+
+    // 如果本地缓存不存在,才从服务器获取
     try {
-      const userInfo = await request<GetInfoResult>('/getInfo');
+      // 使用全局缓存机制获取用户信息
+      const userInfo = await this.getUserInfoWithCache();
       
       if (userInfo.user) {
         this.currentUserPermissions = {
@@ -76,6 +118,25 @@ class PermissionService {
     }
   }
 
+  // 使用全局缓存获取用户信息
+  private async getUserInfoWithCache(): Promise<GetInfoResult> {
+    // 首先尝试从本地存储获取缓存
+    const cachedUserInfo = this.getCachedUserInfo();
+    if (cachedUserInfo) {
+      return cachedUserInfo;
+    }
+    
+    // 如果本地缓存不存在,从服务器获取
+    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 || []));
+    
+    return userInfo;
+  }
+
   // 检查用户是否拥有指定权限
   async hasPermission(permission: string): Promise<boolean> {
     
@@ -167,7 +228,6 @@ class PermissionService {
   }
 
 
-
   // 清除权限缓存
   clearCache(): void {
     this.currentUserPermissions = null;