--- description: "实体现有字段与用户实体关联开发指令" --- ## 适用场景 当实体中已经存在用户相关的字段(如 `handlerId`, `createdBy`, `userId`, `operatorId` 等),需要将这些字段与 `User` 实体建立关联关系时使用。 ## 开发步骤 ### 1. 修改实体定义 找到实体文件 `src/server/modules/[模块名]/[实体名].entity.ts`,将现有用户字段改为关联关系: #### 1.1 替换字段类型和装饰器 ```typescript // 修改前:原始字段定义 @Column({ name: 'handler_id', type: 'int', comment: '处理人ID' }) handlerId!: number; // 修改后:单向关联关系定义 @ManyToOne(() => User) @JoinColumn({ name: 'handler_id' }) handler!: User; // 如果允许为空(单向关联) @ManyToOne(() => User, { nullable: true }) @JoinColumn({ name: 'handler_id' }) handler?: User | null; ``` #### 1.2 添加User实体导入 ```typescript // 在实体顶部添加 import { User } from '@/server/modules/users/user.entity'; ``` #### 1.3 在User实体中添加反向关联(可选) 如果需要双向关联,在User实体中添加对应的关系定义: ```typescript // 在User实体中添加对应的关系定义(双向关联) @OneToMany(() => AlertHandleLog, log => log.handler) alertHandleLogs?: AlertHandleLog[]; ``` > **注意**:单向关联不需要此步骤,保持现有配置即可 ### 2. 更新Zod Schema 在对应的 schema 文件中同步修改: #### 2.1 修改实体Schema ```typescript // 在文件顶部添加导入 import { UserSchema } from '@/server/modules/users/user.schema'; // 修改前 handlerId: z.number().int().positive().openapi({ description: '处理人ID', example: 1 }) // 修改后 - 使用导入的UserSchema handler: UserSchema.omit({ password: true }).nullable().optional().openapi({ description: '处理人信息' }) ``` #### 2.2 修改Create/Update DTO ```typescript // 修改前:Create DTO export const CreateAlertHandleLogDto = z.object({ handlerId: z.number().int().positive().openapi({ description: '处理人ID', example: 1 }), // ... 其他字段 }); // 修改后:Create DTO export const CreateAlertHandleLogDto = z.object({ handlerId: z.number().int().positive().openapi({ description: '处理人ID', example: 1 }), // ... 其他字段 }); // Update DTO保持不变,但handlerId改为optional export const UpdateAlertHandleLogDto = z.object({ handlerId: z.number().int().positive().optional().openapi({ description: '处理人ID', example: 1 }), // ... 其他字段 }); ``` ### 4. 更新通用CRUD配置 如果使用通用CRUD路由,需要配置relations: ```typescript const routes = createCrudRoutes({ entity: AlertHandleLog, createSchema: CreateAlertHandleLogDto, updateSchema: UpdateAlertHandleLogDto, getSchema: AlertHandleLogSchema, listSchema: AlertHandleLogSchema, relations: ['handler'], // 添加关联配置 middleware: [authMiddleware] }); ``` ## 命名规范 | 类型 | 命名格式 | 示例 | 说明 | |------|----------|------|------| | 数据库字段 | `{前缀}_id` | `handler_id` | 保持原有字段名不变 | | 实体字段 | `{前缀}` | `handler` | 关联实体对象 | | 反向关联 | `{实体名}s` | `alertHandleLogs` | User实体中的集合名 | | 外键字段 | `{前缀}Id` | `handlerId` | DTO中的外键字段 | ## 完整示例 ### 实体类 ([`src/server/modules/alerts/alert-handle-log.entity.ts`](src/server/modules/alerts/alert-handle-log.entity.ts)) ```typescript import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'; import { User } from '@/server/modules/users/user.entity'; @Entity('alert_handle_logs') export class AlertHandleLog { @PrimaryGeneratedColumn({ unsigned: true }) id!: number; @Column({ name: 'handler_id', type: 'int', comment: '处理人ID' }) handlerId!: number; // 单向关联定义(无需反向引用) @ManyToOne(() => User) @JoinColumn({ name: 'handler_id' }) handler!: User; // ... 其他字段 } ``` ### User实体反向关联(可选,仅双向关联需要) 如果需要双向关联,在User实体中添加: ```typescript @OneToMany(() => AlertHandleLog, log => log.handler) alertHandleLogs?: AlertHandleLog[]; ``` > **单向关联说明**:当前配置为单向关联,无需在User实体中添加反向关系定义 ### Schema定义 ([`src/server/modules/alerts/alert-handle-log.schema.ts`](src/server/modules/alerts/alert-handle-log.schema.ts)) ```typescript // 引入用户Schema import { UserSchema } from '@/server/modules/users/user.schema'; // 响应Schema - 使用UserSchema.omit去掉敏感字段 handler: UserSchema.omit({ password: true }).nullable().optional().openapi({ description: '处理人信息' }), // 请求DTO中的外键字段 handlerId: z.number().int().positive().openapi({ description: '处理人ID', example: 1 }) ``` ## 注意事项 1. **数据库兼容性**:现有数据库字段保持不变,只是从整数字段升级为外键约束 2. **API兼容性**:DTO中的外键字段名保持不变(如handlerId),确保API接口兼容 3. **查询性能**:配置relations后,关联查询会自动优化,避免N+1问题 4. **级联操作**:默认不级联删除,删除用户不会影响关联记录 5. **空值处理**:如果handlerId为null,handler关联对象将为null ## 常见问题 ### Q: 如何支持多个用户关联字段? A: 重复上述步骤,为每个用户字段建立独立的关联关系,例如: ```typescript @ManyToOne(() => User, user => user.createdAlerts) @JoinColumn({ name: 'created_by' }) creator!: User; @ManyToOne(() => User, user => user.updatedAlerts) @JoinColumn({ name: 'updated_by' }) updater?: User | null; ``` ### Q: 如何处理复合用户关联? A: 使用相同的方法,只是字段名不同: ```typescript @ManyToOne(() => User, user => user.assignedTasks) @JoinColumn({ name: 'assignee_id' }) assignee!: User; ```