From 38208a57e8dd8be049a52ad32c24af3760d7f1b5 Mon Sep 17 00:00:00 2001 From: liuyi Date: Wed, 4 Jun 2025 16:13:25 +0800 Subject: [PATCH] add base subscriber --- .../content/subscribers/post.subscriber.ts | 11 ++- src/modules/database/base/subscriber.ts | 76 +++++++++++++++++++ src/modules/database/types.ts | 17 ++++- src/modules/database/utils.ts | 19 ++++- 4 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 src/modules/database/base/subscriber.ts diff --git a/src/modules/content/subscribers/post.subscriber.ts b/src/modules/content/subscribers/post.subscriber.ts index de67c65..43746c9 100644 --- a/src/modules/content/subscribers/post.subscriber.ts +++ b/src/modules/content/subscribers/post.subscriber.ts @@ -1,21 +1,20 @@ -import { DataSource, EventSubscriber } from 'typeorm'; +import { DataSource, EventSubscriber, ObjectType } from 'typeorm'; import { PostBodyType } from '@/modules/content/constants'; import { PostEntity } from '@/modules/content/entities/post.entity'; import { PostRepository } from '@/modules/content/repositories/post.repository'; import { SanitizeService } from '@/modules/content/services/SanitizeService'; +import { BaseSubscriber } from '@/modules/database/base/subscriber'; @EventSubscriber() -export class PostSubscriber { +export class PostSubscriber extends BaseSubscriber { + protected entity: ObjectType = PostEntity; constructor( protected dataSource: DataSource, protected sanitizeService: SanitizeService, protected postRepository: PostRepository, ) { - dataSource.subscribers.push(this); - } - listenTo() { - return PostEntity; + super(dataSource); } async afterLoad(entity: PostEntity) { diff --git a/src/modules/database/base/subscriber.ts b/src/modules/database/base/subscriber.ts new file mode 100644 index 0000000..5f19297 --- /dev/null +++ b/src/modules/database/base/subscriber.ts @@ -0,0 +1,76 @@ +import { Optional } from '@nestjs/common'; +import { isNil } from 'lodash'; +import { + DataSource, + EntitySubscriberInterface, + EntityTarget, + EventSubscriber, + InsertEvent, + ObjectLiteral, + ObjectType, + RecoverEvent, + RemoveEvent, + SoftRemoveEvent, + TransactionCommitEvent, + TransactionRollbackEvent, + TransactionStartEvent, + UpdateEvent, +} from 'typeorm'; + +import { RepositoryType } from '../types'; +import { getCustomRepository } from '../utils'; + +type SubscriberEvent = + | InsertEvent + | UpdateEvent + | SoftRemoveEvent + | RemoveEvent + | RecoverEvent + | TransactionStartEvent + | TransactionCommitEvent + | TransactionRollbackEvent; + +@EventSubscriber() +export abstract class BaseSubscriber + implements EntitySubscriberInterface +{ + protected abstract entity: ObjectType; + + protected constructor(@Optional() protected dataSource?: DataSource) { + if (!isNil(this.dataSource)) { + this.dataSource.subscribers.push(this); + } + } + + protected getDataSource(event: SubscriberEvent) { + return this.dataSource ?? event.connection; + } + + protected getManage(event: SubscriberEvent) { + return this.dataSource ? this.dataSource.manager : event.manager; + } + + listenTo() { + return this.entity; + } + + async afterLoad(entity: any) { + if ('parent' in entity && isNil(entity.depth)) { + entity.depth = 0; + } + } + + protected getRepository< + C extends ClassType

, + P extends RepositoryType, + R extends EntityTarget, + >(event: SubscriberEvent, respository?: C, entity?: R) { + return isNil(respository) + ? this.getDataSource(event).getRepository(entity ?? this.entity) + : getCustomRepository(this.getDataSource(event), respository); + } + + protected isUpdated(cloumn: keyof T, event: UpdateEvent) { + return !!event.updatedColumns.find((o) => o.propertyName === cloumn); + } +} diff --git a/src/modules/database/types.ts b/src/modules/database/types.ts index 36f6aef..ace6c80 100644 --- a/src/modules/database/types.ts +++ b/src/modules/database/types.ts @@ -1,7 +1,16 @@ -import { FindTreeOptions, ObjectLiteral, SelectQueryBuilder } from 'typeorm'; +import { + FindTreeOptions, + ObjectLiteral, + SelectQueryBuilder, + Repository, + TreeRepository, +} from 'typeorm'; import { OrderType, SelectTrashMode } from '@/modules/database/constants'; +import { BaseRepository } from './base/repository'; +import { BaseTreeRepository } from './base/tree.repository'; + export type QueryHook = ( qb: SelectQueryBuilder, ) => Promise>; @@ -52,3 +61,9 @@ export type ServiceListQueryOptionNotWithTrashed = Omit export type ServiceListQueryOption = | ServiceListQueryOptionNotWithTrashed | ServiceListQueryOptionWithTrashed; + +export type RepositoryType = + | Repository + | TreeRepository + | BaseRepository + | BaseTreeRepository; diff --git a/src/modules/database/utils.ts b/src/modules/database/utils.ts index c64d5c7..c1cc9d2 100644 --- a/src/modules/database/utils.ts +++ b/src/modules/database/utils.ts @@ -1,8 +1,10 @@ import { isArray, isNil } from 'lodash'; -import { ObjectLiteral, SelectQueryBuilder } from 'typeorm'; +import { DataSource, ObjectLiteral, ObjectType, Repository, SelectQueryBuilder } from 'typeorm'; import { OrderQueryType, PaginateOptions, PaginateReturn } from '@/modules/database/types'; +import { CUSTOM_REPOSITORY_METADATA } from './constants'; + export const paginate = async ( qb: SelectQueryBuilder, options: PaginateOptions, @@ -80,3 +82,18 @@ export const getOrderByQuery = ( } return qb.orderBy(`${alias}.${(orderBy as any).name}`, (orderBy as any).order); }; + +export const getCustomRepository =

, T extends ObjectLiteral>( + dataSource: DataSource, + Repo: ClassType

, +): P => { + if (isNil(Repo)) { + return null; + } + const entity = Reflect.getMetadata(CUSTOM_REPOSITORY_METADATA, Repo); + if (!entity) { + return null; + } + const base = dataSource.getRepository>(entity); + return new Repo(base.target, base.manager, base.queryRunner) as P; +};