generic-crud.service.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { DataSource, Repository, ObjectLiteral, DeepPartial } from 'typeorm';
  2. import { z } from '@hono/zod-openapi';
  3. export abstract class GenericCrudService<T extends ObjectLiteral> {
  4. protected repository: Repository<T>;
  5. constructor(
  6. protected dataSource: DataSource,
  7. protected entity: new () => T
  8. ) {
  9. this.repository = this.dataSource.getRepository(entity);
  10. }
  11. /**
  12. * 获取分页列表
  13. */
  14. /**
  15. * 获取分页列表,支持高级查询
  16. */
  17. async getList(
  18. page: number = 1,
  19. pageSize: number = 10,
  20. keyword?: string,
  21. searchFields?: string[],
  22. where?: Partial<T>,
  23. relations: string[] = [],
  24. order: { [P in keyof T]?: 'ASC' | 'DESC' } = {}
  25. ): Promise<[T[], number]> {
  26. const skip = (page - 1) * pageSize;
  27. const query = this.repository.createQueryBuilder('entity');
  28. // 关联查询
  29. if (relations.length > 0) {
  30. relations.forEach(relation => {
  31. query.leftJoinAndSelect(`entity.${relation}`, relation);
  32. });
  33. }
  34. // 关键词搜索
  35. if (keyword && searchFields && searchFields.length > 0) {
  36. query.andWhere(searchFields.map(field => `entity.${field} LIKE :keyword`).join(' OR '), {
  37. keyword: `%${keyword}%`
  38. });
  39. }
  40. // 条件查询
  41. if (where) {
  42. Object.entries(where).forEach(([key, value]) => {
  43. if (value !== undefined && value !== null) {
  44. query.andWhere(`entity.${key} = :${key}`, { [key]: value });
  45. }
  46. });
  47. }
  48. // 排序
  49. Object.entries(order).forEach(([key, direction]) => {
  50. query.orderBy(`entity.${key}`, direction);
  51. });
  52. return query.skip(skip).take(pageSize).getManyAndCount();
  53. }
  54. /**
  55. * 高级查询方法
  56. */
  57. createQueryBuilder(alias: string = 'entity') {
  58. return this.repository.createQueryBuilder(alias);
  59. }
  60. /**
  61. * 根据ID获取单个实体
  62. */
  63. async getById(id: number): Promise<T | null> {
  64. return this.repository.findOneBy({ id } as any);
  65. }
  66. /**
  67. * 创建实体
  68. */
  69. async create(data: DeepPartial<T>): Promise<T> {
  70. const entity = this.repository.create(data as DeepPartial<T>);
  71. return this.repository.save(entity);
  72. }
  73. /**
  74. * 更新实体
  75. */
  76. async update(id: number, data: Partial<T>): Promise<T | null> {
  77. await this.repository.update(id, data);
  78. return this.getById(id);
  79. }
  80. /**
  81. * 删除实体
  82. */
  83. async delete(id: number): Promise<boolean> {
  84. const result = await this.repository.delete(id);
  85. return result.affected === 1;
  86. }
  87. }
  88. export type CrudOptions<
  89. T extends ObjectLiteral,
  90. CreateSchema extends z.ZodSchema = z.ZodSchema,
  91. UpdateSchema extends z.ZodSchema = z.ZodSchema,
  92. GetSchema extends z.ZodSchema = z.ZodSchema,
  93. ListSchema extends z.ZodSchema = z.ZodSchema
  94. > = {
  95. entity: new () => T;
  96. createSchema: CreateSchema;
  97. updateSchema: UpdateSchema;
  98. getSchema: GetSchema;
  99. listSchema: ListSchema;
  100. searchFields?: string[];
  101. middleware?: any[];
  102. };