|
@@ -0,0 +1,254 @@
|
|
|
|
|
+import { Entity, PrimaryGeneratedColumn, Column, OneToMany, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
|
|
|
|
+import { z } from '@hono/zod-openapi';
|
|
|
|
|
+
|
|
|
|
|
+export enum PermissionType {
|
|
|
|
|
+ MODULE = 'module', // 模块权限
|
|
|
|
|
+ OPERATION = 'operation', // 操作权限
|
|
|
|
|
+ DATA = 'data' // 数据权限
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export enum DataScopeType {
|
|
|
|
|
+ PERSONAL = 'personal', // 仅个人数据
|
|
|
|
|
+ DEPARTMENT = 'department', // 部门数据
|
|
|
|
|
+ SUB_DEPARTMENT = 'sub_department', // 部门及下级数据
|
|
|
|
|
+ COMPANY = 'company', // 全公司数据
|
|
|
|
|
+ CUSTOM = 'custom' // 自定义范围
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@Entity('permission')
|
|
|
|
|
+export class Permission {
|
|
|
|
|
+ @PrimaryGeneratedColumn({ unsigned: true })
|
|
|
|
|
+ id!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'code', type: 'varchar', length: 100, unique: true, comment: '权限编码' })
|
|
|
|
|
+ code!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'name', type: 'varchar', length: 100, comment: '权限名称' })
|
|
|
|
|
+ name!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'type', type: 'enum', enum: PermissionType, comment: '权限类型' })
|
|
|
|
|
+ type!: PermissionType;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'module', type: 'varchar', length: 50, comment: '所属模块' })
|
|
|
|
|
+ module!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'action', type: 'varchar', length: 50, comment: '操作类型' })
|
|
|
|
|
+ action!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'description', type: 'text', nullable: true, comment: '权限描述' })
|
|
|
|
|
+ description?: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'sort_order', type: 'int', default: 0, comment: '排序' })
|
|
|
|
|
+ sortOrder!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'is_active', type: 'tinyint', default: 1, comment: '是否启用(0:禁用,1:启用)' })
|
|
|
|
|
+ isActive!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'parent_id', type: 'int', nullable: true, comment: '父权限ID' })
|
|
|
|
|
+ parentId?: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'config', type: 'json', nullable: true, comment: '权限配置' })
|
|
|
|
|
+ config?: any;
|
|
|
|
|
+
|
|
|
|
|
+ @CreateDateColumn({ name: 'created_at', type: 'timestamp' })
|
|
|
|
|
+ createdAt!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ @UpdateDateColumn({ name: 'updated_at', type: 'timestamp' })
|
|
|
|
|
+ updatedAt!: Date;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@Entity('role_permission')
|
|
|
|
|
+export class RolePermission {
|
|
|
|
|
+ @PrimaryGeneratedColumn({ unsigned: true })
|
|
|
|
|
+ id!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'role_id', type: 'int', unsigned: true, comment: '角色ID' })
|
|
|
|
|
+ roleId!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'permission_id', type: 'int', unsigned: true, comment: '权限ID' })
|
|
|
|
|
+ permissionId!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'data_scope_type', type: 'enum', enum: DataScopeType, default: DataScopeType.PERSONAL, comment: '数据范围类型' })
|
|
|
|
|
+ dataScopeType!: DataScopeType;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({ name: 'custom_departments', type: 'json', nullable: true, comment: '自定义部门范围' })
|
|
|
|
|
+ customDepartments?: number[];
|
|
|
|
|
+
|
|
|
|
|
+ @CreateDateColumn({ name: 'created_at', type: 'timestamp' })
|
|
|
|
|
+ createdAt!: Date;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 预定义权限列表
|
|
|
|
|
+export const PERMISSIONS = {
|
|
|
|
|
+ // 系统管理权限
|
|
|
|
|
+ SYSTEM: {
|
|
|
|
|
+ USER: {
|
|
|
|
|
+ CREATE: 'system:user:create',
|
|
|
|
|
+ UPDATE: 'system:user:update',
|
|
|
|
|
+ DELETE: 'system:user:delete',
|
|
|
|
|
+ VIEW: {
|
|
|
|
|
+ OWN: 'system:user:view:own',
|
|
|
|
|
+ DEPARTMENT: 'system:user:view:department',
|
|
|
|
|
+ SUB_DEPARTMENT: 'system:user:view:sub_department',
|
|
|
|
|
+ ALL: 'system:user:view:all'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ ROLE: {
|
|
|
|
|
+ CREATE: 'system:role:create',
|
|
|
|
|
+ UPDATE: 'system:role:update',
|
|
|
|
|
+ DELETE: 'system:role:delete',
|
|
|
|
|
+ VIEW: 'system:role:view'
|
|
|
|
|
+ },
|
|
|
|
|
+ DEPARTMENT: {
|
|
|
|
|
+ CREATE: 'system:department:create',
|
|
|
|
|
+ UPDATE: 'system:department:update',
|
|
|
|
|
+ DELETE: 'system:department:delete',
|
|
|
|
|
+ VIEW: 'system:department:view'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 客户管理权限
|
|
|
|
|
+ CLIENT: {
|
|
|
|
|
+ CREATE: 'client:create',
|
|
|
|
|
+ UPDATE: 'client:update',
|
|
|
|
|
+ DELETE: 'client:delete',
|
|
|
|
|
+ VIEW: {
|
|
|
|
|
+ OWN: 'client:view:own',
|
|
|
|
|
+ DEPARTMENT: 'client:view:department',
|
|
|
|
|
+ SUB_DEPARTMENT: 'client:view:sub_department',
|
|
|
|
|
+ ALL: 'client:view:all'
|
|
|
|
|
+ },
|
|
|
|
|
+ ASSIGN: 'client:assign',
|
|
|
|
|
+ TRANSFER: 'client:transfer'
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 合同管理权限
|
|
|
|
|
+ CONTRACT: {
|
|
|
|
|
+ CREATE: 'contract:create',
|
|
|
|
|
+ UPDATE: 'contract:update',
|
|
|
|
|
+ DELETE: 'contract:delete',
|
|
|
|
|
+ VIEW: {
|
|
|
|
|
+ OWN: 'contract:view:own',
|
|
|
|
|
+ DEPARTMENT: 'contract:view:department',
|
|
|
|
|
+ SUB_DEPARTMENT: 'contract:view:sub_department',
|
|
|
|
|
+ ALL: 'contract:view:all'
|
|
|
|
|
+ },
|
|
|
|
|
+ APPROVE: 'contract:approve',
|
|
|
|
|
+ RENEW: 'contract:renew'
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 跟进记录权限
|
|
|
|
|
+ FOLLOW_UP: {
|
|
|
|
|
+ CREATE: 'follow_up:create',
|
|
|
|
|
+ UPDATE: 'follow_up:update',
|
|
|
|
|
+ DELETE: 'follow_up:delete',
|
|
|
|
|
+ VIEW: {
|
|
|
|
|
+ OWN: 'follow_up:view:own',
|
|
|
|
|
+ DEPARTMENT: 'follow_up:view:department',
|
|
|
|
|
+ SUB_DEPARTMENT: 'follow_up:view:sub_department',
|
|
|
|
|
+ ALL: 'follow_up:view:all'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 订单管理权限
|
|
|
|
|
+ ORDER: {
|
|
|
|
|
+ CREATE: 'order:create',
|
|
|
|
|
+ UPDATE: 'order:update',
|
|
|
|
|
+ DELETE: 'order:delete',
|
|
|
|
|
+ VIEW: {
|
|
|
|
|
+ OWN: 'order:view:own',
|
|
|
|
|
+ DEPARTMENT: 'order:view:department',
|
|
|
|
|
+ SUB_DEPARTMENT: 'order:view:sub_department',
|
|
|
|
|
+ ALL: 'order:view:all'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 费用管理权限
|
|
|
|
|
+ EXPENSE: {
|
|
|
|
|
+ CREATE: 'expense:create',
|
|
|
|
|
+ UPDATE: 'expense:update',
|
|
|
|
|
+ DELETE: 'expense:delete',
|
|
|
|
|
+ VIEW: {
|
|
|
|
|
+ OWN: 'expense:view:own',
|
|
|
|
|
+ DEPARTMENT: 'expense:view:department',
|
|
|
|
|
+ SUB_DEPARTMENT: 'expense:view:sub_department',
|
|
|
|
|
+ ALL: 'expense:view:all'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 文件管理权限
|
|
|
|
|
+ FILE: {
|
|
|
|
|
+ UPLOAD: 'file:upload',
|
|
|
|
|
+ DELETE: 'file:delete',
|
|
|
|
|
+ VIEW: {
|
|
|
|
|
+ OWN: 'file:view:own',
|
|
|
|
|
+ DEPARTMENT: 'file:view:department',
|
|
|
|
|
+ SUB_DEPARTMENT: 'file:view:sub_department',
|
|
|
|
|
+ ALL: 'file:view:all'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// Zod schemas
|
|
|
|
|
+export const PermissionSchema = z.object({
|
|
|
|
|
+ id: z.number().int().positive().openapi({ description: '权限ID' }),
|
|
|
|
|
+ code: z.string().max(100).openapi({ description: '权限编码', example: 'client:create' }),
|
|
|
|
|
+ name: z.string().max(100).openapi({ description: '权限名称', example: '创建客户' }),
|
|
|
|
|
+ type: z.enum([PermissionType.MODULE, PermissionType.OPERATION, PermissionType.DATA]).openapi({ description: '权限类型', example: PermissionType.OPERATION }),
|
|
|
|
|
+ module: z.string().max(50).openapi({ description: '所属模块', example: 'client' }),
|
|
|
|
|
+ action: z.string().max(50).openapi({ description: '操作类型', example: 'create' }),
|
|
|
|
|
+ description: z.string().max(500).optional().openapi({ description: '权限描述', example: '允许创建新客户' }),
|
|
|
|
|
+ sortOrder: z.number().int().default(0).openapi({ description: '排序', example: 0 }),
|
|
|
|
|
+ isActive: z.number().int().min(0).max(1).default(1).openapi({ description: '是否启用', example: 1 }),
|
|
|
|
|
+ parentId: z.number().int().positive().optional().openapi({ description: '父权限ID', example: 1 }),
|
|
|
|
|
+ config: z.any().optional().openapi({ description: '权限配置' }),
|
|
|
|
|
+ createdAt: z.date().openapi({ description: '创建时间' }),
|
|
|
|
|
+ updatedAt: z.date().openapi({ description: '更新时间' })
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+export const CreatePermissionDto = z.object({
|
|
|
|
|
+ code: z.string().max(100).openapi({ description: '权限编码', example: 'client:create' }),
|
|
|
|
|
+ name: z.string().max(100).openapi({ description: '权限名称', example: '创建客户' }),
|
|
|
|
|
+ type: z.enum([PermissionType.MODULE, PermissionType.OPERATION, PermissionType.DATA]).openapi({ description: '权限类型', example: PermissionType.OPERATION }),
|
|
|
|
|
+ module: z.string().max(50).openapi({ description: '所属模块', example: 'client' }),
|
|
|
|
|
+ action: z.string().max(50).openapi({ description: '操作类型', example: 'create' }),
|
|
|
|
|
+ description: z.string().max(500).optional().openapi({ description: '权限描述', example: '允许创建新客户' }),
|
|
|
|
|
+ sortOrder: z.number().int().optional().default(0).openapi({ description: '排序', example: 0 }),
|
|
|
|
|
+ isActive: z.number().int().min(0).max(1).optional().default(1).openapi({ description: '是否启用', example: 1 }),
|
|
|
|
|
+ parentId: z.number().int().positive().optional().openapi({ description: '父权限ID', example: 1 }),
|
|
|
|
|
+ config: z.any().optional().openapi({ description: '权限配置' })
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+export const UpdatePermissionDto = z.object({
|
|
|
|
|
+ code: z.string().max(100).optional().openapi({ description: '权限编码', example: 'client:create' }),
|
|
|
|
|
+ name: z.string().max(100).optional().openapi({ description: '权限名称', example: '创建客户' }),
|
|
|
|
|
+ type: z.enum([PermissionType.MODULE, PermissionType.OPERATION, PermissionType.DATA]).optional().openapi({ description: '权限类型', example: PermissionType.OPERATION }),
|
|
|
|
|
+ module: z.string().max(50).optional().openapi({ description: '所属模块', example: 'client' }),
|
|
|
|
|
+ action: z.string().max(50).optional().openapi({ description: '操作类型', example: 'create' }),
|
|
|
|
|
+ description: z.string().max(500).optional().openapi({ description: '权限描述', example: '允许创建新客户' }),
|
|
|
|
|
+ sortOrder: z.number().int().optional().openapi({ description: '排序', example: 0 }),
|
|
|
|
|
+ isActive: z.number().int().min(0).max(1).optional().openapi({ description: '是否启用', example: 1 }),
|
|
|
|
|
+ parentId: z.number().int().positive().optional().openapi({ description: '父权限ID', example: 1 }),
|
|
|
|
|
+ config: z.any().optional().openapi({ description: '权限配置' })
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+export const RolePermissionSchema = z.object({
|
|
|
|
|
+ id: z.number().int().positive().openapi({ description: '关联ID' }),
|
|
|
|
|
+ roleId: z.number().int().positive().openapi({ description: '角色ID', example: 1 }),
|
|
|
|
|
+ permissionId: z.number().int().positive().openapi({ description: '权限ID', example: 1 }),
|
|
|
|
|
+ dataScopeType: z.enum([DataScopeType.PERSONAL, DataScopeType.DEPARTMENT, DataScopeType.SUB_DEPARTMENT, DataScopeType.COMPANY, DataScopeType.CUSTOM]).default(DataScopeType.PERSONAL).openapi({ description: '数据范围类型', example: DataScopeType.PERSONAL }),
|
|
|
|
|
+ customDepartments: z.array(z.number().int().positive()).optional().openapi({ description: '自定义部门范围', example: [1, 2, 3] }),
|
|
|
|
|
+ createdAt: z.date().openapi({ description: '创建时间' })
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+export const CreateRolePermissionDto = z.object({
|
|
|
|
|
+ roleId: z.number().int().positive().openapi({ description: '角色ID', example: 1 }),
|
|
|
|
|
+ permissionId: z.number().int().positive().openapi({ description: '权限ID', example: 1 }),
|
|
|
|
|
+ dataScopeType: z.enum([DataScopeType.PERSONAL, DataScopeType.DEPARTMENT, DataScopeType.SUB_DEPARTMENT, DataScopeType.COMPANY, DataScopeType.CUSTOM]).optional().default(DataScopeType.PERSONAL).openapi({ description: '数据范围类型', example: DataScopeType.PERSONAL }),
|
|
|
|
|
+ customDepartments: z.array(z.number().int().positive()).optional().openapi({ description: '自定义部门范围', example: [1, 2, 3] })
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+export const UpdateRolePermissionDto = z.object({
|
|
|
|
|
+ dataScopeType: z.enum([DataScopeType.PERSONAL, DataScopeType.DEPARTMENT, DataScopeType.SUB_DEPARTMENT, DataScopeType.COMPANY, DataScopeType.CUSTOM]).optional().openapi({ description: '数据范围类型', example: DataScopeType.PERSONAL }),
|
|
|
|
|
+ customDepartments: z.array(z.number().int().positive()).optional().openapi({ description: '自定义部门范围', example: [1, 2, 3] })
|
|
|
|
|
+});
|