Ver Fonte

服务重新规划异常及i18n,见代理服务Readme

lrf há 11 meses atrás
pai
commit
467eede466

+ 7 - 3
README.md

@@ -4,8 +4,8 @@
   * [X]	2.单点登录迁移至代理
   * [x]	3.接口鉴权
   * [x] 4.前端路由处理: 未加载到路由,但是跳转了,所以又重刷新页面了
-  * [ ] 5.重新整理异常,用代理服务那边的方式设置
-  * [ ] 6.需要将条件转换为sql,sql转换器!
+  * [ ] 5.重新整理异常:
+  * [ ] 6.需要将条件转换为sql,sql转换器!,先试试通过pg的方式连接,不行再说,有nodejs驱动,不行就全都自己写
   * [x] x.对数据进行解密
   * [x] x2.服务接口只接收本地请求
 
@@ -14,5 +14,9 @@
   * [ ] 2.配置文件外置共用
   * [ ] 3.数据分级(需要与业务绑定)
 
-  
+# 异常使用
+## 1.只设ServiceError在每个项目中,并将自己项目使用的Code做成enum存放在各自项目中使用.
+## 2.将异常的i18n放在代理服务中统一管理.所有的翻译都是通过代理来翻译.写在代理项目的 locales>${language}>error.ts中
+## 3.发生异常到返回给请求的流程:
+  各自项目中发生异常,throw new ServiceError(${ErrorCode})>代理服务接到编码,并将编码再次throw new ServiceError(${请求返回的ErrorCode})从而格式化并翻译异常,返回前端
 

+ 12 - 2
src/config/config.default.ts

@@ -1,5 +1,5 @@
 import { MidwayConfig } from '@midwayjs/core';
-
+import path = require('path');
 export default {
   // use for cookie sign key, should change to your own and keep security
   keys: '1715577385578_8567',
@@ -8,6 +8,16 @@ export default {
   },
   //是否启用数据加密,需要与前端配合一致,否则会一端加密,一端不解密
   useCrypto: true,
+  midwayLogger: {
+    default: {
+      level: 'warn',
+      transports: {
+        file: {
+          dir: path.resolve(__dirname, '../../logs'),
+        },
+      },
+    },
+  },
   jwt: {
     secret: 'Ziyouyanfa!@#',
     expiresIn: 3600, // 3600
@@ -25,7 +35,7 @@ export default {
       },
       zh_cn: {
         default: require('../locales/zh_cn/defaults'),
-        LoginError: require('../locales/zh_cn/error/LoginError'),
+        error: require('../locales/zh_cn/error'),
       },
     },
   },

+ 1 - 1
src/config/config.local.ts

@@ -1,5 +1,5 @@
 import { MidwayConfig } from '@midwayjs/core';
-const redisHost = '120.48.146.1';
+const redisHost = '127.0.0.1';
 const redisPwd = '123456';
 const redisDB = 6;
 export default {

+ 0 - 8
src/error/Readme.md

@@ -1,8 +0,0 @@
-# 自定义异常
-
-## 变量与文件名
-  - config.default.ts i18n.localTable 下所有语种的 key 和 引入的文件名
-  - error目录下面的 自定义异常文件中的 code 
-  - locales目录下 各个语种下的error目录的文件名 与 文件中的 codePrefix
-  
-#### errorcode 一般都是 "=" 左右两侧是一个值 值也可以无视大小写,都会经过全大写处理

+ 0 - 20
src/error/login.error.ts

@@ -1,20 +0,0 @@
-import { MidwayError, registerErrorCode } from '@midwayjs/core';
-// 需保持一致的标识,具体看error的readme
-const code = 'LoginError';
-// 写这个enum只是为了能有提示,不随便瞎写,都规范起来
-export enum LoginErroCode {
-  TEST = 'TEST',
-  NOT_LOGIN = 'NOT_LOGIN',
-  ACCOUNT_HAS_EXPIRED = 'ACCOUNT_HAS_EXPIRED',
-  ACCOUNT_LOGGED_IN_ELESWHERE = 'ACCOUNT_LOGGED_IN_ELESWHERE',
-}
-export const FrameworkErrorEnum = registerErrorCode(code, LoginErroCode);
-
-export class LoginError extends MidwayError {
-  detail: any;
-  group: string;
-  constructor(errcode: string) {
-    super(errcode);
-    this.group = code;
-  }
-}

+ 18 - 2
src/error/service.error.ts

@@ -1,9 +1,25 @@
 import { MidwayError } from '@midwayjs/core';
 
 export class ServiceError extends MidwayError {
-  group: string;
   constructor(errcode: string) {
     super(errcode);
-    this.group = 'service';
   }
 }
+export enum ErrorCode {
+  TEST = 'TEST',
+  // 权限,登录,角色
+  NOT_LOGIN = 'NOT_LOGIN',
+  ACCOUNT_HAS_EXPIRED = 'ACCOUNT_HAS_EXPIRED',
+  ACCOUNT_LOGGED_IN_ELESWHERE = 'ACCOUNT_LOGGED_IN_ELESWHERE',
+  USER_NOT_FOUND = 'USER_NOT_FOUND',
+  BAD_PASSWORD = 'BAD_PASSWORD',
+  USER_IS_DISABLED = 'USER_IS_DISABLED',
+  ROLE_IS_DISABLED = 'ROLE_IS_DISABLED',
+
+  // es
+  ES_ERROR = 'ES_ERROR',
+  ES_INDEX_NOT_FOUND = 'ES_INDEX_NOT_FOUND',
+  ES_DATA_NOT_FOUND = 'ES_DATA_NOT_FOUND',
+
+  SERVICE_CANT_USE = 'SERVICE_CANT_USE',
+}

+ 5 - 13
src/filter/customError.filter.ts

@@ -5,35 +5,27 @@ import {
   Inject,
 } from '@midwayjs/core';
 import { Context } from '@midwayjs/koa';
-import { LoginError } from '../error/login.error';
 import { ServiceError } from '../error/service.error';
 import { MidwayI18nService } from '@midwayjs/i18n';
-import { toLower } from 'lodash';
-interface CustomError extends Error {
-  group: string;
-}
 
-@Catch([LoginError, ServiceError])
+// 指定那些异常来这里处理
+@Catch([ServiceError])
 export class CustomErrorFilter {
   @Inject()
   ctx: Context;
   @ApplicationContext()
   applicationContext: IMidwayContainer;
-  async catch(err: CustomError, ctx: Context) {
-    // 所有的未分类错误会到这里
+  async catch(err: Error, ctx: Context) {
     const i18n = await this.applicationContext.getAsync(MidwayI18nService);
     const errmsg = i18n.translate(err.message, {
-      locale: this.ctx.locale,
+      locale: ctx.locale,
       // 需要全小写
-      group: toLower(err.group),
+      group: 'error',
     });
     const result: any = {
       errcode: err.message,
       errmsg,
     };
-    if (process.env.NODE_ENV !== 'production') {
-      result.details = err.stack;
-    }
     return result;
   }
 }

+ 19 - 0
src/locales/zh_cn/error.ts

@@ -0,0 +1,19 @@
+export default {
+  TEST: 'i18n异常测试',
+  // 权限,登录,角色
+  NOT_LOGIN: '未检测到登录信息,请登录!',
+  ACCOUNT_HAS_EXPIRED: '账号登录已失效,请重新登录!',
+  ACCOUNT_LOGGED_IN_ELESWHERE: '本账号已在其他地方登录,请重新登录!',
+  USER_NOT_FOUND: '未找到用户!',
+  BAD_PASSWORD: '密码错误',
+  USER_IS_DISABLED: '该用户已被禁用',
+  ROLE_IS_DISABLED: '当前角色下的用户无法使用',
+
+  // es
+  ES_ERROR: 'ElasticSearch发生错误!',
+  ES_INDEX_NOT_FOUND: 'ElasticSearch发生错误,未找到索引!',
+  ES_DATA_NOT_FOUND: 'ElasticSearch发生错误,未找到数据!',
+
+  // 其他服务
+  SERVICE_CANT_USE: '当前服务无法使用',
+};

+ 0 - 17
src/locales/zh_cn/error/LoginError.ts

@@ -1,17 +0,0 @@
-import { toUpper } from 'lodash';
-// 需要与 对应的 自定义errorcode 前缀对应 都经过 toUpper处理
-const codePrefix = 'LoginError';
-const obj = {
-  TEST: '测试异常',
-  NOT_LOGIN: '未检测到登录信息,请登录!',
-  ACCOUNT_HAS_EXPIRED: '账号登录已失效,请重新登录!',
-  ACCOUNT_LOGGED_IN_ELESWHERE: '本账号已在其他地方登录,请重新登录!',
-  // '该用户已被禁用',
-  // '当前角色下的用户无法使用'
-};
-const listObject = {};
-Object.keys(obj).map(i => {
-  const val = obj[i];
-  listObject[`${toUpper(codePrefix)}_${toUpper(i)}`] = toUpper(val);
-});
-export default listObject;

+ 28 - 20
src/service/proxy.service.ts

@@ -1,17 +1,10 @@
 import { App, Config, Inject, InjectClient, Provide } from '@midwayjs/core';
 import { Application, Context } from '@midwayjs/koa';
 import { RequestBase } from '../interface/proxy.interface';
-import {
-  get,
-  isEqual,
-  isObject,
-  isString,
-  lowerCase,
-  omit,
-  pick,
-} from 'lodash';
+import { get, lowerCase, omit, pick } from 'lodash';
 import { HttpServiceFactory, HttpService } from '@midwayjs/axios';
 import { PemService } from './pem.service';
+import { ServiceError } from '../error/service.error';
 @Provide()
 export class ProxyService {
   @Inject()
@@ -103,18 +96,33 @@ export class ProxyService {
       }
     }
     reqConfig = JSON.parse(JSON.stringify(reqConfig));
-    const res = await this.serviceAxios.request(reqConfig);
-    if (res.status !== 200) throw new Error('proxy service request error');
-    const result = res.data;
-    if (this.useCrypto) {
-      // 需要将返回的数据加密成字符串
-      if (get(result, 'data')) {
-        const enReturn = this.pemService.encrypt(
-          omit(res.data, ['errcode', 'errmsg'])
-        );
-        result.data = enReturn;
+    let result;
+    try {
+      const res = await this.serviceAxios.request(reqConfig);
+      if (res.status !== 200) throw new Error('proxy service request error');
+      result = res.data;
+    } catch (error) {
+      // 请求不是自定义的错误,把错误隐藏,写到日志中
+      this.ctx.logger.error(error.stack);
+      return {
+        errmsg: 'proxy service request error',
+        code: get(error, 'response.status'),
+      };
+    }
+    if (result.errcode === 0) {
+      if (this.useCrypto) {
+        // 需要将返回的数据加密成字符串
+        if (get(result, 'data')) {
+          const enReturn = this.pemService.encrypt(
+            omit(result, ['errcode', 'errmsg'])
+          );
+          result.data = enReturn;
+        }
       }
+      return result;
+    } else {
+      // 需要将异常信息翻译并返回
+      throw new ServiceError(result.errcode);
     }
-    return result;
   }
 }

+ 5 - 6
src/service/singleSignOn.service.ts

@@ -2,7 +2,7 @@ import { Config, Inject, Provide } from '@midwayjs/core';
 import { JwtService } from '@midwayjs/jwt';
 import { Context } from '@midwayjs/koa';
 import { get } from 'lodash';
-import { FrameworkErrorEnum, LoginError } from '../error/login.error';
+import { ServiceError, ErrorCode } from '../error/service.error';
 import * as Crypto from 'crypto-js';
 import { RedisService } from '@midwayjs/redis';
 
@@ -30,9 +30,9 @@ export class SingleSignOnService {
       const data = this.jwtService.decodeSync(token);
       if (data) user = data;
     }
-    if (!user) throw new LoginError(FrameworkErrorEnum.NOT_LOGIN);
+    if (!user) throw new ServiceError(ErrorCode.NOT_LOGIN);
     const { _id, role, login_code } = user;
-    if (!login_code) throw new LoginError(FrameworkErrorEnum.NOT_LOGIN);
+    if (!login_code) throw new ServiceError(ErrorCode.NOT_LOGIN);
     // 解密
     const decodeResult = Crypto.AES.decrypt(login_code, this.jwtSecret);
     const decode = Crypto.enc.Utf8.stringify(decodeResult).toString();
@@ -43,14 +43,13 @@ export class SingleSignOnService {
     const rediskey = `${this.loginSign}:${role}:${_id}`;
     // 取出当前记录在案的 code
     const redisCode = await this.redisService.get(rediskey);
-    if (!redisCode)
-      throw new LoginError(FrameworkErrorEnum.ACCOUNT_HAS_EXPIRED);
+    if (!redisCode) throw new ServiceError(ErrorCode.ACCOUNT_HAS_EXPIRED);
     // 判断是否一致
     if (code === redisCode) {
       // 一致,延时
       await this.redisService.expire(rediskey, this.jwtExpiresIn);
     } else {
-      throw new LoginError(FrameworkErrorEnum.ACCOUNT_LOGGED_IN_ELESWHERE);
+      throw new ServiceError(ErrorCode.ACCOUNT_LOGGED_IN_ELESWHERE);
     }
   }
   /**