Jelajahi Sumber

Merge remote-tracking branch 'origin/master'

zhuang 1 bulan lalu
induk
melakukan
129df06c1b
1 mengubah file dengan 272 tambahan dan 0 penghapusan
  1. 272 0
      src/app/core/net/default.interceptor.ts

+ 272 - 0
src/app/core/net/default.interceptor.ts

@@ -0,0 +1,272 @@
+import {
+  HttpErrorResponse,
+  HttpEvent,
+  HttpHandler,
+  HttpHeaders,
+  HttpInterceptor,
+  HttpRequest,
+  HttpResponse,
+  HttpResponseBase
+} from '@angular/common/http';
+import { Injectable, Injector } from '@angular/core';
+import { Router } from '@angular/router';
+import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
+import { CacheService } from '@delon/cache';
+import { _HttpClient, MenuService } from '@delon/theme';
+import { deepCopy } from '@delon/util';
+import { environment } from '@env/environment';
+import { NzNotificationService } from 'ng-zorro-antd/notification';
+import { Observable, of, throwError } from 'rxjs';
+import { catchError, mergeMap } from 'rxjs/operators';
+
+import * as incontrolUtils from '../../shared/utils/ic-utils';
+import { GeneralUtilsService } from '../utils/general-utils.service';
+
+const CODEMESSAGE = {
+  200: '服务器成功返回请求的数据。',
+  201: '新建或修改数据成功。',
+  202: '一个请求已经进入后台排队(异步任务)。',
+  204: '删除数据成功。',
+  400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
+  401: '用户没有权限(令牌、用户名、密码错误)。',
+  403: '用户得到授权,但是访问是被禁止的。',
+  404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
+  406: '请求的格式不可得。',
+  410: '请求的资源被永久删除,且不会再得到的。',
+  422: '当创建一个对象时,发生一个验证错误。',
+  500: '服务器发生错误,请检查服务器。',
+  502: '网关错误。',
+  503: '服务不可用,服务器暂时过载或维护。',
+  504: '网关超时。',
+  651: '证书无效,需要申请'
+};
+
+/**
+ * 默认HTTP拦截器,其注册细节见 `app.module.ts`
+ */
+@Injectable()
+export class DefaultInterceptor implements HttpInterceptor {
+  notify401: any;
+  icUtils = incontrolUtils;
+  constructor(private injector: Injector, public srv: CacheService, private menuService: MenuService) {}
+
+  private get notification(): NzNotificationService {
+    return this.injector.get(NzNotificationService);
+  }
+
+  private goTo(url: string) {
+    setTimeout(() => this.injector.get(Router).navigateByUrl(url));
+  }
+
+  private utils(): GeneralUtilsService {
+    return this.injector.get(GeneralUtilsService);
+  }
+
+  private checkStatus(ev: HttpResponseBase) {
+    if ((ev.status >= 200 && ev.status < 300) || ev.status === 401) {
+      return;
+    }
+
+    // const errortext = CODEMESSAGE[ev.status] || ev.statusText;
+    // this.notification.error(`请求错误 ${ev.status}: ${ev.url}`, errortext);
+  }
+
+  private goToLogin() {
+    const currentTime = new Date().getTime();
+    if (!this.notify401 || (this.notify401 && currentTime - this.notify401 > 1000)) {
+      this.notification.error(`未登录或登录已过期,请重新登录。`, ``);
+    }
+    this.notify401 = new Date().getTime();
+    // 清空 token 信息
+    (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).clear();
+    // }
+    let initUrl = sessionStorage.getItem('ic_url_before_401');
+    if (!initUrl && !window.location.hash.includes('login')) {
+      initUrl = incontrolUtils.urlHash();
+      sessionStorage.setItem('ic_url_before_401', initUrl);
+    }
+    this.goTo('/passport/login');
+  }
+
+  private handleData(newReq: any, ev: HttpResponseBase): Observable<any> {
+    // 可能会因为 `throw` 导出无法执行 `_HttpClient` 的 `end()` 操作
+    this.checkStatus(ev);
+    // 业务处理:一些通用操作
+    switch (ev.status) {
+      case 200:
+        // 业务层级错误处理,以下是假定restful有一套统一输出格式(指不管成功与否都有相应的数据格式)情况下进行处理
+        // 例如响应内容:
+        //  错误内容:{ status: 1, msg: '非法参数' }
+        //  正确内容:{ status: 0, response: {  } }
+        // 则以下代码片断可直接适用
+        // if (event instanceof HttpResponse) {
+        //     const body: any = event.body;
+        //     if (body && body.status !== 0) {
+        //         this.msg.error(body.msg);
+        //         /** 继续抛出错误中断后续所有 Pipe、subscribe 操作,因此: */
+        //         /** this.http.get('/').subscribe() 并不会触发 */
+        //         return throwError({});
+        //     } else {
+        //         /** 重新修改 `body` 内容为 `response` 内容,对于绝大多数场景已经无须再关心业务状态码 */
+        //         return of(new HttpResponse(Object.assign(event, { body: body.response })));
+        //         /** 或者依然保持完整的格式 */
+        //         return of(event);
+        //     }
+        // }
+        if (ev instanceof HttpResponse) {
+          const evBody = ev.body;
+          if (evBody.code && Number(evBody.code)) {
+            if (Number(evBody.code) === 692) {
+              this.notification.warning('导入失败', ev.body.msg, { nzDuration: 0 });
+            } else if ((Number(evBody.code) >= 600 && Number(evBody.code) < 700) || Number(evBody.code) === 500) {
+              let notInit = !(ev.url || '').includes('source=init');
+              if (notInit) {
+                this.notification.error(evBody.msg, '', { nzDuration: 3000 });
+              }
+              console.error('error', ev, newReq);
+            } else if (Number(evBody.code) >= 700 && Number(evBody.code) < 800) {
+              this.notification.success(evBody.msg, '', { nzDuration: 1500 });
+            }
+          }
+          if (evBody.refreshToken) {
+            // console.log('refreshToken', evBody.refreshToken);
+            (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).set({
+              token: evBody.refreshToken
+            });
+
+            const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token;
+
+            // console.log('token', token);
+          }
+        }
+        break;
+      case 401:
+        this.goToLogin();
+        break;
+      case 403:
+        this.goToLogin();
+        break;
+      case 404:
+      case 500:
+        // this.goTo(`/exception/${ev.status}`);
+        console.error('error', ev, newReq);
+        this.notification.error(`服务器异常,请联系管理员`, ``);
+        break;
+      default:
+        if (ev instanceof HttpErrorResponse) {
+          console.warn('未可知错误,大部分是由于后端不支持CORS或无效配置引起', ev);
+          return throwError(ev);
+        }
+        break;
+    }
+    return of(ev);
+  }
+
+  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
+    // 统一加上服务端前缀
+    let url = req.url;
+    let newReq = req.clone({ url });
+    let igc = this.srv.get('incontrolGlobalConfig', { mode: 'none', type: 'm' });
+    if (!igc) {
+      const userCollection = this.srv.get('ic_userCollection', { mode: 'none', type: 'm' });
+      if (userCollection) {
+        const globalConfig = incontrolUtils.getConfigByCode(userCollection?.configList || [], 'global_config');
+        if (globalConfig) {
+          igc = globalConfig.configOption.config;
+        } else {
+          // console.error('未从缓存中获取到userCollection.globalConfig');
+        }
+      } else {
+        // console.error('未从缓存中获取到userCollection');
+      }
+    }
+    const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token;
+    if (token && token.length > 1) {
+      const userCollection = this.srv.get('ic_userCollection', { mode: 'none', type: 'm' });
+      const globalConfig = incontrolUtils.getConfigByCode(userCollection?.configList || [], 'global_custom_code');
+      const interceptorCode = globalConfig?.configOption?.config?.interceptorCode;
+      if (globalConfig?.configOption?.config?.enabledInterceptorCode && incontrolUtils.icIsNotNull(interceptorCode)) {
+        const customInit = new Function('parameter', interceptorCode);
+        const config = { req: newReq };
+        const parameter = {
+          eventName: 'interceptorCode',
+          config,
+          comp: this
+        };
+        const retData: any = customInit(parameter);
+        if (retData?.req) {
+          newReq = retData.req;
+        }
+      }
+    }
+    if (incontrolUtils.icIsNotNull(igc?.sqlEncodeCofig?.encodeType)) {
+      if (igc.sqlEncodeCofig.encodeDebugger) {
+        console.log('before encode', newReq);
+      }
+      let urlHash = window.location.hash.replace('#', '');
+      if ((urlHash || '').replace(/\//g, '').length < 3) {
+        const firstMenuUrl = window.sessionStorage.getItem('ic_userFirstMenu');
+        if (firstMenuUrl && incontrolUtils.icIsNotNull(firstMenuUrl)) {
+          urlHash = firstMenuUrl;
+        }
+      }
+      newReq = incontrolUtils.encodeApiSql(newReq, {
+        urlHash,
+        type: igc.sqlEncodeCofig.encodeType!,
+        publicKey: igc?.sqlEncodeCofig?.encodePubkey || ''
+      });
+      if (token && token.length > 1) {
+        let incontrolToken = incontrolUtils.RSALong(igc?.sqlEncodeCofig?.encodePubkey || '', `${token}-_-_-${urlHash}`);
+
+        /**
+         * 特殊处理 sys/page/part/getByIds
+         * 2023-07-05 09:09:09
+         * 将body字符串化
+         * 拼入incontrol-token中加密
+         * 把body加密
+         * 后台拆出incontrol-token的最后一段与解密后的body进行对比
+         */
+        if (url.includes('sys/page/part/getByIds')) {
+          const copyBody = JSON.stringify(deepCopy(newReq.body));
+          incontrolToken = incontrolUtils.RSALong(igc?.sqlEncodeCofig?.encodePubkey || '', `${token}-_-_-${urlHash}-_-_-${copyBody}`);
+          const copyBodyRSA = incontrolUtils.RSALong(igc?.sqlEncodeCofig?.encodePubkey || '', copyBody);
+          newReq = newReq.clone({ body: copyBodyRSA });
+        }
+
+        const headers: HttpHeaders = newReq.headers.append('incontrol-token', incontrolToken);
+        newReq = newReq.clone({ headers });
+      }
+      if (igc.sqlEncodeCofig.encodeDebugger) {
+        console.log('after encode', newReq);
+      }
+    } else {
+      if (token && token.length > 1) {
+        const headers: HttpHeaders = newReq.headers.append('incontrol-token', token);
+        newReq = newReq.clone({ headers });
+      }
+    }
+    if (igc?.confuseApiName) {
+      let tempUrl = incontrolUtils.isConfuseApi(newReq.url);
+      if (tempUrl) {
+        newReq = newReq.clone({ url: tempUrl! });
+        const tempBody = deepCopy(newReq.body);
+        if (incontrolUtils.icIsNotNull(tempBody['querySql'])) {
+          tempBody['queryStr'] = tempBody['querySql'];
+          delete tempBody.querySql;
+          newReq = newReq.clone({ body: tempBody! });
+        }
+      }
+    }
+    return next.handle(newReq).pipe(
+      mergeMap((event: any) => {
+        // 允许统一对请求错误处理
+        if (event instanceof HttpResponseBase) {
+          return this.handleData(newReq, event);
+        }
+        // 若一切都正常,则后续操作
+        return of(event);
+      }),
+      catchError((err: HttpErrorResponse) => this.handleData(newReq, err))
+    );
+  }
+}