Просмотр исходного кода

♻️ refactor(role): 重构角色权限系统架构

- 移除role表中的permissions字段,采用role_permission关联表存储权限关系
- 添加数据范围类型(data_scope_type)支持,实现更细粒度的权限控制
- 优化角色权限初始化SQL,使用关联表批量分配权限

✨ feat(role): 实现角色权限数量展示功能

- 添加角色权限数量查询接口
- 在角色管理页面显示各角色拥有的权限数量
- 优化权限数量加载状态显示

🐛 fix(role): 修复角色创建时权限数据处理问题

- 移除创建角色时强制清空permissions数组的逻辑
- 修复角色创建表单无法正确提交权限数据的问题
yourname 8 месяцев назад
Родитель
Сommit
6d4b658cce

+ 74 - 9
docs/crm-permission-init.sql

@@ -77,26 +77,91 @@ INSERT INTO permission (code, name, type, module, action, description, sort_orde
 
 
 -- 超级管理员角色
-INSERT INTO role (name, description, permissions, created_at, updated_at) VALUES
-('super_admin', '超级管理员,拥有系统所有权限', '["system:user:create","system:user:update","system:user:delete","system:user:view:all","system:role:create","system:role:update","system:role:delete","system:role:view","system:department:create","system:department:update","system:department:delete","system:department:view","client:create","client:update","client:delete","client:view:all","client:assign","client:transfer","contract:create","contract:update","contract:delete","contract:view:all","contract:approve","contract:renew","follow_up:create","follow_up:update","follow_up:delete","follow_up:view:all","order:create","order:update","order:delete","order:view:all","expense:create","expense:update","expense:delete","expense:view:all","file:upload","file:delete","file:view:all"]', NOW(), NOW()),
+INSERT INTO role (name, description, created_at, updated_at) VALUES
+('super_admin', '超级管理员,拥有系统所有权限', NOW(), NOW()),
 
 -- 系统管理员角色
-('admin', '系统管理员,管理用户、角色、部门等', '["system:user:create","system:user:update","system:user:delete","system:user:view:all","system:role:create","system:role:update","system:role:delete","system:role:view","system:department:create","system:department:update","system:department:delete","system:department:view"]', NOW(), NOW()),
+('admin', '系统管理员,管理用户、角色、部门等', NOW(), NOW()),
 
 -- 部门经理角色
-('department_manager', '部门经理,管理部门内所有数据', '["system:user:create","system:user:update","system:user:view:department","system:department:view","client:create","client:update","client:delete","client:view:department","client:assign","contract:create","contract:update","contract:delete","contract:view:department","contract:approve","follow_up:create","follow_up:update","follow_up:delete","follow_up:view:department","order:create","order:update","order:delete","order:view:department","expense:create","expense:update","expense:delete","expense:view:department","file:upload","file:delete","file:view:department"]', NOW(), NOW()),
+('department_manager', '部门经理,管理部门内所有数据', NOW(), NOW()),
 
 -- 销售经理角色
-('sales_manager', '销售经理,管理客户和销售数据', '["client:create","client:update","client:delete","client:view:sub_department","client:assign","contract:create","contract:update","contract:delete","contract:view:sub_department","contract:approve","follow_up:create","follow_up:update","follow_up:delete","follow_up:view:sub_department","order:create","order:update","order:delete","order:view:sub_department","expense:create","expense:update","expense:delete","expense:view:sub_department","file:upload","file:view:sub_department"]', NOW(), NOW()),
+('sales_manager', '销售经理,管理客户和销售数据', NOW(), NOW()),
 
 -- 销售人员角色
-('sales_person', '销售人员,管理个人客户和数据', '["client:create","client:update","client:view:own","contract:create","contract:update","contract:view:own","follow_up:create","follow_up:update","follow_up:view:own","order:create","order:update","order:view:own","expense:create","expense:update","expense:view:own","file:upload","file:view:own"]', NOW(), NOW()),
+('sales_person', '销售人员,管理个人客户和数据', NOW(), NOW()),
 
 -- 财务角色
-('accountant', '财务人员,管理费用和财务数据', '["contract:view:all","order:view:all","expense:create","expense:update","expense:delete","expense:view:all","file:upload","file:view:all"]', NOW(), NOW()),
+('accountant', '财务人员,管理费用和财务数据', NOW(), NOW()),
 
 -- 合同管理员角色
-('contract_admin', '合同管理员,管理合同和续签', '["contract:create","contract:update","contract:delete","contract:view:all","contract:approve","contract:renew","file:upload","file:view:all"]', NOW(), NOW()),
+('contract_admin', '合同管理员,管理合同和续签', NOW(), NOW()),
 
 -- 访客角色(只读权限)
-('guest', '访客,只读权限', '["system:user:view:own","client:view:own","contract:view:own","follow_up:view:own","order:view:own","expense:view:own","file:view:own"]', NOW(), NOW());
+('guest', '访客,只读权限', NOW(), NOW());
+
+-- 为超级管理员角色添加所有权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'COMPANY', NOW()
+FROM role r, permission p
+WHERE r.name = 'super_admin';
+
+-- 为系统管理员角色添加系统管理权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'COMPANY', NOW()
+FROM role r, permission p
+WHERE r.name = 'admin' AND p.code LIKE 'system:%';
+
+-- 为部门经理角色添加相应权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'DEPARTMENT', NOW()
+FROM role r, permission p
+WHERE r.name = 'department_manager'
+  AND (p.code LIKE 'system:%' OR p.code LIKE 'client:%' OR p.code LIKE 'contract:%'
+       OR p.code LIKE 'follow_up:%' OR p.code LIKE 'order:%' OR p.code LIKE 'expense:%'
+       OR p.code LIKE 'file:%')
+  AND p.code NOT LIKE '%:all';
+
+-- 为销售经理角色添加相应权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'SUB_DEPARTMENT', NOW()
+FROM role r, permission p
+WHERE r.name = 'sales_manager'
+  AND (p.code LIKE 'client:%' OR p.code LIKE 'contract:%' OR p.code LIKE 'follow_up:%'
+       OR p.code LIKE 'order:%' OR p.code LIKE 'expense:%' OR p.code LIKE 'file:%')
+  AND p.code NOT LIKE '%:all';
+
+-- 为销售人员角色添加相应权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'PERSONAL', NOW()
+FROM role r, permission p
+WHERE r.name = 'sales_person'
+  AND (p.code LIKE 'client:%' OR p.code LIKE 'contract:%' OR p.code LIKE 'follow_up:%'
+       OR p.code LIKE 'order:%' OR p.code LIKE 'expense:%' OR p.code LIKE 'file:%')
+  AND p.code NOT LIKE '%:all';
+
+-- 为财务角色添加相应权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'COMPANY', NOW()
+FROM role r, permission p
+WHERE r.name = 'accountant'
+  AND (p.code LIKE 'contract:view:%' OR p.code LIKE 'order:view:%'
+       OR p.code LIKE 'expense:%' OR p.code LIKE 'file:%');
+
+-- 为合同管理员角色添加相应权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'COMPANY', NOW()
+FROM role r, permission p
+WHERE r.name = 'contract_admin'
+  AND (p.code LIKE 'contract:%' OR p.code LIKE 'file:%');
+
+-- 为访客角色添加相应权限
+INSERT INTO role_permission (role_id, permission_id, data_scope_type, created_at)
+SELECT r.id, p.id, 'PERSONAL', NOW()
+FROM role r, permission p
+WHERE r.name = 'guest'
+  AND (p.code LIKE 'system:user:view:own' OR p.code LIKE 'client:view:own'
+       OR p.code LIKE 'contract:view:own' OR p.code LIKE 'follow_up:view:own'
+       OR p.code LIKE 'order:view:own' OR p.code LIKE 'expense:view:own'
+       OR p.code LIKE 'file:view:own');

+ 28 - 4
src/client/admin/pages/Roles.tsx

@@ -67,7 +67,7 @@ const useCreateRole = () => {
   return useMutation({
     mutationFn: async (data: CreateRoleRequest) => {
       const response = await roleClient.$post({
-        json: { ...data, permissions: [] as string[] }
+        json: data
       });
       if (!response.ok) throw new Error('创建角色失败');
       return response.json();
@@ -173,6 +173,22 @@ const roles = rolesData?.data || [];
 const { data: permissions = [], isLoading: permissionsLoading } = usePermissions();
 const { data: rolePermissions = [], isLoading: rolePermissionsLoading } = useRolePermissions(selectedRole?.id ?? null);
 
+// 单独查询每个角色的权限数量
+const useRolePermissionCount = (roleId: number) => {
+  return useQuery({
+    queryKey: ['role-permission-count', roleId],
+    queryFn: async () => {
+      const response = await rolePermissionClient.$get({
+        query: { roleId }
+      });
+      if (!response.ok) throw new Error('获取权限数量失败');
+      const data = await response.json();
+      return data.data.length;
+    },
+    enabled: !!roleId,
+  });
+};
+
 // 更新分页信息
 React.useEffect(() => {
   if (rolesData?.pagination) {
@@ -318,9 +334,17 @@ React.useEffect(() => {
       title: '权限数量',
       key: 'permissionCount',
       width: 100,
-      render: (_: any, record: RoleItem) => (
-        <Tag color="blue">{record.permissions?.length || 0}</Tag>
-      ),
+      render: (_: any, record: RoleItem) => {
+        const RolePermissionCount = ({ roleId }: { roleId: number }) => {
+          const { data: count = 0, isLoading } = useRolePermissionCount(roleId);
+          return (
+            <Tag color="blue" loading={isLoading}>
+              {count}
+            </Tag>
+          );
+        };
+        return <RolePermissionCount roleId={record.id} />;
+      },
     },
     {
       title: '创建时间',

+ 0 - 10
src/server/modules/users/role.entity.ts

@@ -16,10 +16,6 @@ export const RoleSchema = z.object({
     description: '角色描述',
     example: '系统管理员角色'
   }),
-  permissions: z.array(z.string()).min(1).openapi({
-    description: '角色权限列表',
-    example: ['user:create', 'user:delete']
-  }),
   createdAt: z.date().openapi({ description: '创建时间' }),
   updatedAt: z.date().openapi({ description: '更新时间' })
 });
@@ -38,9 +34,6 @@ export class Role {
   @Column({ type: 'text', nullable: true })
   description!: string | null;
 
-  @Column({ type: 'simple-array', nullable: false })
-  permissions: Permission[] = [];
-
   @CreateDateColumn({ name: 'created_at', type: 'timestamp' })
   createdAt!: Date;
 
@@ -49,8 +42,5 @@ export class Role {
 
   constructor(partial?: Partial<Role>) {
     Object.assign(this, partial);
-    if (!this.permissions) {
-      this.permissions = [];
-    }
   }
 }