import { createContext, useContext, useState, useCallback, useMemo, useRef, useEffect } from 'react'; import { menuItems, currentUser as defaultUser } from '@/lib/mock'; import { authApi } from '@/api/auth'; export type UserRole = 'admin' | 'dba' | 'developer' | 'guest'; /** 细粒度权限类型 */ export type PermissionAction = | 'ticket.create' | 'ticket.approve' | 'ticket.execute' | 'ticket.rollback' | 'ticket.delete' | 'query.create' | 'query.export' | 'instance.add' | 'instance.edit' | 'instance.delete' | 'user.manage' | 'role.manage' | 'rule.manage' | 'workflow.manage' | 'config.manage' | 'announcement.manage' | 'auto-task.manage' | 'instance-group.manage'; /** 数据源级权限 */ export interface DataSourcePermission { instanceId: number; database?: string; tables?: string[]; columns?: Record; actions: ('read' | 'write' | 'delete' | 'export')[]; } /** 扩展的用户信息 */ export interface AuthUser { id: number; username: string; email: string; role: UserRole; roleName: string; avatar: string; /** 用户组ID列表 */ groupIds?: number[]; /** 细粒度数据源权限 */ dataSourcePermissions?: DataSourcePermission[]; /** 自定义权限(覆盖角色默认权限) */ customPermissions?: Record; /** 权限最后更新时间 */ permissionsUpdatedAt?: number; } /** 权限缓存结构 */ interface PermissionCache { menu: Map; action: Map; dataSource: Map; timestamp: number; } interface AuthContextType { user: AuthUser; isLoggedIn: boolean; login: (username: string, password: string) => Promise; logout: () => void; switchRole: (role: UserRole) => void; /** 菜单级权限检查(带缓存) */ hasPermission: (menuId: string) => boolean; /** 操作级权限检查(带缓存) */ hasActionPermission: (action: PermissionAction | string) => boolean; /** 数据源级权限检查 */ hasDataSourcePermission: ( instanceId: number, action: 'read' | 'write' | 'delete' | 'export', database?: string, table?: string ) => boolean; /** 批量检查数据源权限 */ checkDataSourcePermissions: ( instanceId: number, actions: ('read' | 'write' | 'delete' | 'export')[], database?: string, table?: string ) => Record; /** 刷新用户权限 */ refreshPermissions: () => Promise; /** 清除权限缓存 */ clearPermissionCache: () => void; visibleMenuItems: typeof menuItems; isAdmin: boolean; isDBA: boolean; isDeveloper: boolean; isGuest: boolean; /** 是否拥有指定角色或以上级别权限 */ hasRoleLevel: (minRole: UserRole) => boolean; } const roleNameMap: Record = { admin: '超级管理员', dba: '数据库管理员', developer: '开发人员', guest: '访客', }; /** 角色权重映射,用于权限级别比较 */ const roleWeightMap: Record = { admin: 100, dba: 80, developer: 60, guest: 20, }; const routePermissionMap: Record = { dashboard: ['admin', 'dba', 'developer', 'guest'], instances: ['admin', 'dba'], 'instance-groups': ['admin'], 'data-dictionary': ['admin', 'dba', 'developer'], tickets: ['admin', 'dba', 'developer'], queries: ['admin', 'dba', 'developer', 'guest'], roles: ['admin'], users: ['admin'], 'audit-logs': ['admin', 'dba'], workflow: ['admin', 'dba'], 'auto-task': ['admin'], settings: ['admin'], }; export const actionPermissionMap: Record = { 'ticket.create': ['admin', 'dba', 'developer'], 'ticket.approve': ['admin', 'dba'], 'ticket.execute': ['admin', 'dba'], 'ticket.rollback': ['admin', 'dba'], 'ticket.delete': ['admin'], 'query.create': ['admin', 'dba', 'developer', 'guest'], 'query.export': ['admin', 'dba', 'developer'], 'instance.add': ['admin', 'dba'], 'instance.edit': ['admin', 'dba'], 'instance.delete': ['admin'], 'user.manage': ['admin'], 'role.manage': ['admin'], 'rule.manage': ['admin', 'dba'], 'workflow.manage': ['admin', 'dba'], 'config.manage': ['admin'], 'announcement.manage': ['admin'], 'auto-task.manage': ['admin'], 'instance-group.manage': ['admin'], }; /** 权限变更事件名称 */ const PERMISSION_CHANGE_EVENT = 'auth:permissions_changed'; /** 缓存有效期(毫秒) */ const CACHE_TTL = 5 * 60 * 1000; // 5分钟 const AuthContext = createContext(null); export function AuthProvider({ children }: { children: React.ReactNode }) { const [user, setUser] = useState({ id: defaultUser.id, username: defaultUser.username, email: defaultUser.email, role: defaultUser.role, roleName: defaultUser.roleName, avatar: defaultUser.avatar, groupIds: [], dataSourcePermissions: [], customPermissions: {}, }); const [isLoggedIn, setIsLoggedIn] = useState(() => !!localStorage.getItem('token')); /** 权限缓存引用 */ const permissionCacheRef = useRef({ menu: new Map(), action: new Map(), dataSource: new Map(), timestamp: 0, }); /** 权限变更监听器引用 */ const listenersRef = useRef void>>(new Set()); /** 检查缓存是否有效 */ const isCacheValid = useCallback((): boolean => { return Date.now() - permissionCacheRef.current.timestamp < CACHE_TTL; }, []); /** 清除权限缓存 */ const clearPermissionCache = useCallback(() => { permissionCacheRef.current.menu.clear(); permissionCacheRef.current.action.clear(); permissionCacheRef.current.dataSource.clear(); permissionCacheRef.current.timestamp = 0; }, []); /** 触发权限变更事件 */ const emitPermissionChange = useCallback((updatedUser: AuthUser) => { listenersRef.current.forEach(listener => listener(updatedUser)); // 触发自定义事件,供其他组件监听 if (typeof window !== 'undefined') { window.dispatchEvent(new CustomEvent(PERMISSION_CHANGE_EVENT, { detail: updatedUser })); } }, []); /** 注册权限变更监听器 */ useEffect(() => { const handleStorageChange = (e: StorageEvent) => { if (e.key === 'token') { if (!e.newValue) { setIsLoggedIn(false); clearPermissionCache(); } } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, [clearPermissionCache]); const login = useCallback(async (username: string, password: string): Promise => { try { const res = await authApi.login(username, password); localStorage.setItem('token', res.access_token); const u = res.user; const role = u.role as UserRole; // 解析后端返回的细粒度权限 const dataSourcePermissions: DataSourcePermission[] = u.data_source_permissions ? JSON.parse(u.data_source_permissions) : []; const customPermissions: Record = u.custom_permissions ? JSON.parse(u.custom_permissions) : {}; const newUser: AuthUser = { id: u.id, username: u.username, email: u.email || '', role, roleName: roleNameMap[role] || role, avatar: u.display_name?.charAt(0) || username.charAt(0).toUpperCase(), groupIds: u.group_ids || [], dataSourcePermissions, customPermissions, permissionsUpdatedAt: Date.now(), }; setUser(newUser); setIsLoggedIn(true); clearPermissionCache(); emitPermissionChange(newUser); return true; } catch { return false; } }, [clearPermissionCache, emitPermissionChange]); const logout = useCallback(() => { localStorage.removeItem('token'); setIsLoggedIn(false); clearPermissionCache(); setUser({ id: defaultUser.id, username: defaultUser.username, email: defaultUser.email, role: defaultUser.role, roleName: defaultUser.roleName, avatar: defaultUser.avatar, groupIds: [], dataSourcePermissions: [], customPermissions: {}, }); emitPermissionChange({} as AuthUser); }, [clearPermissionCache, emitPermissionChange]); const switchRole = useCallback((role: UserRole) => { setUser(prev => { const newUser: AuthUser = { ...prev, role, roleName: roleNameMap[role], username: role === 'admin' ? 'admin' : role === 'dba' ? 'zhangsan' : role === 'developer' ? 'wangwu' : 'visitor', email: `${role}@company.com`, avatar: role.charAt(0).toUpperCase(), }; clearPermissionCache(); emitPermissionChange(newUser); return newUser; }); }, [clearPermissionCache, emitPermissionChange]); /** 菜单权限检查(带缓存) */ const hasPermission = useCallback((menuId: string): boolean => { if (!isLoggedIn) return false; const cache = permissionCacheRef.current; if (isCacheValid() && cache.menu.has(menuId)) { return cache.menu.get(menuId)!; } const allowedRoles = routePermissionMap[menuId]; if (!allowedRoles) { cache.menu.set(menuId, false); return false; } const result = allowedRoles.includes(user.role); cache.menu.set(menuId, result); cache.timestamp = Date.now(); return result; }, [user.role, isLoggedIn, isCacheValid]); /** 操作权限检查(带缓存,支持自定义权限覆盖) */ const hasActionPermission = useCallback((action: PermissionAction | string): boolean => { if (!isLoggedIn) return false; const cache = permissionCacheRef.current; if (isCacheValid() && cache.action.has(action)) { return cache.action.get(action)!; } // 优先检查自定义权限 if (user.customPermissions && action in user.customPermissions) { const customResult = user.customPermissions[action]; cache.action.set(action, customResult); cache.timestamp = Date.now(); return customResult; } const allowedRoles = actionPermissionMap[action]; if (!allowedRoles) { cache.action.set(action, false); return false; } const result = allowedRoles.includes(user.role); cache.action.set(action, result); cache.timestamp = Date.now(); return result; }, [user.role, user.customPermissions, isLoggedIn, isCacheValid]); /** 数据源级权限检查 */ const hasDataSourcePermission = useCallback(( instanceId: number, action: 'read' | 'write' | 'delete' | 'export', database?: string, table?: string ): boolean => { if (!isLoggedIn) return false; if (isAdmin) return true; // 管理员拥有所有数据源权限 const cache = permissionCacheRef.current; const cacheKey = `${instanceId}:${action}:${database || '*'}:${table || '*'}`; if (isCacheValid() && cache.dataSource.has(cacheKey)) { return cache.dataSource.get(cacheKey)!; } const perms = user.dataSourcePermissions || []; if (perms.length === 0) { cache.dataSource.set(cacheKey, false); return false; } const hasPerm = perms.some((ds: DataSourcePermission) => { // 检查实例ID if (ds.instanceId !== instanceId) return false; // 检查操作权限 if (!ds.actions.includes(action)) return false; // 检查数据库级别 if (database && ds.database && ds.database !== database) return false; // 检查表级别 if (table && ds.tables && ds.tables.length > 0 && !ds.tables.includes(table)) return false; return true; }); cache.dataSource.set(cacheKey, hasPerm); cache.timestamp = Date.now(); return hasPerm; }, [user.dataSourcePermissions, user.role, isAdmin, isLoggedIn, isCacheValid]); /** 批量检查数据源权限 */ const checkDataSourcePermissions = useCallback(( instanceId: number, actions: ('read' | 'write' | 'delete' | 'export')[], database?: string, table?: string ): Record => { const result: Record = {}; for (const action of actions) { result[action] = hasDataSourcePermission(instanceId, action, database, table); } return result; }, [hasDataSourcePermission]); /** 从服务器刷新用户权限 */ const refreshPermissions = useCallback(async (): Promise => { try { const res = await authApi.getCurrentUser(); if (res.user) { const u = res.user; const role = u.role as UserRole; const dataSourcePermissions: DataSourcePermission[] = u.data_source_permissions ? JSON.parse(u.data_source_permissions) : []; const customPermissions: Record = u.custom_permissions ? JSON.parse(u.custom_permissions) : {}; const newUser: AuthUser = { ...user, id: u.id, username: u.username, email: u.email || '', role, roleName: roleNameMap[role] || role, groupIds: u.group_ids || [], dataSourcePermissions, customPermissions, permissionsUpdatedAt: Date.now(), }; setUser(newUser); clearPermissionCache(); emitPermissionChange(newUser); } } catch (error) { console.error('刷新权限失败:', error); } }, [user, clearPermissionCache, emitPermissionChange]); const visibleMenuItems = useMemo(() => { return menuItems.filter(item => item.roles.includes(user.role)); }, [user.role]); /** 检查是否拥有指定角色或以上级别权限 */ const hasRoleLevel = useCallback((minRole: UserRole): boolean => { return roleWeightMap[user.role] >= roleWeightMap[minRole]; }, [user.role]); const value = useMemo(() => ({ user, isLoggedIn, login, logout, switchRole, hasPermission, hasActionPermission, hasDataSourcePermission, checkDataSourcePermissions, refreshPermissions, clearPermissionCache, visibleMenuItems, isAdmin: user.role === 'admin', isDBA: user.role === 'dba', isDeveloper: user.role === 'developer', isGuest: user.role === 'guest', hasRoleLevel, }), [user, isLoggedIn, login, logout, switchRole, hasPermission, hasActionPermission, hasDataSourcePermission, checkDataSourcePermissions, refreshPermissions, clearPermissionCache, visibleMenuItems, hasRoleLevel]); return {children}; } export function useAuth(): AuthContextType { const ctx = useContext(AuthContext); if (!ctx) throw new Error('useAuth must be used within AuthProvider'); return ctx; } /** Hook: 检查操作权限(带缓存) */ export function useActionPermission(action: PermissionAction | string): boolean { const { hasActionPermission } = useAuth(); return hasActionPermission(action); } /** Hook: 检查数据源权限 */ export function useDataSourcePermission( instanceId: number, action: 'read' | 'write' | 'delete' | 'export', database?: string, table?: string ): boolean { const { hasDataSourcePermission } = useAuth(); return hasDataSourcePermission(instanceId, action, database, table); } /** Hook: 监听权限变更 */ export function usePermissionChange(callback: (user: AuthUser) => void): void { const { user } = useAuth(); useEffect(() => { const handleChange = (e: Event) => { const customEvent = e as CustomEvent; callback(customEvent.detail); }; window.addEventListener(PERMISSION_CHANGE_EVENT, handleChange); return () => window.removeEventListener(PERMISSION_CHANGE_EVENT, handleChange); }, [callback]); // 当user变化时也触发回调 useEffect(() => { if (user && user.id) { callback(user); } }, [user.id, user.permissionsUpdatedAt, callback]); } /** Hook: 检查角色级别 */ export function useRoleLevel(minRole: UserRole): boolean { const { hasRoleLevel } = useAuth(); return hasRoleLevel(minRole); }