Compare commits
No commits in common. "b8cc65a7682cc4325fa26a2a5fc9f0c04af389f4" and "46253b3a4f6abb3351a4e66ca25f22b3ffb8d327" have entirely different histories.
b8cc65a768
...
46253b3a4f
@ -1,4 +1,4 @@
|
|||||||
import { IsDefined, IsUUID } from 'class-validator';
|
import { IsUUID, IsDefined } from 'class-validator';
|
||||||
|
|
||||||
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Exclude, Expose, Type } from 'class-transformer';
|
import { Exclude, Expose, Type } from 'class-transformer';
|
||||||
import type { Relation } from 'typeorm';
|
|
||||||
import {
|
import {
|
||||||
BaseEntity,
|
BaseEntity,
|
||||||
Column,
|
Column,
|
||||||
@ -11,6 +10,8 @@ import {
|
|||||||
TreeParent,
|
TreeParent,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import type { Relation } from 'typeorm';
|
||||||
|
|
||||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||||
|
|
||||||
@Exclude()
|
@Exclude()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Exclude, Expose } from 'class-transformer';
|
import { Exclude, Expose } from 'class-transformer';
|
||||||
import type { Relation } from 'typeorm';
|
|
||||||
import { Column, Entity, ManyToMany, PrimaryColumn } from 'typeorm';
|
import { Column, Entity, ManyToMany, PrimaryColumn } from 'typeorm';
|
||||||
|
import type { Relation } from 'typeorm';
|
||||||
|
|
||||||
import { PostEntity } from '@/modules/content/entities/post.entity';
|
import { PostEntity } from '@/modules/content/entities/post.entity';
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import { QueryHook } from '@/modules/database/types';
|
|||||||
type FindCommentTreeOptions = FindTreeOptions & {
|
type FindCommentTreeOptions = FindTreeOptions & {
|
||||||
addQuery?: QueryHook<CommentEntity>;
|
addQuery?: QueryHook<CommentEntity>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@CustomRepository(CommentEntity)
|
@CustomRepository(CommentEntity)
|
||||||
export class CommentRepository extends BaseTreeRepository<CommentEntity> {
|
export class CommentRepository extends BaseTreeRepository<CommentEntity> {
|
||||||
protected _qbName = 'comment';
|
protected _qbName = 'comment';
|
||||||
|
@ -4,7 +4,6 @@ import { deepMerge } from '@/modules/core/helpers';
|
|||||||
|
|
||||||
export class SanitizeService {
|
export class SanitizeService {
|
||||||
protected config: sanitizeHtml.IOptions = {};
|
protected config: sanitizeHtml.IOptions = {};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.config = {
|
this.config = {
|
||||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'code']),
|
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img', 'code']),
|
||||||
|
@ -13,7 +13,6 @@ export class MatchConstraint implements ValidatorConstraintInterface {
|
|||||||
const relatedValue = (validationArguments.object as any)[relatedProperty];
|
const relatedValue = (validationArguments.object as any)[relatedProperty];
|
||||||
return (value === relatedValue) !== reverse;
|
return (value === relatedValue) !== reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
const [relatedProperty, reverse] = validationArguments.constraints;
|
const [relatedProperty, reverse] = validationArguments.constraints;
|
||||||
return `${relatedProperty} and ${validationArguments.property} ${
|
return `${relatedProperty} and ${validationArguments.property} ${
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
registerDecorator,
|
|
||||||
ValidationArguments,
|
ValidationArguments,
|
||||||
ValidationOptions,
|
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
|
ValidationOptions,
|
||||||
|
registerDecorator,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
type ModelType = 1 | 2 | 3 | 4 | 5;
|
type ModelType = 1 | 2 | 3 | 4 | 5;
|
||||||
@ -30,7 +30,6 @@ export class PasswordConstraint implements ValidatorConstraintInterface {
|
|||||||
return /\d/.test(value) && /[A-Za-z]/.test(value);
|
return /\d/.test(value) && /[A-Za-z]/.test(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
return "($value) 's format error";
|
return "($value) 's format error";
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
registerDecorator,
|
|
||||||
ValidationArguments,
|
ValidationArguments,
|
||||||
ValidationOptions,
|
|
||||||
ValidatorConstraint,
|
ValidatorConstraint,
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
|
ValidationOptions,
|
||||||
|
registerDecorator,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { isArray, isNil } from 'lodash';
|
import { isArray, isNil } from 'lodash';
|
||||||
import { DataSource, ObjectType, Repository } from 'typeorm';
|
import { ObjectType, Repository, DataSource } from 'typeorm';
|
||||||
|
|
||||||
type Condition = {
|
type Condition = {
|
||||||
entity: ObjectType<any>;
|
entity: ObjectType<any>;
|
||||||
@ -52,7 +52,6 @@ export class DataExistConstraint implements ValidatorConstraintInterface {
|
|||||||
const item = await repo.findOne({ where: { [map]: value } });
|
const item = await repo.findOne({ where: { [map]: value } });
|
||||||
return !!item;
|
return !!item;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
if (!validationArguments.constraints[0]) {
|
if (!validationArguments.constraints[0]) {
|
||||||
return 'Model not been specified!';
|
return 'Model not been specified!';
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
registerDecorator,
|
|
||||||
ValidationArguments,
|
|
||||||
ValidationOptions,
|
|
||||||
ValidatorConstraint,
|
ValidatorConstraint,
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
|
ValidationArguments,
|
||||||
|
registerDecorator,
|
||||||
|
ValidationOptions,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { isNil, merge } from 'lodash';
|
import { merge, isNil } from 'lodash';
|
||||||
import { DataSource, ObjectType } from 'typeorm';
|
import { DataSource, ObjectType } from 'typeorm';
|
||||||
|
|
||||||
type Condition = {
|
type Condition = {
|
||||||
|
@ -2,9 +2,9 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import {
|
import {
|
||||||
registerDecorator,
|
registerDecorator,
|
||||||
ValidationArguments,
|
ValidationArguments,
|
||||||
ValidationOptions,
|
|
||||||
ValidatorConstraint,
|
ValidatorConstraint,
|
||||||
ValidatorConstraintInterface,
|
ValidatorConstraintInterface,
|
||||||
|
ValidationOptions,
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { isNil, merge } from 'lodash';
|
import { isNil, merge } from 'lodash';
|
||||||
import { DataSource, ObjectType } from 'typeorm';
|
import { DataSource, ObjectType } from 'typeorm';
|
||||||
@ -39,7 +39,6 @@ export class UniqueConstraint implements ValidatorConstraintInterface {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||||
const { entity, property } = validationArguments.constraints[0];
|
const { entity, property } = validationArguments.constraints[0];
|
||||||
const queryProperty = property ?? validationArguments.property;
|
const queryProperty = property ?? validationArguments.property;
|
||||||
|
@ -18,7 +18,6 @@ type Condition = {
|
|||||||
|
|
||||||
property?: string;
|
property?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ValidatorConstraint({ name: 'dataUniqueExist', async: true })
|
@ValidatorConstraint({ name: 'dataUniqueExist', async: true })
|
||||||
export class UniqueExistConstraint implements ValidatorConstraintInterface {
|
export class UniqueExistConstraint implements ValidatorConstraintInterface {
|
||||||
@ -49,7 +48,6 @@ export class UniqueExistConstraint implements ValidatorConstraintInterface {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultMessage?(args?: ValidationArguments): string {
|
defaultMessage?(args?: ValidationArguments): string {
|
||||||
const { entity, property } = args.constraints[0];
|
const { entity, property } = args.constraints[0];
|
||||||
const queryProperty = property ?? args.property;
|
const queryProperty = property ?? args.property;
|
||||||
|
@ -24,7 +24,7 @@ import { DBOptions } from './types';
|
|||||||
export class DatabaseModule {
|
export class DatabaseModule {
|
||||||
static async forRoot(configure: Configure): Promise<DynamicModule> {
|
static async forRoot(configure: Configure): Promise<DynamicModule> {
|
||||||
if (!configure.has('database')) {
|
if (!configure.has('database')) {
|
||||||
await panic({ message: 'Database config not exists' });
|
panic({ message: 'Database config not exists' });
|
||||||
}
|
}
|
||||||
const { connections } = await configure.get<DBOptions>('database');
|
const { connections } = await configure.get<DBOptions>('database');
|
||||||
const imports: ModuleMetadata['imports'] = [];
|
const imports: ModuleMetadata['imports'] = [];
|
||||||
@ -46,7 +46,6 @@ export class DatabaseModule {
|
|||||||
providers,
|
providers,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static forRepository<T extends Type<any>>(
|
static forRepository<T extends Type<any>>(
|
||||||
repositories: T[],
|
repositories: T[],
|
||||||
datasourceName?: string,
|
datasourceName?: string,
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
import { PickType } from '@nestjs/swagger';
|
import { PickType } from '@nestjs/swagger';
|
||||||
|
|
||||||
import { IsDefined, IsUUID, Length } from 'class-validator';
|
import { Length } from 'class-validator';
|
||||||
|
|
||||||
import { IsPassword } from '@/modules/core/constraints/password.constraint';
|
import { IsPassword } from '@/modules/core/constraints/password.constraint';
|
||||||
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
||||||
import { UserCommonDto } from '@/modules/user/dtos/user.common.dto';
|
import { UserCommonDto } from '@/modules/user/dtos/user.common.dto';
|
||||||
|
|
||||||
import { UserValidateGroup } from '../constants';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新用户信息
|
* 更新用户信息
|
||||||
*/
|
*/
|
||||||
@DtoValidation({ groups: [UserValidateGroup.ACCOUNT_UPDATE] })
|
@DtoValidation({ whitelist: false, groups: [UserValidateGroup.ACCOUNT_UPDATE] })
|
||||||
export class UpdateAccountDto extends PickType(UserCommonDto, ['username', 'nickname']) {
|
export class UpdateAccountDto extends PickType(UserCommonDto, ['username', 'nickname']) {
|
||||||
/**
|
/**
|
||||||
* 待更新的用户ID
|
* 待更新的用户ID
|
||||||
|
@ -61,7 +61,7 @@ export class UserCommonDto {
|
|||||||
)
|
)
|
||||||
@IsMatchPhone(
|
@IsMatchPhone(
|
||||||
undefined,
|
undefined,
|
||||||
{ strictMode: true },
|
{ strictMode: trus },
|
||||||
{ message: '手机格式错误,示例: +86.15005255555', always: true },
|
{ message: '手机格式错误,示例: +86.15005255555', always: true },
|
||||||
)
|
)
|
||||||
@IsOptional({
|
@IsOptional({
|
||||||
|
@ -4,7 +4,7 @@ import { IsDefined, IsEnum, IsUUID } from 'class-validator';
|
|||||||
|
|
||||||
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
import { DtoValidation } from '@/modules/core/decorator/dto.validation.decorator';
|
||||||
import { PaginateWithTrashedDto } from '@/modules/restful/dtos/paginate-width-trashed.dto';
|
import { PaginateWithTrashedDto } from '@/modules/restful/dtos/paginate-width-trashed.dto';
|
||||||
import { UserOrderType, UserValidateGroup } from '@/modules/user/constants';
|
import { UserOrderType } from '@/modules/user/constants';
|
||||||
import { UserCommonDto } from '@/modules/user/dtos/user.common.dto';
|
import { UserCommonDto } from '@/modules/user/dtos/user.common.dto';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
/* eslint-disable import/no-extraneous-dependencies */
|
|
||||||
import { ForbiddenException, Injectable } from '@nestjs/common';
|
|
||||||
|
|
||||||
import { FastifyRequest as Request } from 'fastify';
|
|
||||||
|
|
||||||
import { ExtractJwt } from 'passport-jwt';
|
|
||||||
|
|
||||||
import { Configure } from '@/modules/config/configure';
|
|
||||||
|
|
||||||
import { getTime } from '@/modules/core/helpers/time';
|
|
||||||
import { RegisterDto } from '@/modules/user/dtos/auth.dto';
|
|
||||||
import { UserEntity } from '@/modules/user/entities/UserEntity';
|
|
||||||
import { TokenService } from '@/modules/user/services/token.service';
|
|
||||||
import { decrypt } from '@/modules/user/utils';
|
|
||||||
|
|
||||||
import { UpdatePasswordDto } from '../dtos/account.dto';
|
|
||||||
import { UserRepository } from '../repositories/UserRepository';
|
|
||||||
|
|
||||||
import { UserService } from './user.service';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AuthService {
|
|
||||||
constructor(
|
|
||||||
protected configure: Configure,
|
|
||||||
protected userService: UserService,
|
|
||||||
protected tokenService: TokenService,
|
|
||||||
protected userRepository: UserRepository,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户登录验证
|
|
||||||
* @param credential
|
|
||||||
* @param password
|
|
||||||
*/
|
|
||||||
async validateUser(credential: string, password: string) {
|
|
||||||
const user = await this.userService.findOneByCredential(credential, async (query) =>
|
|
||||||
query.addSelect('user.password'),
|
|
||||||
);
|
|
||||||
if (user && decrypt(password, user.password)) {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录用户,并生成新的accessToken和refreshToken
|
|
||||||
* @param user
|
|
||||||
*/
|
|
||||||
async login(user: UserEntity) {
|
|
||||||
const now = await getTime(this.configure);
|
|
||||||
const { accessToken } = await this.tokenService.generateAccessToken(user, now);
|
|
||||||
return accessToken.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注销登录
|
|
||||||
* @param req
|
|
||||||
*/
|
|
||||||
async logout(req: Request) {
|
|
||||||
const accessToken = ExtractJwt.fromAuthHeaderAsBearerToken()(req as any);
|
|
||||||
if (accessToken) {
|
|
||||||
await this.tokenService.removeAccessToken(accessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { msg: 'logout_success' };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录用户后生成新的token和refreshToken
|
|
||||||
* @param id
|
|
||||||
*/
|
|
||||||
async createToken(id: string) {
|
|
||||||
const now = await getTime(this.configure);
|
|
||||||
let user: UserEntity;
|
|
||||||
try {
|
|
||||||
user = await this.userService.detail(id);
|
|
||||||
} catch (err) {
|
|
||||||
throw new ForbiddenException(err);
|
|
||||||
}
|
|
||||||
const { accessToken } = await this.tokenService.generateAccessToken(user, now);
|
|
||||||
return accessToken.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用用户名密码注册用户
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
async register(data: RegisterDto) {
|
|
||||||
const { username, nickname, password } = data;
|
|
||||||
const user = await this.userService.create({
|
|
||||||
username,
|
|
||||||
nickname,
|
|
||||||
password,
|
|
||||||
} as any);
|
|
||||||
return this.userService.findOneByCondition({ id: user.id });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户密码
|
|
||||||
* @param user
|
|
||||||
* @param password
|
|
||||||
* @param oldPassword
|
|
||||||
*/
|
|
||||||
async changePassword(user: UserEntity, { password, oldPassword }: UpdatePasswordDto) {
|
|
||||||
const item = await this.userRepository.findOneOrFail({
|
|
||||||
select: ['password'],
|
|
||||||
where: { id: user.id },
|
|
||||||
});
|
|
||||||
if (decrypt(oldPassword, item.password)) {
|
|
||||||
await this.userRepository.save({ id: user.id, password }, { reload: true });
|
|
||||||
return this.userService.detail(user.id);
|
|
||||||
}
|
|
||||||
throw new ForbiddenException('old password do not match');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,16 @@
|
|||||||
/* eslint-disable import/no-extraneous-dependencies */
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { JwtModule, JwtModuleOptions, JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { FastifyReply as Response } from 'fastify';
|
|
||||||
import jwt from 'jsonwebtoken';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
import { Configure } from '@/modules/config/configure';
|
import { Configure } from '@/modules/config/configure';
|
||||||
import { getTime } from '@/modules/core/helpers/time';
|
import { getTime } from '@/modules/core/helpers/time';
|
||||||
import { defaultUserConfig, getUserConfig } from '@/modules/user/config';
|
import { getUserConfig } from '@/modules/user/config';
|
||||||
import { UserEntity } from '@/modules/user/entities/UserEntity';
|
import { UserEntity } from '@/modules/user/entities/UserEntity';
|
||||||
import { AccessTokenEntity } from '@/modules/user/entities/access.token.entity';
|
import { AccessTokenEntity } from '@/modules/user/entities/access.token.entity';
|
||||||
import { RefreshTokenEntity } from '@/modules/user/entities/refresh.token.entity';
|
import { RefreshTokenEntity } from '@/modules/user/entities/refresh.token.entity';
|
||||||
import { JwtConfig, JwtPayload, UserConfig } from '@/modules/user/types';
|
import { JwtConfig, JwtPayload } from '@/modules/user/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 令牌服务
|
* 令牌服务
|
||||||
@ -83,7 +79,7 @@ export class TokenService {
|
|||||||
const config = await getUserConfig<JwtConfig>(this.configure, 'jwt');
|
const config = await getUserConfig<JwtConfig>(this.configure, 'jwt');
|
||||||
const refreshTokenPayload = { uuid: uuid() };
|
const refreshTokenPayload = { uuid: uuid() };
|
||||||
const refreshToken = new RefreshTokenEntity();
|
const refreshToken = new RefreshTokenEntity();
|
||||||
refreshToken.value = jwt.sign(
|
refreshToken.value = this.jwtService.sign(
|
||||||
refreshTokenPayload,
|
refreshTokenPayload,
|
||||||
this.configure.env.get('USER_REFRESH_TOKEN_EXPIRED', 'my-refresh-secret'),
|
this.configure.env.get('USER_REFRESH_TOKEN_EXPIRED', 'my-refresh-secret'),
|
||||||
);
|
);
|
||||||
@ -134,7 +130,7 @@ export class TokenService {
|
|||||||
* @param token
|
* @param token
|
||||||
*/
|
*/
|
||||||
async verifyAccessToken(token: AccessTokenEntity) {
|
async verifyAccessToken(token: AccessTokenEntity) {
|
||||||
const result = jwt.verify(
|
const result = this.jwtService.verify(
|
||||||
token.value,
|
token.value,
|
||||||
this.configure.env.get('USER_TOKEN_SECRET', 'my-access-secret'),
|
this.configure.env.get('USER_TOKEN_SECRET', 'my-access-secret'),
|
||||||
);
|
);
|
||||||
@ -143,25 +139,4 @@ export class TokenService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JwtModuleFactory(configure: Configure) {
|
|
||||||
return JwtModule.registerAsync({
|
|
||||||
useFactory: async (): Promise<JwtModuleOptions> => {
|
|
||||||
const config = await configure.get<UserConfig>(
|
|
||||||
'user',
|
|
||||||
defaultUserConfig(configure),
|
|
||||||
);
|
|
||||||
const options: JwtModuleOptions = {
|
|
||||||
secret: configure.env.get('USER_TOKEN_SECRET', 'my-access-secret'),
|
|
||||||
verifyOptions: {
|
|
||||||
ignoreExpiration: !configure.env.isProd(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (configure.env.isProd()) {
|
|
||||||
options.signOptions = { expiresIn: `${config.jwt.tokenExpired}s` };
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
|
|
||||||
import { isNil } from 'lodash';
|
|
||||||
import { DataSource, EntityNotFoundError, SelectQueryBuilder } from 'typeorm';
|
|
||||||
|
|
||||||
import { Configure } from '@/modules/config/configure';
|
|
||||||
import { BaseService } from '@/modules/database/base/service';
|
|
||||||
|
|
||||||
import { QueryHook } from '@/modules/database/types';
|
|
||||||
|
|
||||||
import { CreateUserDto, QueryUserDto, UpdateUserDto } from '../dtos/user.dto';
|
|
||||||
import { UserEntity } from '../entities/UserEntity';
|
|
||||||
import { UserRepository } from '../repositories/UserRepository';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UserService extends BaseService<UserEntity, UserRepository> {
|
|
||||||
protected enableTrash = true;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
protected configure: Configure,
|
|
||||||
protected dataSource: DataSource,
|
|
||||||
protected userRepository: UserRepository,
|
|
||||||
) {
|
|
||||||
super(userRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建用户
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
async create(data: CreateUserDto): Promise<UserEntity> {
|
|
||||||
const user = await this.userRepository.save(data, { reload: true });
|
|
||||||
return this.detail(user.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新用户
|
|
||||||
* @param data
|
|
||||||
*/
|
|
||||||
async update(data: UpdateUserDto): Promise<UserEntity> {
|
|
||||||
const updated = await this.userRepository.save(data, { reload: true });
|
|
||||||
return this.detail(updated.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据用户用户凭证查询用户
|
|
||||||
* @param credential
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
async findOneByCredential(credential: string, callback?: QueryHook<UserEntity>) {
|
|
||||||
let query = this.userRepository.buildBaseQuery();
|
|
||||||
if (callback) {
|
|
||||||
query = await callback(query);
|
|
||||||
}
|
|
||||||
return query
|
|
||||||
.where('user.username = :credential', { credential })
|
|
||||||
.orWhere('user.email = :credential', { credential })
|
|
||||||
.orWhere('user.phone = :credential', { credential })
|
|
||||||
.getOne();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据对象条件查找用户,不存在则抛出异常
|
|
||||||
* @param condition
|
|
||||||
* @param callback
|
|
||||||
*/
|
|
||||||
async findOneByCondition(condition: { [key: string]: any }, callback?: QueryHook<UserEntity>) {
|
|
||||||
let query = this.userRepository.buildBaseQuery();
|
|
||||||
if (callback) {
|
|
||||||
query = await callback(query);
|
|
||||||
}
|
|
||||||
const wheres = Object.fromEntries(
|
|
||||||
Object.entries(condition).map(([key, value]) => [key, value]),
|
|
||||||
);
|
|
||||||
const user = query.where(wheres).getOne();
|
|
||||||
if (!user) {
|
|
||||||
throw new EntityNotFoundError(UserEntity, Object.keys(condition).join(','));
|
|
||||||
}
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async buildListQB(
|
|
||||||
queryBuilder: SelectQueryBuilder<UserEntity>,
|
|
||||||
options: QueryUserDto,
|
|
||||||
callback?: QueryHook<UserEntity>,
|
|
||||||
) {
|
|
||||||
const { orderBy } = options;
|
|
||||||
const qb = await super.buildListQB(queryBuilder, options, callback);
|
|
||||||
if (!isNil(orderBy)) {
|
|
||||||
qb.orderBy(`${this.repository.qbName}.${orderBy}`, 'ASC');
|
|
||||||
}
|
|
||||||
return qb;
|
|
||||||
}
|
|
||||||
}
|
|
16
typings/global.d.ts
vendored
16
typings/global.d.ts
vendored
@ -15,20 +15,20 @@ declare type RePartial<T> = {
|
|||||||
[P in keyof T]?: T[P] extends (infer U)[] | undefined
|
[P in keyof T]?: T[P] extends (infer U)[] | undefined
|
||||||
? RePartial<U>[]
|
? RePartial<U>[]
|
||||||
: T[P] extends object | undefined
|
: T[P] extends object | undefined
|
||||||
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
||||||
? T[P]
|
? T[P]
|
||||||
: RePartial<T[P]>
|
: RePartial<T[P]>
|
||||||
: T[P];
|
: T[P];
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type ReRequired<T> = {
|
declare type ReRequired<T> = {
|
||||||
[P in keyof T]-?: T[P] extends (infer U)[] | undefined
|
[P in keyof T]-?: T[P] extends (infer U)[] | undefined
|
||||||
? ReRequired<U>[]
|
? ReRequired<U>[]
|
||||||
: T[P] extends object | undefined
|
: T[P] extends object | undefined
|
||||||
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
? T[P] extends ((...args: any[]) => any) | ClassType<T[P]> | undefined
|
||||||
? T[P]
|
? T[P]
|
||||||
: ReRequired<T[P]>
|
: ReRequired<T[P]>
|
||||||
: T[P];
|
: T[P];
|
||||||
};
|
};
|
||||||
|
|
||||||
declare type WrapperType<T> = T;
|
declare type WrapperType<T> = T;
|
||||||
|
Loading…
Reference in New Issue
Block a user