194 lines
5.6 KiB
TypeScript
194 lines
5.6 KiB
TypeScript
import { AppBaseEntity } from '../entities/app-base-entity';
|
|
import { Brackets, Repository, SelectQueryBuilder } from 'typeorm';
|
|
import { TypeormHelper } from './typeorm-helper';
|
|
import { UnprocessableEntityException } from '@nestjs/common';
|
|
import {
|
|
DuplicateEntityException,
|
|
DuplicateFieldInfo,
|
|
} from '../exceptions/duplicate-entity.exception';
|
|
|
|
export class BaseDbService<Entity extends AppBaseEntity> extends TypeormHelper {
|
|
constructor(protected readonly repository: Repository<Entity>) {
|
|
super();
|
|
}
|
|
|
|
readonly uniqueFields: Array<keyof Entity | Array<keyof Entity>> = [];
|
|
|
|
async isDuplicateEntity<Dto extends Entity & Record<string, any>>(
|
|
dto: Partial<Dto>,
|
|
extendsFields: Array<keyof Dto> = [],
|
|
): Promise<false | never> {
|
|
const qb = this.repository.createQueryBuilder('entity');
|
|
const compareFields = this.getCompareFields(dto, [
|
|
...this.uniqueFields,
|
|
...extendsFields,
|
|
]);
|
|
if (compareFields.length > 0) {
|
|
qb.andWhere(
|
|
new Brackets((bqb) => {
|
|
for (const key of compareFields) {
|
|
if (Array.isArray(key)) {
|
|
bqb.orWhere(
|
|
new Brackets((bbqb) => {
|
|
for (const k of key) {
|
|
bbqb.andWhere(`entity.${k} = :${k}`);
|
|
}
|
|
}),
|
|
);
|
|
} else {
|
|
bqb.orWhere(`entity.${key} = :${key}`);
|
|
}
|
|
}
|
|
}),
|
|
);
|
|
} else {
|
|
return false;
|
|
}
|
|
qb.setParameters(Object.assign({}, dto));
|
|
const uniqueFields = Array.from(new Set(compareFields.flat()));
|
|
qb.addSelect(uniqueFields.map((f) => `entity.${f} AS entity_${f}`));
|
|
return await this.checkDuplicateFields(qb, dto, uniqueFields);
|
|
}
|
|
|
|
async isDuplicateEntityForUpdate<Dto extends Entity>(
|
|
id: string,
|
|
dto: Partial<Dto>,
|
|
extendsFields?: Array<keyof Dto & string>,
|
|
): Promise<false | never>;
|
|
async isDuplicateEntityForUpdate<Dto extends Entity>(
|
|
old: Entity,
|
|
dto: Partial<Dto>,
|
|
extendsFields?: Array<keyof Dto & string>,
|
|
): Promise<false | never>;
|
|
async isDuplicateEntityForUpdate<Dto extends Entity>(
|
|
id: string | Entity,
|
|
dto: Partial<Dto>,
|
|
extendsFields: Array<keyof Dto & string> = [],
|
|
): Promise<false | never> {
|
|
if (typeof id !== 'string') {
|
|
dto = Object.assign({}, id, dto);
|
|
id = id.id;
|
|
}
|
|
const qb = this.repository.createQueryBuilder('entity');
|
|
const compareFields = this.getCompareFields(dto, [
|
|
...this.uniqueFields,
|
|
...extendsFields,
|
|
]);
|
|
const flatCompareFields = compareFields.flat();
|
|
if (compareFields.length > 0) {
|
|
qb.andWhere(
|
|
new Brackets((bqb) => {
|
|
for (const key of compareFields) {
|
|
if (Array.isArray(key)) {
|
|
if (key.length > 0) {
|
|
bqb.orWhere(
|
|
new Brackets((bbqb) =>
|
|
key.forEach((k) => {
|
|
bbqb.andWhere(`entity.${k} = :${k}`);
|
|
}),
|
|
),
|
|
);
|
|
}
|
|
} else {
|
|
bqb.orWhere(`entity.${key} = :${key}`);
|
|
}
|
|
}
|
|
}),
|
|
);
|
|
} else {
|
|
return false;
|
|
}
|
|
qb.andWhere(`entity.id <> :id`);
|
|
qb.setParameters(Object.assign({}, dto, { id }));
|
|
qb.addSelect(flatCompareFields.map((f) => `entity.${f} AS entity_${f}`));
|
|
return await this.checkDuplicateFields(qb, dto, compareFields);
|
|
}
|
|
|
|
async findOne(entity: Entity): Promise<Entity>;
|
|
async findOne(id: string): Promise<Entity>;
|
|
async findOne(idOrEntity: string | Entity): Promise<Entity> {
|
|
if (idOrEntity instanceof Object) {
|
|
return idOrEntity;
|
|
}
|
|
return await this.repository.findOneOrFail({
|
|
where: { id: idOrEntity },
|
|
});
|
|
}
|
|
|
|
checkProperty<T>(
|
|
obj: T,
|
|
field: keyof T,
|
|
whitelist: Array<typeof obj[keyof T]>,
|
|
errMsg: string,
|
|
) {
|
|
if (!whitelist.some((item) => obj[field] === item)) {
|
|
throw new UnprocessableEntityException(errMsg);
|
|
}
|
|
}
|
|
|
|
async canYouRemoveWithIds(ids: string[]): Promise<void> {
|
|
return;
|
|
}
|
|
|
|
private getCompareFields<Dto>(
|
|
dto: Partial<Dto>,
|
|
fields: Array<keyof Dto | Array<keyof Dto>>,
|
|
) {
|
|
if (!Array.isArray(fields)) {
|
|
return [];
|
|
}
|
|
const compareFields = [];
|
|
for (const field of fields as Array<keyof Dto | Array<keyof Dto>>) {
|
|
if (Array.isArray(field)) {
|
|
const tmpFields = [];
|
|
for (const f of field) {
|
|
if (dto[f] !== undefined) {
|
|
tmpFields.push(f);
|
|
}
|
|
}
|
|
compareFields.push(tmpFields);
|
|
} else if (dto[field] !== undefined) {
|
|
compareFields.push(field);
|
|
}
|
|
}
|
|
return compareFields;
|
|
}
|
|
|
|
private async checkDuplicateFields<Dto = { [p: string]: any }>(
|
|
qb: SelectQueryBuilder<Entity>,
|
|
dto: Dto,
|
|
compareFields: Array<keyof Dto & string>,
|
|
): Promise<false | never> {
|
|
const existingEntity = await qb.getOne();
|
|
if (!existingEntity) {
|
|
return false;
|
|
}
|
|
const duplicateEntityInfo: DuplicateFieldInfo[] = [];
|
|
for (const key of compareFields) {
|
|
if (existingEntity[key as string] === dto[key]) {
|
|
duplicateEntityInfo.push({
|
|
property: key,
|
|
value: dto[key],
|
|
});
|
|
}
|
|
}
|
|
throw new DuplicateEntityException(duplicateEntityInfo);
|
|
}
|
|
|
|
protected async getTotalQueryBuilder(
|
|
qb: SelectQueryBuilder<Entity>,
|
|
): Promise<SelectQueryBuilder<Entity>> {
|
|
const totalQb = qb.clone();
|
|
totalQb.offset(0).skip(0).take(undefined).limit(undefined);
|
|
return totalQb;
|
|
}
|
|
|
|
getUpdateRows(returned: any): number {
|
|
return returned?.affected;
|
|
}
|
|
|
|
isFindOne(queryArg): boolean {
|
|
return queryArg.id !== undefined;
|
|
}
|
|
}
|