From e0d2f7652c5b1e59881cf970c9c9aa0eae76f317 Mon Sep 17 00:00:00 2001 From: liuyi Date: Tue, 27 May 2025 23:09:49 +0800 Subject: [PATCH] add constraint --- src/modules/content/dtos/tag.dto.ts | 4 ++ .../constraints/unique.exist.constraint.ts | 71 +++++++++++++++++++ src/modules/database/database.module.ts | 3 +- 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/modules/core/constraints/unique.exist.constraint.ts diff --git a/src/modules/content/dtos/tag.dto.ts b/src/modules/content/dtos/tag.dto.ts index 8ce1032..2d1363a 100644 --- a/src/modules/content/dtos/tag.dto.ts +++ b/src/modules/content/dtos/tag.dto.ts @@ -11,9 +11,12 @@ import { } from 'class-validator'; import { toNumber } from 'lodash'; +import { IsUnique } from '@/modules/core/constraints/unique.constraint'; import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator'; import { PaginateOptions } from '@/modules/database/types'; +import { TagEntity } from '../entities'; + @DtoValidation({ type: 'query' }) export class QueryTagDto implements PaginateOptions { @Transform(({ value }) => toNumber(value)) @@ -31,6 +34,7 @@ export class QueryTagDto implements PaginateOptions { @DtoValidation({ groups: ['create'] }) export class CreateTagDto { + @IsUnique(TagEntity, { groups: ['create'], message: 'The label names are repeated' }) @MaxLength(255, { always: true, message: 'The maximum length of the label name is $constraint1', diff --git a/src/modules/core/constraints/unique.exist.constraint.ts b/src/modules/core/constraints/unique.exist.constraint.ts new file mode 100644 index 0000000..345ef5c --- /dev/null +++ b/src/modules/core/constraints/unique.exist.constraint.ts @@ -0,0 +1,71 @@ +import { + registerDecorator, + ValidationArguments, + ValidationOptions, + ValidatorConstraintInterface, +} from 'class-validator'; +import { isNil, merge } from 'lodash'; +import { DataSource, Not, ObjectType } from 'typeorm'; + +type Condition = { + entity: ObjectType; + + ignore?: string; + + ignoreKey?: string; + + property?: string; +}; + +export class UniqueExistConstraint implements ValidatorConstraintInterface { + constructor(protected dataSource: DataSource) {} + + async validate(value: any, args?: ValidationArguments): Promise { + const config: Omit = { + ignore: 'id', + property: args.property, + }; + const condition = ('entity' in args.constraints[0] + ? merge(config, args.constraints[0]) + : { ...config, entity: args.constraints[0] }) as unknown as Required; + if (!condition.entity) { + return false; + } + const ignoreValue = (args.object as any)[ + isNil(condition.ignoreKey) ? condition.ignore : condition.ignoreKey + ]; + if (ignoreValue === undefined) { + return false; + } + const repo = this.dataSource.getRepository(condition.entity); + return isNil( + await repo.findOne({ + where: { [condition.property]: value, [condition.ignore]: Not(ignoreValue) }, + withDeleted: true, + }), + ); + } + defaultMessage?(args?: ValidationArguments): string { + const { entity, property } = args.constraints[0]; + const queryProperty = property ?? args.property; + if (!(args.object as any).getManager) { + return 'getManager function not been found!'; + } + if (!entity) { + return 'Model not been specified!'; + } + return `${queryProperty} of ${entity.name} must been unique!`; + } +} + +export function IsUniqueExist(params: ObjectType | Condition, options?: ValidationOptions) { + return (object: RecordAny, propertyName: string) => { + registerDecorator({ + target: object.constructor, + propertyName, + options, + constraints: [params], + validator: UniqueExistConstraint, + }); + }; +} diff --git a/src/modules/database/database.module.ts b/src/modules/database/database.module.ts index 5e3017d..6b16230 100644 --- a/src/modules/database/database.module.ts +++ b/src/modules/database/database.module.ts @@ -6,6 +6,7 @@ import { DataSource, ObjectType } from 'typeorm'; import { CUSTOM_REPOSITORY_METADATA } from '@/modules/database/constants'; import { DataExistConstraint } from '../core/constraints/data.exist.constraint'; +import { UniqueConstraint } from '../core/constraints/unique.constraint'; @Module({}) export class DatabaseModule { @@ -14,7 +15,7 @@ export class DatabaseModule { global: true, module: DatabaseModule, imports: [TypeOrmModule.forRoot(configRegister())], - providers: [DataExistConstraint], + providers: [DataExistConstraint, UniqueConstraint], }; } static forRepository>(