feat: 分层 RBAC 权限管理系统

后端:
- 新增 Role / RolePermission 实体(自动 seed 系统角色)
- PermissionService——通过 isAdmin / TenantMember 链路解析用户权限
- @Permission() 装饰器 + PermissionsGuard 守卫
- /api/permissions 和 /api/roles REST API
- UserController 内联 role 检查迁移到 @Permission()
- PermissionModule 全局注册

前端:
- usePermissions hook——获取当前用户权限集
- PermissionGate 组件级门控
- PermissionSettingsView——角色列表+权限矩阵编辑页面
- SettingsView 新增「权限管理」Tab(仅 admin 可见)
- 权限预览(26 项,7 分类)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Developer
2026-06-08 23:25:22 +08:00
parent c57c3028e2
commit ba33d517c1
17 changed files with 1386 additions and 87 deletions
+19 -3
View File
@@ -52,6 +52,7 @@ import { userSettingService } from '../../services/userSettingService';
import { knowledgeGroupService } from '../../services/knowledgeGroupService';
import { apiClient } from '../../services/apiClient';
import { AssessmentTemplateManager } from './AssessmentTemplateManager';
import { PermissionSettingsView } from './PermissionSettingsView';
import { useConfirm } from '../../contexts/ConfirmContext';
import { useToast } from '../../contexts/ToastContext';
@@ -66,7 +67,7 @@ interface SettingsViewProps {
initialTab?: TabType;
}
type TabType = 'general' | 'user' | 'model' | 'tenants' | 'knowledge_base' | 'import_tasks' | 'assessment_templates';
type TabType = 'general' | 'user' | 'model' | 'tenants' | 'knowledge_base' | 'import_tasks' | 'assessment_templates' | 'permissions';
const buildTenantTree = (tenants: Tenant[]): Tenant[] => {
const map = new Map<string, Tenant>();
@@ -2131,6 +2132,16 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
{t('assessmentTemplates')}
</button>
)}
{isAdmin && (
<button
onClick={() => setActiveTab('permissions')}
className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-medium transition-all ${activeTab === 'permissions' ? 'bg-white text-indigo-600 shadow-sm border border-slate-200/60' : 'text-slate-600 hover:bg-slate-100'
}`}
>
<Shield size={18} />
</button>
)}
</div>
</div>
@@ -2139,10 +2150,10 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
<div className="px-8 pt-8 pb-6 flex items-start justify-between shrink-0">
<div>
<h1 className="text-2xl font-bold text-slate-900 leading-tight">
{activeTab === 'general' ? t('generalSettings') : activeTab === 'user' ? t('userManagement') : activeTab === 'model' ? t('modelManagement') : activeTab === 'knowledge_base' ? t('sidebarTitle') : activeTab === 'tenants' ? t('navTenants') : t('assessmentTemplates')}
{activeTab === 'general' ? t('generalSettings') : activeTab === 'user' ? t('userManagement') : activeTab === 'model' ? t('modelManagement') : activeTab === 'knowledge_base' ? t('sidebarTitle') : activeTab === 'tenants' ? t('navTenants') : activeTab === 'permissions' ? '权限管理' : t('assessmentTemplates')}
</h1>
<p className="text-[15px] text-slate-500 mt-1">
{activeTab === 'general' ? t('generalSettingsSubtitle') : activeTab === 'user' ? t('userManagementSubtitle') : activeTab === 'model' ? t('modelManagementSubtitle') : activeTab === 'knowledge_base' ? t('kbSettingsSubtitle') : activeTab === 'tenants' ? t('tenantsSubtitle') : t('assessmentTemplatesSubtitle')}
{activeTab === 'general' ? t('generalSettingsSubtitle') : activeTab === 'user' ? t('userManagementSubtitle') : activeTab === 'model' ? t('modelManagementSubtitle') : activeTab === 'knowledge_base' ? t('kbSettingsSubtitle') : activeTab === 'tenants' ? t('tenantsSubtitle') : activeTab === 'permissions' ? '管理角色和细粒度权限' : t('assessmentTemplatesSubtitle')}
</p>
</div>
</div>
@@ -2183,6 +2194,11 @@ export const SettingsView: React.FC<SettingsViewProps> = ({
<AssessmentTemplateManager />
</div>
)}
{activeTab === 'permissions' && isAdmin && (
<div className="flex-1 overflow-y-auto custom-scrollbar" style={{ height: 'calc(100vh - 220px)' }}>
<PermissionSettingsView />
</div>
)}
</motion.div>
</AnimatePresence>
</div>