add base service
This commit is contained in:
parent
3f0a9ca6fb
commit
6f581ad383
130
src/modules/database/base/service.ts
Normal file
130
src/modules/database/base/service.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import { NotFoundException } from '@nestjs/common';
|
||||||
|
import { isNil } from 'lodash';
|
||||||
|
import { In, ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
||||||
|
|
||||||
|
import { BaseRepository } from '@/modules/database/base/repository';
|
||||||
|
import { BaseTreeRepository } from '@/modules/database/base/tree.repository';
|
||||||
|
import { SelectTrashMode, TreeChildrenResolve } from '@/modules/database/constants';
|
||||||
|
import {
|
||||||
|
PaginateOptions,
|
||||||
|
PaginateReturn,
|
||||||
|
QueryHook,
|
||||||
|
ServiceListQueryOption,
|
||||||
|
} from '@/modules/database/types';
|
||||||
|
import { paginate, treePaginate } from '@/modules/database/utils';
|
||||||
|
|
||||||
|
export abstract class BaseService<
|
||||||
|
T extends ObjectLiteral,
|
||||||
|
R extends BaseRepository<T> | BaseTreeRepository<T>,
|
||||||
|
P extends ServiceListQueryOption<T> = ServiceListQueryOption<T>,
|
||||||
|
> {
|
||||||
|
protected repository: R;
|
||||||
|
|
||||||
|
protected enableTrash = false;
|
||||||
|
|
||||||
|
protected constructor(repository: R) {
|
||||||
|
this.repository = repository;
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
this.repository instanceof BaseRepository ||
|
||||||
|
this.repository instanceof BaseTreeRepository
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error('Repository init error.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async buildItemQB(id: string, qb: SelectQueryBuilder<T>, callback?: QueryHook<T>) {
|
||||||
|
qb.where(`${this.repository.qbName}.id = :id`, { id });
|
||||||
|
if (callback) {
|
||||||
|
return callback(qb);
|
||||||
|
}
|
||||||
|
return qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async buildListQB(qb: SelectQueryBuilder<T>, options?: P, callback?: QueryHook<T>) {
|
||||||
|
const { trashed } = options ?? {};
|
||||||
|
const queryName = this.repository.qbName;
|
||||||
|
if (this.enableTrash && trashed in [SelectTrashMode.ALL, SelectTrashMode.ONLY]) {
|
||||||
|
qb.withDeleted();
|
||||||
|
if (trashed === SelectTrashMode.ONLY) {
|
||||||
|
qb.where(`${queryName}.deletedAt IS NOT NULL`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (callback) {
|
||||||
|
return callback(qb);
|
||||||
|
}
|
||||||
|
return qb;
|
||||||
|
}
|
||||||
|
|
||||||
|
async list(options?: P, callback?: QueryHook<T>) {
|
||||||
|
const { trashed: isTrashed = false } = options ?? {};
|
||||||
|
const trashed = isTrashed || SelectTrashMode.NONE;
|
||||||
|
if (this.repository instanceof BaseTreeRepository) {
|
||||||
|
const withTrashed =
|
||||||
|
this.enableTrash && trashed in [SelectTrashMode.ALL, SelectTrashMode.ONLY];
|
||||||
|
const onlyTrashed = this.enableTrash && trashed === SelectTrashMode.ONLY;
|
||||||
|
const tree = await this.repository.findTrees({ ...options, withTrashed, onlyTrashed });
|
||||||
|
return this.repository.toFlatTrees(tree);
|
||||||
|
}
|
||||||
|
const qb = await this.buildListQB(this.repository.buildBaseQB(), options, callback);
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async paginate(options?: PaginateOptions & P, callback?: QueryHook<T>) {
|
||||||
|
const queryOptions = (options ?? {}) as P;
|
||||||
|
if (this.repository instanceof BaseTreeRepository) {
|
||||||
|
const data = await this.list(queryOptions, callback);
|
||||||
|
return treePaginate(options, data) as PaginateReturn<T>;
|
||||||
|
}
|
||||||
|
const qb = await this.buildListQB(this.repository.buildBaseQB(), queryOptions, callback);
|
||||||
|
return paginate(qb, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async detail(id: string, callback?: QueryHook<T>) {
|
||||||
|
const qb = await this.buildItemQB(id, this.repository.buildBaseQB(), callback);
|
||||||
|
const item = qb.getOne();
|
||||||
|
if (!item) {
|
||||||
|
throw new NotFoundException(`${this.repository.qbName} ${id} NOT FOUND`);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(ids: string[], trash?: boolean) {
|
||||||
|
let items: T[];
|
||||||
|
if (this.repository instanceof BaseTreeRepository) {
|
||||||
|
items = await this.repository.find({
|
||||||
|
where: { id: In(ids) as any },
|
||||||
|
withDeleted: this.enableTrash ? true : undefined,
|
||||||
|
relations: ['parent', 'children'],
|
||||||
|
});
|
||||||
|
if (this.repository.childrenResolve === TreeChildrenResolve.UP) {
|
||||||
|
for (const item of items) {
|
||||||
|
if (isNil(item.children) || item.children.length <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const children = [...item.children].map((o) => {
|
||||||
|
o.parent = item.parent;
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
await this.repository.save(children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items = await this.repository.find({
|
||||||
|
where: { id: In(ids) as any },
|
||||||
|
withDeleted: this.enableTrash ? true : undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.enableTrash && trash) {
|
||||||
|
const directs = items.filter((item) => !isNil(item.deletedAt));
|
||||||
|
const softs = items.filter((item) => isNil(item.deletedAt));
|
||||||
|
return [
|
||||||
|
...(await this.repository.remove(directs)),
|
||||||
|
...(await this.repository.softRemove(softs)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return this.repository.remove(items);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
import { FindTreeOptions, ObjectLiteral, SelectQueryBuilder } from 'typeorm';
|
||||||
|
|
||||||
import { OrderType } from '@/modules/database/constants';
|
import { OrderType, SelectTrashMode } from '@/modules/database/constants';
|
||||||
|
|
||||||
export type QueryHook<Entity> = (
|
export type QueryHook<Entity> = (
|
||||||
qb: SelectQueryBuilder<Entity>,
|
qb: SelectQueryBuilder<Entity>,
|
||||||
@ -38,3 +38,17 @@ export interface QueryParams<T extends ObjectLiteral> {
|
|||||||
|
|
||||||
onlyTrashed?: boolean;
|
onlyTrashed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ServiceListQueryOptionWithTrashed<T extends ObjectLiteral> = Omit<
|
||||||
|
FindTreeOptions & QueryParams<T>,
|
||||||
|
'withTrashed'
|
||||||
|
> & { trashed?: `${SelectTrashMode}` } & RecordAny;
|
||||||
|
|
||||||
|
export type ServiceListQueryOptionNotWithTrashed<T extends ObjectLiteral> = Omit<
|
||||||
|
ServiceListQueryOptionWithTrashed<T>,
|
||||||
|
'trashed'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ServiceListQueryOption<T extends ObjectLiteral> =
|
||||||
|
| ServiceListQueryOptionNotWithTrashed<T>
|
||||||
|
| ServiceListQueryOptionWithTrashed<T>;
|
||||||
|
Loading…
Reference in New Issue
Block a user