add constraint

This commit is contained in:
liuyi 2025-05-27 23:29:14 +08:00
parent e0d2f7652c
commit 43d34250e2
4 changed files with 178 additions and 2 deletions

View File

@ -0,0 +1,75 @@
import { Injectable } from '@nestjs/common';
import {
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments,
registerDecorator,
ValidationOptions,
} from 'class-validator';
import { merge, isNil } from 'lodash';
import { DataSource, ObjectType } from 'typeorm';
type Condition = {
entity: ObjectType<any>;
property?: string;
};
@ValidatorConstraint({ name: 'treeDataUnique', async: true })
@Injectable()
export class TreeUniqueConstraint implements ValidatorConstraintInterface {
constructor(private dataSource: DataSource) {}
async validate(value: any, args: ValidationArguments) {
// 获取要验证的模型和字段
const config: Omit<Condition, 'entity'> = {
property: args.property,
};
const condition = ('entity' in args.constraints[0]
? merge(config, args.constraints[0])
: {
...config,
entity: args.constraints[0],
}) as unknown as Required<Condition>;
if (!condition.entity) {
return false;
}
try {
// 查询是否存在数据,如果已经存在则验证失败
const repo = this.dataSource.getRepository(condition.entity);
return isNil(
await repo.findOne({ where: { [condition.property]: value }, withDeleted: true }),
);
} catch (err) {
// 如果数据库操作异常则验证失败
return false;
}
}
defaultMessage(args: ValidationArguments) {
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 IsUnique(
params: ObjectType<any> | Condition,
validationOptions?: ValidationOptions,
) {
return (object: Record<string, any>, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [params],
validator: TreeUniqueConstraint,
});
};
}

View File

@ -0,0 +1,89 @@
import { Injectable } from '@nestjs/common';
import {
registerDecorator,
ValidationArguments,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import { isNil, merge } from 'lodash';
import { DataSource, Not, ObjectType } from 'typeorm';
type Condition = {
entity: ObjectType<any>;
ignore?: string;
ignoreKey?: string;
property?: string;
};
@ValidatorConstraint({ name: 'treeDataUniqueExist', async: true })
@Injectable()
export class TreeUniqueExistContraint implements ValidatorConstraintInterface {
constructor(private dataSource: DataSource) {}
async validate(value: any, args: ValidationArguments) {
const config: Omit<Condition, 'entity'> = {
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<Condition>;
if (!condition.entity) {
return false;
}
// 在传入的dto数据中获取需要忽略的字段的值
const ignoreValue = (args.object as any)[
isNil(condition.ignoreKey) ? condition.ignore : condition.ignoreKey
];
// 如果忽略字段不存在则验证失败
if (ignoreValue === undefined) {
return false;
}
// 通过entity获取repository
const repo = this.dataSource.getRepository(condition.entity);
// 查询忽略字段之外的数据是否对queryProperty的值唯一
return isNil(
await repo.findOne({
where: {
[condition.property]: value,
[condition.ignore]: Not(ignoreValue),
},
withDeleted: true,
}),
);
}
defaultMessage(args: ValidationArguments) {
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<any> | Condition,
validationOptions?: ValidationOptions,
) {
return (object: Record<string, any>, propertyName: string) => {
registerDecorator({
target: object.constructor,
propertyName,
options: validationOptions,
constraints: [params],
validator: TreeUniqueExistContraint,
});
};
}

View File

@ -1,7 +1,9 @@
import { Injectable } from '@nestjs/common';
import {
registerDecorator,
ValidationArguments,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import { isNil, merge } from 'lodash';
@ -16,7 +18,8 @@ type Condition = {
property?: string;
};
@Injectable()
@ValidatorConstraint({ name: 'dataUniqueExist', async: true })
export class UniqueExistConstraint implements ValidatorConstraintInterface {
constructor(protected dataSource: DataSource) {}

View File

@ -6,7 +6,10 @@ import { DataSource, ObjectType } from 'typeorm';
import { CUSTOM_REPOSITORY_METADATA } from '@/modules/database/constants';
import { DataExistConstraint } from '../core/constraints/data.exist.constraint';
import { TreeUniqueConstraint } from '../core/constraints/tree.unique.constraint';
import { TreeUniqueExistContraint } from '../core/constraints/tree.unique.exist.constraint';
import { UniqueConstraint } from '../core/constraints/unique.constraint';
import { UniqueExistConstraint } from '../core/constraints/unique.exist.constraint';
@Module({})
export class DatabaseModule {
@ -15,7 +18,13 @@ export class DatabaseModule {
global: true,
module: DatabaseModule,
imports: [TypeOrmModule.forRoot(configRegister())],
providers: [DataExistConstraint, UniqueConstraint],
providers: [
DataExistConstraint,
UniqueConstraint,
UniqueExistConstraint,
TreeUniqueConstraint,
TreeUniqueExistContraint,
],
};
}
static forRepository<T extends Type<any>>(