add base repository

This commit is contained in:
liuyi 2025-06-03 14:29:58 +08:00
parent 2c76c94578
commit 3f0a9ca6fb
3 changed files with 24 additions and 110 deletions

View File

@ -1,21 +1,23 @@
import { isNil, pick, unset } from 'lodash'; import { isNil, unset } from 'lodash';
import { FindOptionsUtils, FindTreeOptions, TreeRepository, TreeRepositoryUtils } from 'typeorm'; import { FindOptionsUtils, FindTreeOptions, TreeRepositoryUtils } from 'typeorm';
import { CategoryEntity } from '@/modules/content/entities/category.entity'; import { CategoryEntity } from '@/modules/content/entities/category.entity';
import { BaseTreeRepository } from '@/modules/database/base/tree.repository';
import { OrderType, TreeChildrenResolve } from '@/modules/database/constants';
import { CustomRepository } from '@/modules/database/decorators/repository.decorator'; import { CustomRepository } from '@/modules/database/decorators/repository.decorator';
@CustomRepository(CategoryEntity) @CustomRepository(CategoryEntity)
export class CategoryRepository extends TreeRepository<CategoryEntity> { export class CategoryRepository extends BaseTreeRepository<CategoryEntity> {
protected _qbName = 'category';
protected orderBy = { name: 'customOrder', order: OrderType.ASC };
protected _childrenResolve = TreeChildrenResolve.UP;
buildBaseQB() { buildBaseQB() {
return this.createQueryBuilder('category').leftJoinAndSelect('category.parent', 'parent'); return this.createQueryBuilder('category').leftJoinAndSelect('category.parent', 'parent');
} }
async findTrees(options?: FindTreeOptions) {
const roots = await this.findRoots(options);
await Promise.all(roots.map((root) => this.findDescendantsTree(root, options)));
return roots;
}
findRoots(options?: FindTreeOptions): Promise<CategoryEntity[]> { findRoots(options?: FindTreeOptions): Promise<CategoryEntity[]> {
const escape = (val: string) => this.manager.connection.driver.escape(val); const escape = (val: string) => this.manager.connection.driver.escape(val);
const joinColumn = this.metadata.treeParentRelation!.joinColumns[0]; const joinColumn = this.metadata.treeParentRelation!.joinColumns[0];
@ -39,28 +41,6 @@ export class CategoryRepository extends TreeRepository<CategoryEntity> {
return qb.getMany(); return qb.getMany();
} }
async findDescendantsTree(entity: CategoryEntity, options?: FindTreeOptions) {
const qb = this.createDescendantsQueryBuilder('category', 'treeClosure', entity)
.leftJoinAndSelect('category.parent', 'parent')
.orderBy('category.customOrder', 'ASC');
FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, pick(options, ['relations', 'depth']));
const entities = await qb.getRawAndEntities();
const relationMaps = TreeRepositoryUtils.createRelationMaps(
this.manager,
this.metadata,
'category',
entities.raw,
);
TreeRepositoryUtils.buildChildrenEntityTree(
this.metadata,
entity,
entities.entities,
relationMaps,
{ depth: -1, ...pick(options, ['relations']) },
);
return entity;
}
async findAncestorsTree( async findAncestorsTree(
entity: CategoryEntity, entity: CategoryEntity,
options?: FindTreeOptions, options?: FindTreeOptions,
@ -95,23 +75,6 @@ export class CategoryRepository extends TreeRepository<CategoryEntity> {
return qb.getCount(); return qb.getCount();
} }
async toFlatTrees(
trees: CategoryEntity[],
depth = 0,
parent: CategoryEntity | null = null,
): Promise<CategoryEntity[]> {
const data: Omit<CategoryEntity, 'children'>[] = [];
for (const item of trees) {
item.depth = depth;
item.parent = parent;
const { children } = item;
unset(item, 'children');
data.push(item);
data.push(...(await this.toFlatTrees(children, depth + 1, item)));
}
return data as CategoryEntity[];
}
async flatAncestorsTree(item: CategoryEntity) { async flatAncestorsTree(item: CategoryEntity) {
let data: Omit<CategoryEntity, 'children'>[] = []; let data: Omit<CategoryEntity, 'children'>[] = [];
const category = await this.findAncestorsTree(item); const category = await this.findAncestorsTree(item);

View File

@ -1,20 +1,17 @@
import { pick, unset } from 'lodash'; import { FindOptionsUtils, FindTreeOptions, SelectQueryBuilder } from 'typeorm';
import {
FindOptionsUtils,
FindTreeOptions,
SelectQueryBuilder,
TreeRepository,
TreeRepositoryUtils,
} from 'typeorm';
import { CommentEntity } from '@/modules/content/entities/comment.entity'; import { CommentEntity } from '@/modules/content/entities/comment.entity';
import { BaseTreeRepository } from '@/modules/database/base/tree.repository';
import { CustomRepository } from '@/modules/database/decorators/repository.decorator'; import { CustomRepository } from '@/modules/database/decorators/repository.decorator';
import { QueryHook } from '@/modules/database/types';
type FindCommentTreeOptions = FindTreeOptions & { type FindCommentTreeOptions = FindTreeOptions & {
addQuery?: (query: SelectQueryBuilder<CommentEntity>) => SelectQueryBuilder<CommentEntity>; addQuery?: QueryHook<CommentEntity>;
}; };
@CustomRepository(CommentEntity) @CustomRepository(CommentEntity)
export class CommentRepository extends TreeRepository<CommentEntity> { export class CommentRepository extends BaseTreeRepository<CommentEntity> {
protected _qbName = 'comment';
buildBaseQB(qb: SelectQueryBuilder<CommentEntity>): SelectQueryBuilder<CommentEntity> { buildBaseQB(qb: SelectQueryBuilder<CommentEntity>): SelectQueryBuilder<CommentEntity> {
return qb return qb
.leftJoinAndSelect(`comment.parent`, 'parent') .leftJoinAndSelect(`comment.parent`, 'parent')
@ -22,14 +19,7 @@ export class CommentRepository extends TreeRepository<CommentEntity> {
.orderBy('comment.createdAt', 'DESC'); .orderBy('comment.createdAt', 'DESC');
} }
async findTrees(options: FindCommentTreeOptions): Promise<CommentEntity[]> { async findRoots(options?: FindCommentTreeOptions): Promise<CommentEntity[]> {
options.relations = ['parent', 'children'];
const roots = await this.findRoots(options);
await Promise.all(roots.map((root) => this.findDescendantsTree(root, options)));
return roots;
}
findRoots(options?: FindCommentTreeOptions): Promise<CommentEntity[]> {
const { addQuery, ...rest } = options; const { addQuery, ...rest } = options;
const escape = (val: string) => this.manager.connection.driver.escape(val); const escape = (val: string) => this.manager.connection.driver.escape(val);
const joinColumn = this.metadata.treeParentRelation!.joinColumns[0]; const joinColumn = this.metadata.treeParentRelation!.joinColumns[0];
@ -38,58 +28,19 @@ export class CommentRepository extends TreeRepository<CommentEntity> {
let qb = this.buildBaseQB(this.createQueryBuilder('comment')); let qb = this.buildBaseQB(this.createQueryBuilder('comment'));
FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, rest); FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, rest);
qb.where(`${escape('comment')}.${escape(parentPropertyName)} IS NULL`); qb.where(`${escape('comment')}.${escape(parentPropertyName)} IS NULL`);
qb = addQuery ? addQuery(qb) : qb; qb = addQuery ? await addQuery(qb) : qb;
return qb.getMany(); return qb.getMany();
} }
createDtsQueryBuilder( async createDtsQueryBuilder(
closureTable: string, closureTable: string,
entity: CommentEntity, entity: CommentEntity,
options: FindCommentTreeOptions = {}, options: FindCommentTreeOptions = {},
): SelectQueryBuilder<CommentEntity> { ): Promise<SelectQueryBuilder<CommentEntity>> {
const { addQuery } = options; const { addQuery } = options;
const qb = this.buildBaseQB( const qb = this.buildBaseQB(
super.createDescendantsQueryBuilder('comment', closureTable, entity), super.createDescendantsQueryBuilder('comment', closureTable, entity),
); );
return addQuery ? addQuery(qb) : qb; return addQuery ? addQuery(qb) : qb;
} }
async findDescendantsTree(
entity: CommentEntity,
options: FindCommentTreeOptions = {},
): Promise<CommentEntity> {
const qb: SelectQueryBuilder<CommentEntity> = this.createDtsQueryBuilder(
'treeClosure',
entity,
options,
);
FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, pick(options, ['relations', 'depth']));
const entities = await qb.getRawAndEntities();
const relationMaps = TreeRepositoryUtils.createRelationMaps(
this.manager,
this.metadata,
'comment',
entities.raw,
);
TreeRepositoryUtils.buildChildrenEntityTree(
this.metadata,
entity,
entities.entities,
relationMaps,
{ depth: -1, ...pick(options, ['relations']) },
);
return entity;
}
async toFlatTrees(trees: CommentEntity[], depth = 0): Promise<CommentEntity[]> {
const data: Omit<CommentEntity, 'children'>[] = [];
for (const item of trees) {
item.depth = depth;
const { children } = item;
unset(item, 'children');
data.push(item);
data.push(...(await this.toFlatTrees(children, depth + 1)));
}
return data as CommentEntity[];
}
} }

View File

@ -22,7 +22,7 @@ export class CommentService {
async findTrees(options: QueryCommentTreeDto = {}) { async findTrees(options: QueryCommentTreeDto = {}) {
return this.repository.findTrees({ return this.repository.findTrees({
addQuery: (qb) => { addQuery: async (qb) => {
return isNil(options.post) ? qb : qb.where('post.id = :id', { id: options.post }); return isNil(options.post) ? qb : qb.where('post.id = :id', { id: options.post });
}, },
}); });
@ -30,7 +30,7 @@ export class CommentService {
async paginate(options: QueryCommentDto) { async paginate(options: QueryCommentDto) {
const { post, ...query } = options; const { post, ...query } = options;
const addQuery = (qb: SelectQueryBuilder<CommentEntity>) => { const addQuery = async (qb: SelectQueryBuilder<CommentEntity>) => {
const condition: RecordString = {}; const condition: RecordString = {};
if (!isNil(post)) { if (!isNil(post)) {
condition.post = post; condition.post = post;