瀏覽代碼

✨ feat(crud): 实现数据权限控制类型定义和配置接口

- 创建 `DataPermissionOptions` 接口定义数据权限配置
- 扩展现有的 `CrudOptions` 接口,添加 `dataPermission` 配置项
- 实现权限配置验证逻辑 `validateDataPermissionOptions`
- 添加权限相关类型 `PermissionError` 和 `PermissionResult`
- 标记文档中对应任务为已完成

♻️ refactor(crud): 优化服务导入和配置处理

- 在 `generic-crud.service.ts` 中添加数据权限配置处理逻辑
- 更新服务索引文件,导出新的数据权限相关类型
- 完善代码注释,提高可维护性
yourname 1 月之前
父節點
當前提交
25f347e455

+ 13 - 4
docs/stories/006.001.shared-crud-data-permission.story.md

@@ -19,10 +19,10 @@ Draft
 8. 完整的单元测试和集成测试
 
 ## Tasks / Subtasks
-- [ ] 实现数据权限控制类型定义和配置接口 (AC: 1)
-  - [ ] 创建 `DataPermissionOptions` 接口定义
-  - [ ] 扩展现有的 `CrudOptions` 接口,添加 `dataPermission` 配置
-  - [ ] 实现权限配置验证逻辑
+- [x] 实现数据权限控制类型定义和配置接口 (AC: 1)
+  - [x] 创建 `DataPermissionOptions` 接口定义
+  - [x] 扩展现有的 `CrudOptions` 接口,添加 `dataPermission` 配置
+  - [x] 实现权限配置验证逻辑
 - [ ] 实现查询操作的自动权限过滤 (AC: 2)
   - [ ] 在 `getList` 方法中添加基于用户ID的权限过滤
   - [ ] 在 `getById` 方法中添加权限验证
@@ -130,12 +130,21 @@ Draft
 *此部分由开发代理在实现过程中填写*
 
 ### Agent Model Used
+- Claude Sonnet 4.5
 
 ### Debug Log References
+- 无
 
 ### Completion Notes List
+- 已创建数据权限控制类型定义和配置接口
+- 已扩展现有 CrudOptions 接口,添加 dataPermission 配置
+- 已实现权限配置验证逻辑
+- 构建验证通过,无类型错误
 
 ### File List
+- [packages/shared-crud/src/types/data-permission.types.ts](packages/shared-crud/src/types/data-permission.types.ts) - 新增
+- [packages/shared-crud/src/services/generic-crud.service.ts](packages/shared-crud/src/services/generic-crud.service.ts) - 修改
+- [packages/shared-crud/src/services/index.ts](packages/shared-crud/src/services/index.ts) - 修改
 
 ## QA Results
 *此部分由QA代理在质量检查后填写*

+ 13 - 0
packages/shared-crud/src/services/generic-crud.service.ts

@@ -1,9 +1,11 @@
 import { DataSource, Repository, ObjectLiteral, DeepPartial, In } from 'typeorm';
 import { z } from '@hono/zod-openapi';
+import { DataPermissionOptions, validateDataPermissionOptions } from '../types/data-permission.types';
 
 export abstract class GenericCrudService<T extends ObjectLiteral> {
   protected repository: Repository<T>;
   private userTrackingOptions?: UserTrackingOptions;
+  private dataPermissionOptions?: DataPermissionOptions;
 
   protected relationFields?: RelationFieldOptions;
 
@@ -13,11 +15,18 @@ export abstract class GenericCrudService<T extends ObjectLiteral> {
     options?: {
       userTracking?: UserTrackingOptions;
       relationFields?: RelationFieldOptions;
+      dataPermission?: DataPermissionOptions;
     }
   ) {
     this.repository = this.dataSource.getRepository(entity);
     this.userTrackingOptions = options?.userTracking;
     this.relationFields = options?.relationFields;
+
+    // 验证并设置数据权限配置
+    if (options?.dataPermission) {
+      validateDataPermissionOptions(options.dataPermission);
+      this.dataPermissionOptions = options.dataPermission;
+    }
   }
 
   /**
@@ -321,4 +330,8 @@ export type CrudOptions<
   userTracking?: UserTrackingOptions;
   relationFields?: RelationFieldOptions;
   readOnly?: boolean;
+  /**
+   * 数据权限控制配置
+   */
+  dataPermission?: DataPermissionOptions;
 };

+ 2 - 1
packages/shared-crud/src/services/index.ts

@@ -1,3 +1,4 @@
 export { GenericCrudService } from './generic-crud.service';
 export { ConcreteCrudService } from './concrete-crud.service';
-export type { UserTrackingOptions, RelationFieldOptions, CrudOptions } from './generic-crud.service';
+export type { UserTrackingOptions, RelationFieldOptions, CrudOptions } from './generic-crud.service';
+export type { DataPermissionOptions, PermissionError, PermissionResult } from '../types/data-permission.types';

+ 127 - 0
packages/shared-crud/src/types/data-permission.types.ts

@@ -0,0 +1,127 @@
+/**
+ * 数据权限控制配置选项
+ */
+export interface DataPermissionOptions {
+  /**
+   * 是否启用数据权限控制
+   * @default false
+   */
+  enabled: boolean;
+
+  /**
+   * 用户ID字段名,用于权限过滤
+   * 例如: 'userId', 'createdBy'
+   */
+  userIdField: string;
+
+  /**
+   * 管理员权限覆盖配置
+   */
+  adminOverride?: {
+    /**
+     * 是否启用管理员权限覆盖
+     * @default false
+     */
+    enabled: boolean;
+
+    /**
+     * 管理员角色标识
+     * @default 'admin'
+     */
+    adminRole?: string;
+
+    /**
+     * 管理员用户ID字段(可选,用于管理员用户ID检查)
+     */
+    userIdField?: string;
+  };
+
+  /**
+   * 多租户数据隔离配置
+   */
+  multiTenant?: {
+    /**
+     * 是否启用多租户数据隔离
+     * @default false
+     */
+    enabled: boolean;
+
+    /**
+     * 租户ID字段名
+     */
+    tenantIdField: string;
+  };
+
+  /**
+   * 自定义权限验证函数
+   * @param userId 当前用户ID
+   * @param entity 目标实体
+   * @returns 是否具有权限
+   */
+  customValidator?: (userId: string | number, entity: any) => Promise<boolean>;
+}
+
+/**
+ * 权限验证错误
+ */
+export class PermissionError extends Error {
+  constructor(message: string) {
+    super(message);
+    this.name = 'PermissionError';
+  }
+}
+
+/**
+ * 权限验证结果
+ */
+export interface PermissionResult {
+  /**
+   * 是否具有权限
+   */
+  hasPermission: boolean;
+
+  /**
+   * 错误信息(如果没有权限)
+   */
+  error?: string;
+
+  /**
+   * 是否被管理员权限覆盖
+   */
+  adminOverride?: boolean;
+}
+
+/**
+ * 验证数据权限配置
+ * @param options 数据权限配置
+ * @throws {Error} 如果配置无效
+ */
+export function validateDataPermissionOptions(options: DataPermissionOptions): void {
+  if (!options.enabled) {
+    return;
+  }
+
+  // 验证必填字段
+  if (!options.userIdField) {
+    throw new Error('启用数据权限控制时必须指定 userIdField');
+  }
+
+  // 验证管理员权限覆盖配置
+  if (options.adminOverride?.enabled) {
+    if (!options.adminOverride.adminRole) {
+      throw new Error('启用管理员权限覆盖时必须指定 adminRole');
+    }
+  }
+
+  // 验证多租户配置
+  if (options.multiTenant?.enabled) {
+    if (!options.multiTenant.tenantIdField) {
+      throw new Error('启用多租户数据隔离时必须指定 tenantIdField');
+    }
+  }
+
+  // 验证自定义验证器
+  if (options.customValidator && typeof options.customValidator !== 'function') {
+    throw new Error('customValidator 必须是一个函数');
+  }
+}