diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..95b9752 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch via NPM", + "request": "launch", + "runtimeArgs": [ + "run", + "dev" + ], + "runtimeExecutable": "npm", + "skipFiles": [ + "/**" + ], + "type": "node" + } + ] +} \ No newline at end of file diff --git a/apps/api/src/constants/error-code.constant.ts b/apps/api/src/constants/error-code.constant.ts new file mode 100644 index 0000000..6774cab --- /dev/null +++ b/apps/api/src/constants/error-code.constant.ts @@ -0,0 +1,43 @@ +export enum ErrorEnum { + DEFAULT = '0:未知错误', + SERVER_ERROR = '500:服务繁忙,请稍后再试', + + SYSTEM_USER_EXISTS = '1001:系统用户已存在', + INVALID_VERIFICATION_CODE = '1002:验证码填写有误', + INVALID_USERNAME_PASSWORD = '1003:用户名密码有误', + NODE_ROUTE_EXISTS = '1004:节点路由已存在', + PERMISSION_REQUIRES_PARENT = '1005:权限必须包含父节点', + ILLEGAL_OPERATION_DIRECTORY_PARENT = '1006:非法操作:该节点仅支持目录类型父节点', + ILLEGAL_OPERATION_CANNOT_CONVERT_NODE_TYPE = '1007:非法操作:节点类型无法直接转换', + ROLE_HAS_ASSOCIATED_USERS = '1008:该角色存在关联用户,请先删除关联用户', + DEPARTMENT_HAS_ASSOCIATED_USERS = '1009:该部门存在关联用户,请先删除关联用户', + DEPARTMENT_HAS_ASSOCIATED_ROLES = '1010:该部门存在关联角色,请先删除关联角色', + PASSWORD_MISMATCH = '1011:旧密码与原密码不一致', + LOGOUT_OWN_SESSION = '1012:如想下线自身可右上角退出', + NOT_ALLOWED_TO_LOGOUT_USER = '1013:不允许下线该用户', + PARENT_MENU_NOT_FOUND = '1014:父级菜单不存在', + DEPARTMENT_HAS_CHILD_DEPARTMENTS = '1015:该部门存在子部门,请先删除子部门', + SYSTEM_BUILTIN_FUNCTION_NOT_ALLOWED = '1016:系统内置功能不允许操作', + USER_NOT_FOUND = '1017:用户不存在', + UNABLE_TO_FIND_DEPARTMENT_FOR_USER = '1018:无法查找当前用户所属部门', + DEPARTMENT_NOT_FOUND = '1019:部门不存在', + PARAMETER_CONFIG_KEY_EXISTS = '1021:参数配置键值对已存在', + DEFAULT_ROLE_NOT_FOUND = '1022:所分配的默认角色不存在', + + INVALID_LOGIN = '1101:登录无效,请重新登录', + NO_PERMISSION = '1102:无权限访问', + ONLY_ADMIN_CAN_LOGIN = '1103:不是管理员,无法登录', + REQUEST_INVALIDATED = '1104:当前请求已失效', + ACCOUNT_LOGGED_IN_ELSEWHERE = '1105:您的账号已在其他地方登录', + GUEST_ACCOUNT_RESTRICTED_OPERATION = '1106:游客账号不允许操作', + REQUESTED_RESOURCE_NOT_FOUND = '1107:所请求的资源不存在', + + TOO_MANY_REQUESTS = '1201:请求频率过快,请一分钟后再试', + MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY = '1202:一天最多发送5条验证码', + VERIFICATION_CODE_SEND_FAILED = '1203:验证码发送失败', + + INSECURE_MISSION = '1301:不安全的任务,确保执行的加入@Mission注解', + EXECUTED_MISSION_NOT_FOUND = '1302:所执行的任务不存在', + MISSION_EXECUTION_FAILED = '1303:任务执行失败', + MISSION_NOT_FOUND = '1304:任务不存在', +} diff --git a/apps/api/src/constants/error.ts b/apps/api/src/constants/error.ts deleted file mode 100644 index a54a808..0000000 --- a/apps/api/src/constants/error.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * 业务错误码定义,请使用CODE_前缀定义 - */ -export enum ErrorEnum { - CODE_500 = '服务繁忙,请稍后再试', - CODE_1001 = '系统用户已存在', - CODE_1002 = '验证码填写有误', - CODE_1003 = '用户名密码有误', - CODE_1004 = '节点路由已存在', - CODE_1005 = '权限必须包含父节点', - CODE_1006 = '非法操作:该节点仅支持目录类型父节点', - CODE_1007 = '非法操作:节点类型无法直接转换', - CODE_1008 = '该角色存在关联用户,请先删除关联用户', - CODE_1009 = '该部门存在关联用户,请先删除关联用户', - CODE_1010 = '该部门存在关联角色,请先删除关联角色', - CODE_1011 = '旧密码与原密码不一致', - CODE_1012 = '如想下线自身可右上角退出', - CODE_1013 = '不允许下线该用户', - CODE_1014 = '父级菜单不存在', - CODE_1015 = '该部门存在子部门,请先删除子部门', - CODE_1016 = '系统内置功能不允许操作', - CODE_1017 = '用户不存在', - CODE_1018 = '无法查找当前用户所属部门', - CODE_1019 = '部门不存在', - CODE_1021 = '参数配置键值对已存在', - CODE_1022 = '所分配的默认角色不存在', - CODE_1023 = '密码格式不对', - CODE_1024 = '用户名格式不对', - CODE_1025 = '邮箱格式不对', - CODE_1026 = '手机格式不对', - - CODE_1101 = '登录无效或无权限访问', - CODE_1102 = '登录身份已过期', - CODE_1103 = '无权限访问', - CODE_1104 = '不是管理员,无法登录', - CODE_1105 = '当前请求已失效', - CODE_1106 = '您的账号已在其他地方登录', - CODE_1107 = '游客账号不允许操作', - CODE_1108 = '所请求的资源不存在', - - CODE_1201 = '请求频率过快,请一分钟后再试', - CODE_1202 = '一天最多发送5条验证码', - CODE_1203 = '验证码发送失败', - - CODE_1301 = '不安全的任务,确保执行的加入@Mission注解', - CODE_1302 = '所执行的任务不存在', - CODE_1303 = '任务执行失败', - CODE_1304 = '任务不存在', - - CODE_2004 = 'todo 数据不存在', -} diff --git a/apps/api/src/constants/response.ts b/apps/api/src/constants/response.constant.ts similarity index 100% rename from apps/api/src/constants/response.ts rename to apps/api/src/constants/response.constant.ts diff --git a/apps/api/src/constants/role.ts b/apps/api/src/constants/role.ts deleted file mode 100644 index d7621d4..0000000 --- a/apps/api/src/constants/role.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const Roles = { - ADMIN: 'admin', - USER: 'user', -} as const; - -export type Role = typeof Roles[keyof typeof Roles]; diff --git a/apps/api/src/constants/param-config.ts b/apps/api/src/constants/system.constant.ts similarity index 100% rename from apps/api/src/constants/param-config.ts rename to apps/api/src/constants/system.constant.ts diff --git a/apps/api/src/constants/type.ts b/apps/api/src/constants/type.ts deleted file mode 100644 index 83a4e4e..0000000 --- a/apps/api/src/constants/type.ts +++ /dev/null @@ -1,47 +0,0 @@ -export enum StatusTypeEnum { - /** - * 失败、禁用 - */ - Failure = 0, - Disable = 0, - - /** - * 成功、启用 - */ - Successful = 1, - Enable = 1, -} - -export enum SysLogTypeEnum { - /** - * 登录日志 - */ - Login = 1, - - /** - * 操作日志 - */ - Operate = 2, -} - -export enum MenuEntityTypeEnum { - /** - * 目录 - */ - Catalogue = 0, - - /** - * 菜单 - */ - Menu = 1, - - /** - * 权限 - */ - Permission = 2, -} - -export enum BoolTypeEnum { - False = 0, - True = 1, -} diff --git a/apps/api/src/exceptions/api.exception.ts b/apps/api/src/exceptions/api.exception.ts index 0aa2a1b..8c4e6aa 100644 --- a/apps/api/src/exceptions/api.exception.ts +++ b/apps/api/src/exceptions/api.exception.ts @@ -1,6 +1,6 @@ import { HttpException, HttpStatus } from '@nestjs/common'; -import { ErrorEnum } from '../constants/error'; +import { ErrorEnum } from '../constants/error-code.constant'; /** * 业务错误时可抛出该异常 @@ -8,17 +8,29 @@ import { ErrorEnum } from '../constants/error'; export class ApiException extends HttpException { private errorCode: number; - /** - * 业务错误,请求结果仍为200 - */ - constructor(err: ErrorEnum) { - super(`${err}`, HttpStatus.OK); - // CODE_500 str parse to 500 number - this.errorCode = Number( - Object.entries(ErrorEnum) - .find(([_, val]) => val === err)[0] - .replace('CODE_', ''), + constructor(error: ErrorEnum | string) { + if (!error.includes(':')) { + super( + HttpException.createBody({ + code: 0, + message: error, + }), + HttpStatus.OK, + ); + this.errorCode = 0; + return; + } + + const [code, message] = error.split(':'); + super( + HttpException.createBody({ + code, + message, + }), + HttpStatus.OK, ); + + this.errorCode = Number(code); } getErrorCode(): number { diff --git a/apps/api/src/exceptions/socket.exception.ts b/apps/api/src/exceptions/socket.exception.ts index 5c53fbd..3b55fe1 100644 --- a/apps/api/src/exceptions/socket.exception.ts +++ b/apps/api/src/exceptions/socket.exception.ts @@ -1,18 +1,35 @@ +import { HttpException } from '@nestjs/common'; import { WsException } from '@nestjs/websockets'; -import { ErrorEnum } from '../constants/error'; +import { ErrorEnum } from '../constants/error-code.constant'; export class SocketException extends WsException { private errorCode: number; - constructor(err: ErrorEnum) { - super(`${err}`); - // CODE_500 str parse to 500 number - this.errorCode = Number( - Object.entries(ErrorEnum) - .find(([_, val]) => val === err)[0] - .replace('CODE_', ''), + constructor(message: string); + constructor(error: ErrorEnum); + constructor(...args: any) { + const error = args[0]; + if (typeof error === 'string') { + super( + HttpException.createBody({ + code: 0, + message: error, + }), + ); + this.errorCode = 0; + return; + } + + const [code, message] = error.split(':'); + super( + HttpException.createBody({ + code, + message, + }), ); + + this.errorCode = Number(code); } getErrorCode(): number { diff --git a/apps/api/src/filters/app.filter.ts b/apps/api/src/filters/app.filter.ts index 9fb8795..4fe663a 100644 --- a/apps/api/src/filters/app.filter.ts +++ b/apps/api/src/filters/app.filter.ts @@ -4,15 +4,25 @@ import { ExceptionFilter, HttpException, HttpStatus, + Logger, } from '@nestjs/common'; import { FastifyReply } from 'fastify'; -import { ErrorEnum } from '../constants/error'; -import { ApiException } from '../exceptions/api.exception'; +import { ErrorEnum } from '@/constants/error-code.constant'; +import { ApiException } from '@/exceptions/api.exception'; +import { isDev } from '@/global/env'; @Catch() export class AppFilter implements ExceptionFilter { + private readonly logger = new Logger(AppFilter.name); + + constructor() { + this.registerCatchAllExceptionsHook(); + } + catch(exception: unknown, host: ArgumentsHost) { + this.logger.error(exception); + const ctx = host.switchToHttp(); const response = ctx.getResponse(); @@ -29,11 +39,8 @@ export class AppFilter implements ExceptionFilter { exception instanceof HttpException ? exception.message : `${exception}`; // 系统内部错误时,在生产模式下隐藏具体异常消息 - if ( - process.env.NODE_ENV === 'production' && - httpStatus === HttpStatus.INTERNAL_SERVER_ERROR - ) { - errorMessage = ErrorEnum.CODE_500; + if (!isDev && httpStatus === HttpStatus.INTERNAL_SERVER_ERROR) { + errorMessage = ErrorEnum.SERVER_ERROR?.split(':')[1]; } // 返回基础响应结果 @@ -45,4 +52,14 @@ export class AppFilter implements ExceptionFilter { response.status(httpStatus).send(resBody); } + + registerCatchAllExceptionsHook() { + process.on('unhandledRejection', (reason: any) => { + console.error('unhandledRejection: ', reason); + }); + + process.on('uncaughtException', (err) => { + console.error('uncaughtException: ', err); + }); + } } diff --git a/apps/api/src/global/env.ts b/apps/api/src/global/env.ts new file mode 100644 index 0000000..d5914f1 --- /dev/null +++ b/apps/api/src/global/env.ts @@ -0,0 +1,11 @@ +import cluster from 'cluster'; + +export const isMainCluster = + process.env.NODE_APP_INSTANCE && + parseInt(process.env.NODE_APP_INSTANCE) === 0; +export const isMainProcess = cluster.isPrimary || isMainCluster; + +export const isDev = process.env.NODE_ENV === 'development'; + +export const isTest = !!process.env.TEST; +export const cwd = process.cwd(); diff --git a/apps/api/src/interceptors/transform.interceptor.ts b/apps/api/src/interceptors/transform.interceptor.ts index f675332..67414e6 100644 --- a/apps/api/src/interceptors/transform.interceptor.ts +++ b/apps/api/src/interceptors/transform.interceptor.ts @@ -5,7 +5,6 @@ import { NestInterceptor, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; -import { FastifyReply } from 'fastify'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -14,7 +13,7 @@ import { ResOp } from '@/common/model/response.model'; import { SKIP_TRANSFORM_DECORATOR_KEY } from '../decorators/skip-transform.decorator'; /** - * 统一处理返回接口结果,如果不需要则添加@Keep装饰器 + * 统一处理返回接口结果,如果不需要则添加 @SkipTransform 装饰器 */ export class TransformInterceptor implements NestInterceptor { constructor(private readonly reflector: Reflector) {} @@ -23,16 +22,19 @@ export class TransformInterceptor implements NestInterceptor { context: ExecutionContext, next: CallHandler, ): Observable { + const isSkipTransform = this.reflector.get( + SKIP_TRANSFORM_DECORATOR_KEY, + context.getHandler(), + ); + if (isSkipTransform) return next.handle(); + return next.handle().pipe( map((data) => { - const isSkipTransform = this.reflector.get( - SKIP_TRANSFORM_DECORATOR_KEY, - context.getHandler(), - ); - if (isSkipTransform) return data; + if (typeof data === 'undefined') { + context.switchToHttp().getResponse().status(HttpStatus.NO_CONTENT); + return data; + } - const response = context.switchToHttp().getResponse(); - response.header('Content-Type', 'application/json; charset=utf-8'); return new ResOp(HttpStatus.OK, data ?? null); }), ); diff --git a/apps/api/src/modules/apps/todo/todo.service.ts b/apps/api/src/modules/apps/todo/todo.service.ts index 3077bd6..430a050 100644 --- a/apps/api/src/modules/apps/todo/todo.service.ts +++ b/apps/api/src/modules/apps/todo/todo.service.ts @@ -1,9 +1,7 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { ErrorEnum } from '@/constants/error'; -import { ApiException } from '@/exceptions/api.exception'; import { paginate } from '@/helper/paginate'; import { Pagination } from '@/helper/paginate/pagination'; import { TodoEntity } from '@/modules/apps/todo/todo.entity'; @@ -27,7 +25,7 @@ export class TodoService { async detail(id: number): Promise { const item = await this.todoRepository.findOneBy({ id }); - if (!item) throw new ApiException(ErrorEnum.CODE_2004); + if (!item) throw new NotFoundException('未找到该记录'); return item; } diff --git a/apps/api/src/modules/auth/auth.module.ts b/apps/api/src/modules/auth/auth.module.ts index 69559c2..7cb81d2 100644 --- a/apps/api/src/modules/auth/auth.module.ts +++ b/apps/api/src/modules/auth/auth.module.ts @@ -3,10 +3,10 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; - import { TypeOrmModule } from '@nestjs/typeorm'; import { IJwtConfig } from '@/config'; +import { isDev } from '@/global/env'; import { LogModule } from '../system/log/log.module'; import { MenuModule } from '../system/menu/menu.module'; @@ -46,7 +46,7 @@ const strategies = [LocalStrategy, JwtStrategy]; return { secret, expires, - ignoreExpiration: process.env.NODE_ENV === 'development', + ignoreExpiration: isDev, }; }, inject: [ConfigService], diff --git a/apps/api/src/modules/auth/auth.service.ts b/apps/api/src/modules/auth/auth.service.ts index f44580a..c846f39 100644 --- a/apps/api/src/modules/auth/auth.service.ts +++ b/apps/api/src/modules/auth/auth.service.ts @@ -4,7 +4,7 @@ import { Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import { isEmpty } from 'lodash'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { UserService } from '@/modules/system/user/user.service'; @@ -32,12 +32,12 @@ export class AuthService { const user = await this.userService.findUserByUserName(credential); if (isEmpty(user)) { - throw new ApiException(ErrorEnum.CODE_1003); + throw new ApiException(ErrorEnum.USER_NOT_FOUND); } const comparePassword = MD5(`${password}${user.psalt}`); if (user.password !== comparePassword) { - throw new ApiException(ErrorEnum.CODE_1003); + throw new ApiException(ErrorEnum.PASSWORD_MISMATCH); } if (user) { @@ -60,12 +60,12 @@ export class AuthService { ): Promise { const user = await this.userService.findUserByUserName(username); if (isEmpty(user)) { - throw new ApiException(ErrorEnum.CODE_1003); + throw new ApiException(ErrorEnum.USER_NOT_FOUND); } const comparePassword = MD5(`${password}${user.psalt}`); if (user.password !== comparePassword) { - throw new ApiException(ErrorEnum.CODE_1003); + throw new ApiException(ErrorEnum.PASSWORD_MISMATCH); } const roleIds = await this.roleService.getRoleIdsByUser(user.id); @@ -97,7 +97,7 @@ export class AuthService { const comparePassword = MD5(`${password}${user.psalt}`); if (user.password !== comparePassword) { - throw new ApiException(ErrorEnum.CODE_1003); + throw new ApiException(ErrorEnum.PASSWORD_MISMATCH); } } diff --git a/apps/api/src/modules/auth/dto/captcha.dto.ts b/apps/api/src/modules/auth/dto/captcha.dto.ts index 959e424..2bb3843 100644 --- a/apps/api/src/modules/auth/dto/captcha.dto.ts +++ b/apps/api/src/modules/auth/dto/captcha.dto.ts @@ -8,8 +8,6 @@ import { IsString, } from 'class-validator'; -import { ErrorEnum } from '@/constants/error'; - export class ImageCaptchaDto { @ApiProperty({ required: false, @@ -34,13 +32,13 @@ export class ImageCaptchaDto { export class SendEmailCodeDto { @ApiProperty({ description: '邮箱' }) - @IsEmail({}, { message: ErrorEnum.CODE_1025 }) + @IsEmail({}, { message: '邮箱格式不正确' }) email: string; } export class SendSmsCodeDto { @ApiProperty({ description: '手机号' }) - @IsMobilePhone('zh-CN', {}, { message: ErrorEnum.CODE_1026 }) + @IsMobilePhone('zh-CN', {}, { message: '手机号格式不正确' }) phone: string; } diff --git a/apps/api/src/modules/auth/guards/jwt-auth.guard.ts b/apps/api/src/modules/auth/guards/jwt-auth.guard.ts index e13bc9b..a645403 100644 --- a/apps/api/src/modules/auth/guards/jwt-auth.guard.ts +++ b/apps/api/src/modules/auth/guards/jwt-auth.guard.ts @@ -4,7 +4,7 @@ import { AuthGuard } from '@nestjs/passport'; import { FastifyReply, FastifyRequest } from 'fastify'; import { isEmpty, isNil } from 'lodash'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { AuthService } from '@/modules/auth/auth.service'; import { TokenService } from '@/modules/auth/services/token.service'; @@ -40,21 +40,21 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { if (isPublic) return true; if (isEmpty(requestToken)) { - throw new ApiException(ErrorEnum.CODE_1101); + throw new ApiException(ErrorEnum.INVALID_LOGIN); } // 判断token是否存在,如果不存在则认证失败 const accessToken = isNil(requestToken) ? undefined : await this.tokenService.checkAccessToken(requestToken!); - if (!accessToken) throw new ApiException(ErrorEnum.CODE_1101); + if (!accessToken) throw new ApiException(ErrorEnum.INVALID_LOGIN); // 无法通过token校验 // 尝试通过refreshToken刷新token if (!isNil(requestToken)) { const token = await this.tokenService.refreshToken(accessToken); - if (isNil(token)) throw new ApiException(ErrorEnum.CODE_1101); + if (isNil(token)) throw new ApiException(ErrorEnum.INVALID_LOGIN); if (token.accessToken) { // 将新的token挂载到当前请求上 @@ -67,7 +67,7 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { // 刷新失败(refreshToken过期)则再次抛出认证失败的异常 result = await super.canActivate(context); } catch (error) { - throw new ApiException(ErrorEnum.CODE_1102); + throw new ApiException(ErrorEnum.INVALID_LOGIN); } } } @@ -75,13 +75,13 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) { if (isPublic) return true; if (isEmpty(request.user)) { - throw new ApiException(ErrorEnum.CODE_1101); + throw new ApiException(ErrorEnum.INVALID_LOGIN); } const pv = await this.authService.getPasswordVersionByUid(request.user.uid); if (pv !== `${request.user.pv}`) { // 密码版本不一致,登录期间已更改过密码 - throw new ApiException(ErrorEnum.CODE_1102); + throw new ApiException(ErrorEnum.INVALID_LOGIN); } // 不允许多端登录 diff --git a/apps/api/src/modules/auth/services/captcha.service.ts b/apps/api/src/modules/auth/services/captcha.service.ts index aa8fde8..2017b78 100644 --- a/apps/api/src/modules/auth/services/captcha.service.ts +++ b/apps/api/src/modules/auth/services/captcha.service.ts @@ -4,7 +4,7 @@ import { Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import { isEmpty } from 'lodash'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { CaptchaLogService } from '@/modules/system/log/services/captcha-log.service'; @@ -22,7 +22,7 @@ export class CaptchaService { async checkImgCaptcha(id: string, code: string): Promise { const result = await this.redis.get(`captcha:img:${id}`); if (isEmpty(result) || code.toLowerCase() !== result.toLowerCase()) { - throw new ApiException(ErrorEnum.CODE_1002); + throw new ApiException(ErrorEnum.INVALID_VERIFICATION_CODE); } // 校验成功后移除验证码 await this.redis.del(`captcha:img:${id}`); diff --git a/apps/api/src/modules/rbac/constant.ts b/apps/api/src/modules/rbac/constant.ts index 730e13f..d98b5a6 100644 --- a/apps/api/src/modules/rbac/constant.ts +++ b/apps/api/src/modules/rbac/constant.ts @@ -4,7 +4,9 @@ export const POLICY_KEY = 'policy'; export const ALLOW_ANON_KEY = 'allow_anon_permission'; -export enum Roles { - ADMIN = 'admin', - USER = 'user', -} +export const Roles = { + ADMIN: 'admin', + USER: 'user', +} as const; + +export type Role = (typeof Roles)[keyof typeof Roles]; diff --git a/apps/api/src/modules/rbac/guards/rbac.guard.ts b/apps/api/src/modules/rbac/guards/rbac.guard.ts index b05da39..95104f8 100644 --- a/apps/api/src/modules/rbac/guards/rbac.guard.ts +++ b/apps/api/src/modules/rbac/guards/rbac.guard.ts @@ -4,7 +4,7 @@ import { FastifyRequest } from 'fastify'; import { DataSource } from 'typeorm'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { AuthService } from '@/modules/auth/auth.service'; @@ -72,7 +72,7 @@ export class RbacGuard implements CanActivate { } if (!canNext) { - throw new ApiException(ErrorEnum.CODE_1103); + throw new ApiException(ErrorEnum.NO_PERMISSION); } return true; diff --git a/apps/api/src/modules/rbac/guards/resource.guard.ts b/apps/api/src/modules/rbac/guards/resource.guard.ts index 7f46d80..fc2125a 100644 --- a/apps/api/src/modules/rbac/guards/resource.guard.ts +++ b/apps/api/src/modules/rbac/guards/resource.guard.ts @@ -6,18 +6,20 @@ import { isNil } from 'lodash'; import { DataSource, Repository } from 'typeorm'; -import { ErrorEnum } from '@/constants/error'; -import { Roles } from '@/constants/role'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { IS_PUBLIC_KEY } from '../../auth/constants'; -import { POLICY_KEY } from '../constant'; +import { POLICY_KEY, Roles } from '../constant'; import { ResourceObject } from '../decorators/resource.decorator'; @Injectable() export class ResourceGuard implements CanActivate { - constructor(private reflector: Reflector, private dataSource: DataSource) {} + constructor( + private reflector: Reflector, + private dataSource: DataSource, + ) {} async canActivate(context: ExecutionContext): Promise { const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ @@ -57,16 +59,16 @@ export class ResourceGuard implements CanActivate { const id = getRequestItemId(request); if (!id) { - throw new ApiException(ErrorEnum.CODE_1108); + throw new ApiException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND); } const item = await repo.findOne({ where: { id }, relations: ['user'] }); if (!item) { - throw new ApiException(ErrorEnum.CODE_1108); + throw new ApiException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND); } if (!item?.user) { - throw new ApiException(ErrorEnum.CODE_1017); + throw new ApiException(ErrorEnum.USER_NOT_FOUND); } if (condition) { @@ -75,7 +77,7 @@ export class ResourceGuard implements CanActivate { // 如果没有设置policy,则默认只能操作自己的数据 if (item.user?.id !== user.uid) { - throw new ApiException(ErrorEnum.CODE_1108); + throw new ApiException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND); } } diff --git a/apps/api/src/modules/shared/mailer/mailer.service.ts b/apps/api/src/modules/shared/mailer/mailer.service.ts index c6d9c92..57ed755 100644 --- a/apps/api/src/modules/shared/mailer/mailer.service.ts +++ b/apps/api/src/modules/shared/mailer/mailer.service.ts @@ -1,14 +1,14 @@ import { InjectRedis } from '@liaoliaots/nestjs-redis'; -import { MailerService as NestMailerService } from '@nestjs-modules/mailer'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { MailerService as NestMailerService } from '@nestjs-modules/mailer'; import dayjs from 'dayjs'; import Redis from 'ioredis'; import { IAppConfig } from '@/config'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { randomValue } from '@/utils'; @@ -58,7 +58,7 @@ export class MailerService { ); } catch (error) { console.log(error); - throw new ApiException(ErrorEnum.CODE_1203); + throw new ApiException(ErrorEnum.VERIFICATION_CODE_SEND_FAILED); } return { @@ -70,7 +70,7 @@ export class MailerService { async checkCode(to, code) { const ret = await this.redis.get(`captcha:${to}`); if (ret !== code) { - throw new ApiException(ErrorEnum.CODE_1002); + throw new ApiException(ErrorEnum.INVALID_VERIFICATION_CODE); } await this.redis.del(`captcha:${to}`); } @@ -80,11 +80,11 @@ export class MailerService { // ip限制 const ipLimit = await this.redis.get(`ip:${ip}:send:limit`); - if (ipLimit) throw new ApiException(ErrorEnum.CODE_1201); + if (ipLimit) throw new ApiException(ErrorEnum.TOO_MANY_REQUESTS); // 1分钟最多接收1条 const limit = await this.redis.get(`captcha:${to}:limit`); - if (limit) throw new ApiException(ErrorEnum.CODE_1201); + if (limit) throw new ApiException(ErrorEnum.TOO_MANY_REQUESTS); // 1天一个邮箱最多接收5条 let limitCountOfDay: string | number = await this.redis.get( @@ -92,7 +92,7 @@ export class MailerService { ); limitCountOfDay = limitCountOfDay ? Number(limitCountOfDay) : 0; if (limitCountOfDay > LIMIT_TIME) - throw new ApiException(ErrorEnum.CODE_1202); + throw new ApiException(ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY); // 1天一个ip最多发送5条 let ipLimitCountOfDay: string | number = await this.redis.get( @@ -100,7 +100,7 @@ export class MailerService { ); ipLimitCountOfDay = ipLimitCountOfDay ? Number(ipLimitCountOfDay) : 0; if (ipLimitCountOfDay > LIMIT_TIME) - throw new ApiException(ErrorEnum.CODE_1202); + throw new ApiException(ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY); } async log(to: string, code: string, ip: string) { diff --git a/apps/api/src/modules/socket/admin-ws.guard.ts b/apps/api/src/modules/socket/admin-ws.guard.ts index 19d7e19..db8d76b 100644 --- a/apps/api/src/modules/socket/admin-ws.guard.ts +++ b/apps/api/src/modules/socket/admin-ws.guard.ts @@ -2,7 +2,7 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Observable } from 'rxjs'; import { Socket } from 'socket.io'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { SocketException } from '@/exceptions/socket.exception'; import { AuthService } from './auth.service'; @@ -24,7 +24,7 @@ export class AdminWsGuard implements CanActivate { // close client.disconnect(); // 无法通过token校验 - throw new SocketException(ErrorEnum.CODE_1101); + throw new SocketException(ErrorEnum.INVALID_LOGIN); } } } diff --git a/apps/api/src/modules/socket/auth.service.ts b/apps/api/src/modules/socket/auth.service.ts index e223d40..ed464ef 100644 --- a/apps/api/src/modules/socket/auth.service.ts +++ b/apps/api/src/modules/socket/auth.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { isEmpty } from 'lodash'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { SocketException } from '@/exceptions/socket.exception'; @@ -12,14 +12,14 @@ export class AuthService { checkAdminAuthToken(token: string | string[] | undefined): IAuthUser | never { if (isEmpty(token)) { - throw new SocketException(ErrorEnum.CODE_1101); + throw new SocketException(ErrorEnum.INVALID_LOGIN); } try { // 挂载对象到当前请求上 return this.jwtService.verify(Array.isArray(token) ? token[0] : token); } catch (e) { // 无法通过token校验 - throw new SocketException(ErrorEnum.CODE_1101); + throw new SocketException(ErrorEnum.INVALID_LOGIN); } } } diff --git a/apps/api/src/modules/system/dept/dept.controller.ts b/apps/api/src/modules/system/dept/dept.controller.ts index d2695a9..7ca2fe8 100644 --- a/apps/api/src/modules/system/dept/dept.controller.ts +++ b/apps/api/src/modules/system/dept/dept.controller.ts @@ -9,7 +9,7 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiResult } from '@/decorators/api-result.decorator'; import { IdParam } from '@/decorators/id-param.decorator'; import { ApiSecurityAuth } from '@/decorators/swagger.decorator'; @@ -69,11 +69,13 @@ export class DeptController { async delete(@IdParam() id: number): Promise { // 查询是否有关联用户或者部门,如果含有则无法删除 const count = await this.deptService.countUserByDeptId(id); - if (count > 0) throw new ApiException(ErrorEnum.CODE_1009); + if (count > 0) + throw new ApiException(ErrorEnum.DEPARTMENT_HAS_ASSOCIATED_USERS); const count2 = await this.deptService.countChildDept(id); - if (count2 > 0) throw new ApiException(ErrorEnum.CODE_1015); + if (count2 > 0) + throw new ApiException(ErrorEnum.DEPARTMENT_HAS_CHILD_DEPARTMENTS); await this.deptService.delete(id); } diff --git a/apps/api/src/modules/system/dept/dept.service.ts b/apps/api/src/modules/system/dept/dept.service.ts index 319ba31..5c47354 100644 --- a/apps/api/src/modules/system/dept/dept.service.ts +++ b/apps/api/src/modules/system/dept/dept.service.ts @@ -3,7 +3,7 @@ import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm'; import { isEmpty } from 'lodash'; import { EntityManager, Repository, TreeRepository } from 'typeorm'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { DeptEntity } from '@/modules/system/dept/dept.entity'; import { UserEntity } from '@/modules/system/user/entities/user.entity'; @@ -37,7 +37,7 @@ export class DeptService { .getOne(); if (isEmpty(dept)) { - throw new ApiException(ErrorEnum.CODE_1019); + throw new ApiException(ErrorEnum.DEPARTMENT_NOT_FOUND); } return dept; } diff --git a/apps/api/src/modules/system/dict/dict.service.ts b/apps/api/src/modules/system/dict/dict.service.ts index 24095fe..15e4d5c 100644 --- a/apps/api/src/modules/system/dict/dict.service.ts +++ b/apps/api/src/modules/system/dict/dict.service.ts @@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { paginate } from '@/helper/paginate'; import { Pagination } from '@/helper/paginate/pagination'; @@ -75,7 +75,7 @@ export class DictService { async isExistKey(key: string): Promise { const result = await this.dictRepository.findOneBy({ key }); if (result) { - throw new ApiException(ErrorEnum.CODE_1021); + throw new ApiException(ErrorEnum.PARAMETER_CONFIG_KEY_EXISTS); } } diff --git a/apps/api/src/modules/system/menu/menu.controller.ts b/apps/api/src/modules/system/menu/menu.controller.ts index 6d02798..ca1f789 100644 --- a/apps/api/src/modules/system/menu/menu.controller.ts +++ b/apps/api/src/modules/system/menu/menu.controller.ts @@ -12,7 +12,7 @@ import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { flattenDeep } from 'lodash'; import { IAppConfig } from '@/config'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiResult } from '@/decorators/api-result.decorator'; import { IdParam } from '@/decorators/id-param.decorator'; import { ApiSecurityAuth } from '@/decorators/swagger.decorator'; @@ -76,7 +76,7 @@ export class MenuController { @Body() dto: Partial, ): Promise { if (id <= this.configService.get('app').protectSysPermMenuMaxId) - throw new ApiException(ErrorEnum.CODE_1016); + throw new ApiException(ErrorEnum.SYSTEM_BUILTIN_FUNCTION_NOT_ALLOWED); // check await this.menuService.check(dto); @@ -100,7 +100,7 @@ export class MenuController { id <= this.configService.get('app').protectSysPermMenuMaxId ) { // 系统内置功能不提供删除 - throw new ApiException(ErrorEnum.CODE_1016); + throw new ApiException(ErrorEnum.SYSTEM_BUILTIN_FUNCTION_NOT_ALLOWED); } // 如果有子目录,一并删除 const childMenus = await this.menuService.findChildMenus(id); diff --git a/apps/api/src/modules/system/menu/menu.service.ts b/apps/api/src/modules/system/menu/menu.service.ts index f8fd4c1..fe0a943 100644 --- a/apps/api/src/modules/system/menu/menu.service.ts +++ b/apps/api/src/modules/system/menu/menu.service.ts @@ -6,7 +6,7 @@ import { concat, isEmpty, uniq } from 'lodash'; import { In, IsNull, Like, Not, Repository } from 'typeorm'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { MenuEntity } from '@/modules/system/menu/menu.entity'; @@ -92,16 +92,16 @@ export class MenuService { async check(dto: Partial): Promise { if (dto.type === 2 && !dto.parent) { // 无法直接创建权限,必须有parent - throw new ApiException(ErrorEnum.CODE_1005); + throw new ApiException(ErrorEnum.PERMISSION_REQUIRES_PARENT); } if (dto.type === 1 && dto.parent) { const parent = await this.getMenuItemInfo(dto.parent); if (isEmpty(parent)) { - throw new ApiException(ErrorEnum.CODE_1014); + throw new ApiException(ErrorEnum.PARENT_MENU_NOT_FOUND); } if (parent && parent.type === 1) { // 当前新增为菜单但父节点也为菜单时为非法操作 - throw new ApiException(ErrorEnum.CODE_1006); + throw new ApiException(ErrorEnum.ILLEGAL_OPERATION_DIRECTORY_PARENT); } } } diff --git a/apps/api/src/modules/system/online/online.controller.ts b/apps/api/src/modules/system/online/online.controller.ts index 7f85d45..d9b46d7 100644 --- a/apps/api/src/modules/system/online/online.controller.ts +++ b/apps/api/src/modules/system/online/online.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Get, Post } from '@nestjs/common'; import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiResult } from '@/decorators/api-result.decorator'; import { ApiSecurityAuth } from '@/decorators/swagger.decorator'; import { ApiException } from '@/exceptions/api.exception'; @@ -34,7 +34,7 @@ export class OnlineController { @Permission('system:online:kick') async kick(@Body() dto: KickDto, @AuthUser() user: IAuthUser): Promise { if (dto.id === user.uid) { - throw new ApiException(ErrorEnum.CODE_1012); + throw new ApiException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER); } await this.onlineService.kickUser(dto.id, user.uid); } diff --git a/apps/api/src/modules/system/online/online.service.ts b/apps/api/src/modules/system/online/online.service.ts index 4f36866..da8a736 100644 --- a/apps/api/src/modules/system/online/online.service.ts +++ b/apps/api/src/modules/system/online/online.service.ts @@ -6,7 +6,7 @@ import { EntityManager } from 'typeorm'; import { UAParser } from 'ua-parser-js'; -import { ErrorEnum } from '@/constants/error'; +import { ErrorEnum } from '@/constants/error-code.constant'; import { ApiException } from '@/exceptions/api.exception'; import { AdminWSGateway } from '@/modules/socket/admin-ws.gateway'; import { AdminWSService } from '@/modules/socket/admin-ws.service'; @@ -48,7 +48,7 @@ export class OnlineService { const rootUserId = await this.userService.findRootUserId(); const currentUserInfo = await this.userService.getAccountInfo(currentUid); if (uid === rootUserId) { - throw new ApiException(ErrorEnum.CODE_1013); + throw new ApiException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER); } // reset redis keys await this.userService.forbidden(uid); diff --git a/apps/api/src/modules/system/task/constant.ts b/apps/api/src/modules/system/task/constant.ts index bc5bab8..82f2312 100644 --- a/apps/api/src/modules/system/task/constant.ts +++ b/apps/api/src/modules/system/task/constant.ts @@ -19,3 +19,6 @@ export enum TaskType { Cron = 0, Interval = 1, } + +export const SYS_TASK_QUEUE_NAME = 'system:sys-task'; +export const SYS_TASK_QUEUE_PREFIX = 'system:sys:task'; diff --git a/apps/api/src/modules/system/task/task.controller.ts b/apps/api/src/modules/system/task/task.controller.ts index 880a2cb..bbc8d7e 100644 --- a/apps/api/src/modules/system/task/task.controller.ts +++ b/apps/api/src/modules/system/task/task.controller.ts @@ -8,13 +8,10 @@ import { Query, } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; -import { isEmpty } from 'lodash'; -import { ErrorEnum } from '@/constants/error'; import { ApiResult } from '@/decorators/api-result.decorator'; import { IdParam } from '@/decorators/id-param.decorator'; import { ApiSecurityAuth } from '@/decorators/swagger.decorator'; -import { ApiException } from '@/exceptions/api.exception'; import { Pagination } from '@/helper/paginate/pagination'; import { Permission } from '@/modules/rbac/decorators'; import { TaskEntity } from '@/modules/system/task/task.entity'; @@ -71,11 +68,7 @@ export class TaskController { @Permission(PermissionTask.DELETE) async delete(@IdParam() id: number): Promise { const task = await this.taskService.info(id); - if (!isEmpty(task)) { - await this.taskService.delete(task); - } else { - throw new ApiException(ErrorEnum.CODE_1304); - } + await this.taskService.delete(task); } @Put(':id/once') @@ -83,11 +76,7 @@ export class TaskController { @Permission(PermissionTask.ONCE) async once(@IdParam() id: number): Promise { const task = await this.taskService.info(id); - if (!isEmpty(task)) { - await this.taskService.once(task); - } else { - throw new ApiException(ErrorEnum.CODE_1304); - } + await this.taskService.once(task); } @Put(':id/stop') @@ -95,11 +84,7 @@ export class TaskController { @Permission(PermissionTask.STOP) async stop(@IdParam() id: number): Promise { const task = await this.taskService.info(id); - if (!isEmpty(task)) { - await this.taskService.stop(task); - } else { - throw new ApiException(ErrorEnum.CODE_1304); - } + await this.taskService.stop(task); } @Put(':id/start') @@ -107,10 +92,7 @@ export class TaskController { @Permission(PermissionTask.START) async start(@IdParam() id: number): Promise { const task = await this.taskService.info(id); - if (!isEmpty(task)) { - await this.taskService.start(task); - } else { - throw new ApiException(ErrorEnum.CODE_1304); - } + + await this.taskService.start(task); } } diff --git a/apps/api/src/modules/system/task/task.module.ts b/apps/api/src/modules/system/task/task.module.ts index 0e791a6..a8e84e5 100644 --- a/apps/api/src/modules/system/task/task.module.ts +++ b/apps/api/src/modules/system/task/task.module.ts @@ -5,10 +5,11 @@ import { ConfigService } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { IRedisConfig } from '@/config'; -import { SYS_TASK_QUEUE_NAME, SYS_TASK_QUEUE_PREFIX } from '@/constants/task'; import { LogModule } from '../log/log.module'; +import { SYS_TASK_QUEUE_NAME, SYS_TASK_QUEUE_PREFIX } from './constant'; + import { TaskController } from './task.controller'; import { TaskEntity } from './task.entity'; import { TaskConsumer } from './task.processor'; diff --git a/apps/api/src/modules/system/task/task.processor.ts b/apps/api/src/modules/system/task/task.processor.ts index 856c1ef..d406df9 100644 --- a/apps/api/src/modules/system/task/task.processor.ts +++ b/apps/api/src/modules/system/task/task.processor.ts @@ -1,10 +1,10 @@ import { OnQueueCompleted, Process, Processor } from '@nestjs/bull'; import { Job } from 'bull'; -import { SYS_TASK_QUEUE_NAME } from '@/constants/task'; - import { TaskLogService } from '../log/services/task-log.service'; +import { SYS_TASK_QUEUE_NAME } from './constant'; + import { TaskService } from './task.service'; export interface ExecuteData { diff --git a/apps/api/src/modules/system/task/task.service.ts b/apps/api/src/modules/system/task/task.service.ts index 9af1bd8..8a19b0c 100644 --- a/apps/api/src/modules/system/task/task.service.ts +++ b/apps/api/src/modules/system/task/task.service.ts @@ -1,6 +1,11 @@ import { InjectRedis } from '@liaoliaots/nestjs-redis'; import { InjectQueue } from '@nestjs/bull'; -import { BadRequestException, Injectable, OnModuleInit } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + NotFoundException, + OnModuleInit, +} from '@nestjs/common'; import { ModuleRef, Reflector } from '@nestjs/core'; import { UnknownElementException } from '@nestjs/core/errors/exceptions/unknown-element.exception'; import { InjectRepository } from '@nestjs/typeorm'; @@ -9,8 +14,8 @@ import Redis from 'ioredis'; import { isEmpty } from 'lodash'; import { Like, Repository } from 'typeorm'; -import { ErrorEnum } from '@/constants/error'; -import { SYS_TASK_QUEUE_NAME, SYS_TASK_QUEUE_PREFIX } from '@/constants/task'; +import { ErrorEnum } from '@/constants/error-code.constant'; + import { ApiException } from '@/exceptions/api.exception'; import { paginate } from '@/helper/paginate'; import { Pagination } from '@/helper/paginate/pagination'; @@ -19,7 +24,12 @@ import { AppLoggerService } from '@/modules/shared/services/app-logger.service'; import { TaskEntity } from '@/modules/system/task/task.entity'; import { MISSION_DECORATOR_KEY } from '@/modules/tasks/mission.decorator'; -import { TaskStatus } from './constant'; +import { + SYS_TASK_QUEUE_NAME, + SYS_TASK_QUEUE_PREFIX, + TaskStatus, +} from './constant'; + import { TaskDto, TaskQueryDto } from './task.dto'; @Injectable() @@ -106,10 +116,15 @@ export class TaskService implements OnModuleInit { * task info */ async info(id: number): Promise { - return this.taskRepository + const task = this.taskRepository .createQueryBuilder('task') .where({ id }) .getOne(); + + if (!task) { + throw new NotFoundException('Task Not Found'); + } + return task; } /** @@ -289,7 +304,7 @@ export class TaskService implements OnModuleInit { } // 所执行的任务不存在 if (!service || !(exec in service)) { - throw new ApiException(ErrorEnum.CODE_1302); + throw new NotFoundException('任务不存在'); } // 检测是否有Mission注解 const hasMission = this.reflector.get( @@ -298,12 +313,12 @@ export class TaskService implements OnModuleInit { ); // 如果没有,则抛出错误 if (!hasMission) { - throw new ApiException(ErrorEnum.CODE_1301); + throw new ApiException(ErrorEnum.INSECURE_MISSION); } } catch (e) { if (e instanceof UnknownElementException) { // 任务不存在 - throw new ApiException(ErrorEnum.CODE_1302); + throw new NotFoundException('任务不存在'); } else { // 其余错误则不处理,继续抛出 throw e; diff --git a/apps/api/src/constants/task.ts b/apps/api/src/modules/system/task/task.ts similarity index 100% rename from apps/api/src/constants/task.ts rename to apps/api/src/modules/system/task/task.ts diff --git a/apps/api/src/modules/system/user/user.service.ts b/apps/api/src/modules/system/user/user.service.ts index d3a4662..46168d5 100644 --- a/apps/api/src/modules/system/user/user.service.ts +++ b/apps/api/src/modules/system/user/user.service.ts @@ -8,8 +8,8 @@ import { isEmpty, isNil } from 'lodash'; import { EntityManager, Like, Repository } from 'typeorm'; import { IAppConfig } from '@/config'; -import { ErrorEnum } from '@/constants/error'; -import { SYS_USER_INITPASSWORD } from '@/constants/param-config'; +import { ErrorEnum } from '@/constants/error-code.constant'; +import { SYS_USER_INITPASSWORD } from '@/constants/system.constant'; import { ApiException } from '@/exceptions/api.exception'; import { paginate } from '@/helper/paginate'; @@ -64,9 +64,8 @@ export class UserService { */ async getAccountInfo(uid: number): Promise { const user: UserEntity = await this.userRepository.findOneBy({ id: uid }); - if (isEmpty(user)) { - throw new ApiException(ErrorEnum.CODE_1017); - } + if (isEmpty(user)) throw new ApiException(ErrorEnum.USER_NOT_FOUND); + return user; } @@ -75,9 +74,7 @@ export class UserService { */ async updateAccountInfo(uid: number, info: AccountUpdateDto): Promise { const user = await this.userRepository.findOneBy({ id: uid }); - if (isEmpty(user)) { - throw new ApiException(ErrorEnum.CODE_1017); - } + if (isEmpty(user)) throw new ApiException(ErrorEnum.USER_NOT_FOUND); const data = { ...(info.nickname ? { nickname: info.nickname } : null), @@ -104,12 +101,12 @@ export class UserService { async updatePassword(uid: number, dto: PasswordUpdateDto): Promise { const user = await this.userRepository.findOneBy({ id: uid }); if (isEmpty(user)) { - throw new ApiException(ErrorEnum.CODE_1017); + throw new ApiException(ErrorEnum.USER_NOT_FOUND); } const comparePassword = MD5(`${dto.oldPassword}${user.psalt}`); // 原密码不一致,不允许更改 if (user.password !== comparePassword) { - throw new ApiException(ErrorEnum.CODE_1011); + throw new ApiException(ErrorEnum.PASSWORD_MISMATCH); } const password = MD5(`${dto.newPassword}${user.psalt}`); await this.userRepository.update({ id: uid }, { password }); @@ -141,7 +138,7 @@ export class UserService { username, }); if (!isEmpty(exists)) { - throw new ApiException(ErrorEnum.CODE_1001); + throw new ApiException(ErrorEnum.SYSTEM_USER_EXISTS); } await this.entityManager.transaction(async (manager) => { @@ -332,7 +329,7 @@ export class UserService { async exist(username: string) { const user = await this.userRepository.findOneBy({ username }); if (isNil(user)) { - throw new ApiException(ErrorEnum.CODE_1001); + throw new ApiException(ErrorEnum.SYSTEM_USER_EXISTS); } return true; } @@ -344,7 +341,7 @@ export class UserService { const exists = await this.userRepository.findOneBy({ username, }); - if (!isEmpty(exists)) throw new ApiException(ErrorEnum.CODE_1001); + if (!isEmpty(exists)) throw new ApiException(ErrorEnum.SYSTEM_USER_EXISTS); await this.entityManager.transaction(async (manager) => { const salt = randomValue(32); diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..2444cdc --- /dev/null +++ b/test.ts @@ -0,0 +1,27 @@ +// @ts-nocheck +function Curd(options: any) { + return (target: any) => { + const types = Reflect.getMetadata( + 'design:paramtypes', + target.prototype, + 'say', + ) + console.log(types) // 输出 say 方法的参数类型信息 + return target + } +} +@Curd({ + a: 'test', +}) +class Test { + public a: number + public b: string + constructor(a: number, b: string) { + this.a = a + this.b = b + } + say(name: string, age: string) { + console.log('say,hello', name, age) + return 1 + } +}