847f7b2b5a
fix: 1. 修复交叉评查中无法高亮文档的问题。
322 lines
8.9 KiB
TypeScript
322 lines
8.9 KiB
TypeScript
/**
|
|
* 权限检查Hook
|
|
*
|
|
* 基于RBAC(基于角色的访问控制)模型,提供细粒度的权限检查功能。
|
|
*
|
|
* 权限键格式:module:resource:action
|
|
* 例如:prompt_template:create:write
|
|
*
|
|
* 使用示例:
|
|
* ```typescript
|
|
* const { hasPermission, canCreate, canEdit } = usePermission();
|
|
*
|
|
* // 检查单个权限
|
|
* if (hasPermission('prompt_template:create:write')) {
|
|
* // 显示创建按钮
|
|
* }
|
|
*
|
|
* // 使用便捷方法
|
|
* if (canCreate('prompt_template')) {
|
|
* // 显示创建按钮
|
|
* }
|
|
* ```
|
|
*/
|
|
|
|
import { useRouteLoaderData, useLocation } from "@remix-run/react";
|
|
|
|
interface RootLoaderData {
|
|
permissions?: string[];
|
|
permissionMap?: Record<string, string[]>; // ✅ 新增:权限映射表
|
|
userRole: string;
|
|
userInfo?: {
|
|
role_id?: number;
|
|
role_key?: string;
|
|
role_name?: string;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 交叉评查模块默认权限配置
|
|
* 当 permissionMap 中没有配置时,使用此默认配置
|
|
*/
|
|
const CROSS_CHECKING_DEFAULT_PERMISSIONS: Record<string, string[]> = {
|
|
'/cross-checking': [
|
|
'cross_review:task:read',
|
|
// 'cross_review:task:create',
|
|
// 'cross_review:document:complete',
|
|
'cross_review:progress:view',
|
|
'cross_review:proposal:create',
|
|
'cross_review:proposal:delete',
|
|
'cross_review:proposal:read',
|
|
// 'cross_review:proposal:vote'
|
|
],
|
|
'/cross-checking/upload': [
|
|
// 'cross_review:task:create'
|
|
],
|
|
'/cross-checking/result': [
|
|
// 'cross_review:document:complete',
|
|
'cross_review:progress:view',
|
|
'cross_review:proposal:create',
|
|
'cross_review:proposal:delete',
|
|
'cross_review:proposal:read',
|
|
// 'cross_review:proposal:vote'
|
|
]
|
|
};
|
|
|
|
export function usePermission() {
|
|
const rootData = useRouteLoaderData("root") as RootLoaderData;
|
|
const location = useLocation();
|
|
|
|
// 从root loader获取权限映射表
|
|
const permissionMap = rootData?.permissionMap || {};
|
|
const userRole = rootData?.userRole || 'common';
|
|
|
|
// 🔑 根据当前路由获取权限列表
|
|
const currentPath = location.pathname;
|
|
// console.log('currentPath', currentPath)
|
|
|
|
// 获取当前路由的权限:优先使用 permissionMap,否则使用交叉评查默认配置
|
|
const getCrossCheckingPermissions = (): string[] => {
|
|
// 检查是否是交叉评查相关路由
|
|
if (currentPath.startsWith('/cross-checking')) {
|
|
// 精确匹配
|
|
if (CROSS_CHECKING_DEFAULT_PERMISSIONS[currentPath]) {
|
|
return CROSS_CHECKING_DEFAULT_PERMISSIONS[currentPath];
|
|
}
|
|
// 处理带参数的路由,如 /cross-checking/result?id=xxx
|
|
const basePath = currentPath.split('?')[0];
|
|
if (CROSS_CHECKING_DEFAULT_PERMISSIONS[basePath]) {
|
|
return CROSS_CHECKING_DEFAULT_PERMISSIONS[basePath];
|
|
}
|
|
}
|
|
return [];
|
|
};
|
|
|
|
// 优先使用 permissionMap 中的权限,如果没有则使用交叉评查默认权限
|
|
const currentPermissions = permissionMap[currentPath]?.length > 0
|
|
? permissionMap[currentPath]
|
|
: getCrossCheckingPermissions();
|
|
|
|
// 向后兼容:如果存在旧的permissions数组,也要支持
|
|
const legacyPermissions = rootData?.permissions || [];
|
|
|
|
/**
|
|
* 检查是否有指定权限
|
|
* @param permissionKey 权限键,如 "prompt_template:create:write"
|
|
* @returns boolean
|
|
*/
|
|
const hasPermission = (permissionKey: string): boolean => {
|
|
// 优先使用当前路由的权限列表
|
|
if (currentPermissions.length > 0) {
|
|
return currentPermissions.includes(permissionKey);
|
|
}
|
|
|
|
// 向后兼容:支持旧的permissions数组
|
|
if (legacyPermissions.length > 0) {
|
|
return legacyPermissions.includes(permissionKey);
|
|
}
|
|
|
|
// 降级方案:如果没有权限数据,使用userRole判断(兼容现有系统)
|
|
// 包含'provin'的角色拥有所有权限
|
|
if (userRole.toLowerCase().includes('provin')) {
|
|
return true;
|
|
}
|
|
|
|
// 默认只有查看权限
|
|
if (permissionKey.includes(':read')) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* 检查是否有指定路由的权限
|
|
* @param path 路由路径,如 "/prompts"
|
|
* @param permissionKey 权限键
|
|
* @returns boolean
|
|
*/
|
|
const hasRoutePermission = (path: string, permissionKey: string): boolean => {
|
|
const routePermissions = permissionMap[path] || [];
|
|
return routePermissions.includes(permissionKey);
|
|
};
|
|
|
|
/**
|
|
* 获取当前路由的所有权限
|
|
* @returns 权限列表
|
|
*/
|
|
const getCurrentPermissions = (): string[] => {
|
|
return currentPermissions;
|
|
};
|
|
|
|
/**
|
|
* 获取指定路由的所有权限
|
|
* @param path 路由路径
|
|
* @returns 权限列表
|
|
*/
|
|
const getRoutePermissions = (path: string): string[] => {
|
|
return permissionMap[path] || [];
|
|
};
|
|
|
|
/**
|
|
* 检查是否有指定模块的任意权限
|
|
* @param module 模块名,如 "prompt_template"
|
|
* @returns boolean
|
|
*/
|
|
const hasModulePermission = (module: string): boolean => {
|
|
if (currentPermissions.length > 0) {
|
|
return currentPermissions.some(p => p.startsWith(`${module}:`));
|
|
}
|
|
|
|
if (legacyPermissions.length > 0) {
|
|
return legacyPermissions.some(p => p.startsWith(`${module}:`));
|
|
}
|
|
|
|
// 降级方案
|
|
return userRole.toLowerCase().includes('provin');
|
|
};
|
|
|
|
/**
|
|
* 检查是否有指定资源和动作的权限
|
|
* @param module 模块名,如 "prompt_template"
|
|
* @param resource 资源名,如 "create", "list", "detail"
|
|
* @param action 动作,如 "read", "write", "delete"
|
|
* @returns boolean
|
|
*/
|
|
const hasResourcePermission = (module: string, resource: string, action: string): boolean => {
|
|
const permissionKey = `${module}:${resource}:${action}`;
|
|
return hasPermission(permissionKey);
|
|
};
|
|
|
|
/**
|
|
* 批量检查权限(需要全部满足)
|
|
* @param permissionKeys 权限键数组
|
|
* @returns boolean
|
|
*/
|
|
const hasAllPermissions = (permissionKeys: string[]): boolean => {
|
|
return permissionKeys.every(key => hasPermission(key));
|
|
};
|
|
|
|
/**
|
|
* 批量检查权限(满足任意一个即可)
|
|
* @param permissionKeys 权限键数组
|
|
* @returns boolean
|
|
*/
|
|
const hasAnyPermission = (permissionKeys: string[]): boolean => {
|
|
return permissionKeys.some(key => hasPermission(key));
|
|
};
|
|
|
|
// 便捷方法:检查常见操作权限
|
|
const canCreate = (module: string): boolean => {
|
|
return hasResourcePermission(module, 'create', 'write');
|
|
};
|
|
|
|
const canRead = (module: string, resource: string = 'list'): boolean => {
|
|
return hasResourcePermission(module, resource, 'read');
|
|
};
|
|
|
|
const canUpdate = (module: string): boolean => {
|
|
return hasResourcePermission(module, 'update', 'write');
|
|
};
|
|
|
|
const canDelete = (module: string): boolean => {
|
|
return hasResourcePermission(module, 'delete', 'delete');
|
|
};
|
|
|
|
const canList = (module: string): boolean => {
|
|
return hasResourcePermission(module, 'list', 'read');
|
|
};
|
|
|
|
const canView = (module: string): boolean => {
|
|
return hasResourcePermission(module, 'detail', 'read');
|
|
};
|
|
|
|
/**
|
|
* 检查是否有批量操作权限
|
|
* @param module 模块名,如 "evaluation_group"
|
|
* @returns boolean - 检查是否有 module:batch:write 权限
|
|
*/
|
|
const canBatch = (module: string): boolean => {
|
|
return hasResourcePermission(module, 'batch', 'write');
|
|
};
|
|
|
|
return {
|
|
// 原始权限数据
|
|
permissions: currentPermissions, // ✅ 返回当前路由的权限
|
|
permissionMap, // ✅ 返回完整的权限映射表
|
|
userRole,
|
|
|
|
// 基础检查方法
|
|
hasPermission,
|
|
hasModulePermission,
|
|
hasResourcePermission,
|
|
hasAllPermissions,
|
|
hasAnyPermission,
|
|
|
|
// ✅ 新增:路由权限查询方法
|
|
hasRoutePermission,
|
|
getCurrentPermissions,
|
|
getRoutePermissions,
|
|
|
|
// 便捷方法
|
|
canCreate,
|
|
canRead,
|
|
canUpdate,
|
|
canDelete,
|
|
canList,
|
|
canView,
|
|
canBatch // ✅ 新增:批量操作权限检查
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 权限组件包装器
|
|
*
|
|
* 根据权限控制子组件的显示/隐藏
|
|
*
|
|
* 使用示例:
|
|
* ```typescript
|
|
* <PermissionGuard permission="prompt_template:create:write">
|
|
* <Button>新增模板</Button>
|
|
* </PermissionGuard>
|
|
* ```
|
|
*/
|
|
interface PermissionGuardProps {
|
|
permission?: string;
|
|
permissions?: string[];
|
|
requireAll?: boolean; // true=需要全部权限,false=任意一个即可
|
|
fallback?: React.ReactNode; // 无权限时显示的内容
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export function PermissionGuard({
|
|
permission,
|
|
permissions: permissionList,
|
|
requireAll = false,
|
|
fallback = null,
|
|
children
|
|
}: PermissionGuardProps) {
|
|
const { hasPermission, hasAllPermissions, hasAnyPermission } = usePermission();
|
|
|
|
let hasAccess = false;
|
|
|
|
if (permission) {
|
|
// 单个权限检查
|
|
hasAccess = hasPermission(permission);
|
|
} else if (permissionList && permissionList.length > 0) {
|
|
// 多个权限检查
|
|
hasAccess = requireAll
|
|
? hasAllPermissions(permissionList)
|
|
: hasAnyPermission(permissionList);
|
|
} else {
|
|
// 没有指定权限,默认允许访问
|
|
hasAccess = true;
|
|
}
|
|
|
|
if (!hasAccess) {
|
|
return <>{fallback}</>;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|