| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- import { DataSource, Repository, ObjectLiteral, DeepPartial } from 'typeorm';
- import { z } from '@hono/zod-openapi';
- export abstract class GenericCrudService<T extends ObjectLiteral> {
- protected repository: Repository<T>;
- private userTrackingOptions?: UserTrackingOptions;
- constructor(
- protected dataSource: DataSource,
- protected entity: new () => T,
- options?: {
- userTracking?: UserTrackingOptions;
- }
- ) {
- this.repository = this.dataSource.getRepository(entity);
- this.userTrackingOptions = options?.userTracking;
- }
- /**
- * 获取分页列表
- */
- async getList(
- page: number = 1,
- pageSize: number = 10,
- keyword?: string,
- searchFields?: string[],
- where?: Partial<T>,
- relations: string[] = [],
- order: { [P in keyof T]?: 'ASC' | 'DESC' } = {},
- filters?: {
- [key: string]: any;
- }
- ): Promise<[T[], number]> {
- const skip = (page - 1) * pageSize;
- const query = this.repository.createQueryBuilder('entity');
- // 添加关联关系(支持嵌套关联,如 ['contract.client'])
- if (relations.length > 0) {
- relations.forEach((relation, relationIndex) => {
- const parts = relation.split('.');
- let currentAlias = 'entity';
-
- parts.forEach((part, index) => {
- const newAlias = index === 0 ? part : `${currentAlias}_${relationIndex}`;
- query.leftJoinAndSelect(`${currentAlias}.${part}`, newAlias);
- currentAlias = newAlias;
- });
- });
- }
- // 关键词搜索
- if (keyword && searchFields && searchFields.length > 0) {
- query.andWhere(searchFields.map(field => `entity.${field} LIKE :keyword`).join(' OR '), {
- keyword: `%${keyword}%`
- });
- }
- // 条件查询
- if (where) {
- Object.entries(where).forEach(([key, value]) => {
- if (value !== undefined && value !== null) {
- query.andWhere(`entity.${key} = :${key}`, { [key]: value });
- }
- });
- }
- // 扩展筛选条件
- if (filters) {
- Object.entries(filters).forEach(([key, value]) => {
- if (value !== undefined && value !== null && value !== '') {
- const fieldName = key.startsWith('_') ? key.substring(1) : key;
-
- // 支持不同类型的筛选
- if (Array.isArray(value)) {
- // 数组类型:IN查询
- if (value.length > 0) {
- query.andWhere(`entity.${fieldName} IN (:...${key})`, { [key]: value });
- }
- } else if (typeof value === 'string' && value.includes('%')) {
- // 模糊匹配
- query.andWhere(`entity.${fieldName} LIKE :${key}`, { [key]: value });
- } else if (typeof value === 'object' && value !== null) {
- // 范围查询
- if ('gte' in value) {
- query.andWhere(`entity.${fieldName} >= :${key}_gte`, { [`${key}_gte`]: value.gte });
- }
- if ('gt' in value) {
- query.andWhere(`entity.${fieldName} > :${key}_gt`, { [`${key}_gt`]: value.gt });
- }
- if ('lte' in value) {
- query.andWhere(`entity.${fieldName} <= :${key}_lte`, { [`${key}_lte`]: value.lte });
- }
- if ('lt' in value) {
- query.andWhere(`entity.${fieldName} < :${key}_lt`, { [`${key}_lt`]: value.lt });
- }
- if ('between' in value && Array.isArray(value.between) && value.between.length === 2) {
- query.andWhere(`entity.${fieldName} BETWEEN :${key}_start AND :${key}_end`, {
- [`${key}_start`]: value.between[0],
- [`${key}_end`]: value.between[1]
- });
- }
- } else {
- // 精确匹配
- query.andWhere(`entity.${fieldName} = :${key}`, { [key]: value });
- }
- }
- });
- }
- // 排序
- Object.entries(order).forEach(([key, direction]) => {
- query.orderBy(`entity.${key}`, direction);
- });
- return query.skip(skip).take(pageSize).getManyAndCount();
- }
- /**
- * 根据ID获取单个实体
- */
- async getById(id: number, relations: string[] = []): Promise<T | null> {
- return this.repository.findOne({
- where: { id } as any,
- relations
- });
- }
- /**
- * 设置用户跟踪字段
- */
- private setUserFields(data: any, userId?: string | number, isCreate: boolean = true): void {
- if (!this.userTrackingOptions || !userId) {
- return;
- }
- const { createdByField = 'createdBy', updatedByField = 'updatedBy' } = this.userTrackingOptions;
- if (isCreate && createdByField) {
- data[createdByField] = userId;
- }
- if (updatedByField) {
- data[updatedByField] = userId;
- }
- }
- /**
- * 创建实体
- */
- async create(data: DeepPartial<T>, userId?: string | number): Promise<T> {
- const entityData = { ...data };
- this.setUserFields(entityData, userId, true);
- const entity = this.repository.create(entityData as DeepPartial<T>);
- return this.repository.save(entity);
- }
- /**
- * 更新实体
- */
- async update(id: number, data: Partial<T>, userId?: string | number): Promise<T | null> {
- const updateData = { ...data };
- this.setUserFields(updateData, userId, false);
- await this.repository.update(id, updateData);
- return this.getById(id);
- }
- /**
- * 删除实体
- */
- async delete(id: number): Promise<boolean> {
- const result = await this.repository.delete(id);
- return result.affected === 1;
- }
- /**
- * 高级查询方法
- */
- createQueryBuilder(alias: string = 'entity') {
- return this.repository.createQueryBuilder(alias);
- }
- }
- export interface UserTrackingOptions {
- createdByField?: string;
- updatedByField?: string;
- }
- export type CrudOptions<
- T extends ObjectLiteral,
- CreateSchema extends z.ZodSchema = z.ZodSchema,
- UpdateSchema extends z.ZodSchema = z.ZodSchema,
- GetSchema extends z.ZodSchema = z.ZodSchema,
- ListSchema extends z.ZodSchema = z.ZodSchema
- > = {
- entity: new () => T;
- createSchema: CreateSchema;
- updateSchema: UpdateSchema;
- getSchema: GetSchema;
- listSchema: ListSchema;
- searchFields?: string[];
- relations?: string[];
- middleware?: any[];
- userTracking?: UserTrackingOptions;
- };
|