刘光辉
10 小时以前 0dfe84494048ce27ba8449831782128412d3eb13
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
 * 该文件可自行根据业务逻辑进行调整
 */
import type { AxiosResponse, CreateAxiosOptions, CreateInternalAxiosRequestConfig, RequestInterceptorConfig, RequestOptions } from '@vben/request';
 
import { AesEncryption, isObject } from '@jnpf/utils';
 
import { useAppConfig } from '@vben/hooks';
import { $t } from '@vben/locales';
import { preferences } from '@vben/preferences';
import { errorMessageResponseInterceptor, RequestClient, ResultEnum } from '@vben/request';
import { useAccessStore } from '@vben/stores';
 
import { message } from 'ant-design-vue';
 
import { router } from '#/router';
import { useAuthStore } from '#/store';
import { getJnpfAppEnCode, getRealJnpfAppEnCode } from '#/utils/jnpf';
 
const aesEncryption = new AesEncryption();
const { apiURL, reportApiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
 
// 请求之前处理config
function beforeRequestHook(): RequestInterceptorConfig {
  return {
    fulfilled(config) {
      // 数据加密
      if (config?.requestOptions?.useCipher && config.data) {
        const resultStr = aesEncryption.encryptByAES(JSON.stringify(config.data));
        if (resultStr) config.data = { encryptData: resultStr };
      }
      return config;
    },
  };
}
// 数据处理,方便区分多种处理方式
function transformResponseHook() {
  return {
    fulfilled(res: AxiosResponse<any>) {
      const options = (res.config as CreateInternalAxiosRequestConfig)?.requestOptions;
      // 数据解密
      if ((options as RequestOptions).useCipher) {
        const resultStr = aesEncryption.decryptByAES(res.data);
        if (resultStr) res.data = JSON.parse(resultStr);
      }
      const { isTransformResponse, isReturnNativeResponse } = options as RequestOptions;
      // 是否返回原生响应头 比如:需要获取响应头时使用该属性
      if (isReturnNativeResponse) {
        return res;
      }
      // 不进行任何处理,直接返回
      // 用于页面代码可能需要直接获取code,data,msg这些信息时开启
      if (!isTransformResponse) {
        return res.data;
      }
      if (!res.data) {
        throw new Error($t('fallback.http.internalServerError'));
      }
      //  这里 code,data,msg为 后台统一的字段,需要在 types.ts内修改为项目自己的接口返回格式
      const { code, msg } = res.data;
 
      // 这里逻辑可以根据项目进行修改
      const hasSuccess = res.data && isObject(res.data) && Reflect.has(res.data, 'code') && code === ResultEnum.SUCCESS;
      if (hasSuccess) {
        return res.data;
      }
      // 在此处根据自己项目的实际情况对不同的code执行不同的操作
      // 如果不希望中断当前请求,请return数据,否则直接抛出异常即可
      let errorMsg = '';
      switch (code) {
        case ResultEnum.FORBIDDEN: {
          errorMsg = msg || $t('fallback.http.forbidden');
          if (getRealJnpfAppEnCode() && res.config.url?.includes('oauth/CurrentUser')) {
            const authStore = useAuthStore();
            authStore.goForbidden();
          }
          break;
        }
        case ResultEnum.TOKEN_ERROR:
        case ResultEnum.TOKEN_LOGGED:
        case ResultEnum.TOKEN_TIMEOUT: {
          errorMsg = msg || $t('fallback.http.internalServerError');
          const accessStore = useAccessStore();
          const authStore = useAuthStore();
          accessStore.setAccessToken(null);
          authStore.logout();
          break;
        }
        default: {
          errorMsg = msg || $t('fallback.http.internalServerError');
        }
      }
      if ((options as RequestOptions).errorMessageMode === 'message') {
        message.error(errorMsg);
      }
      throw new Error(errorMsg);
    },
  };
}
 
function createRequestClient(opt?: Partial<CreateAxiosOptions>) {
  const client = new RequestClient(opt || {});
 
  // 请求头处理
  client.addRequestInterceptor({
    fulfilled: async (config) => {
      const accessStore = useAccessStore();
 
      if (accessStore.accessToken) config.headers.Authorization = accessStore.accessToken;
      const routeAppCode = getJnpfAppEnCode();
      const realAppCode = getRealJnpfAppEnCode();
      let appScope = 'CONSOLE';
      if (routeAppCode.toLowerCase().startsWith('backend_')) {
        appScope = 'BACKEND';
      } else if (routeAppCode) {
        appScope = 'FRONTEND';
      }
      config.headers['App-Code'] = realAppCode;
      config.headers['Jnpf-App-Scope'] = appScope;
      config.headers['Accept-Language'] = preferences.app.locale;
      config.headers['Jnpf-Origin'] = 'pc';
      config.headers['Vue-Version'] = '3';
      // 审计「操作入口」:用户是从哪个菜单点进来的。route.meta.modelId 即 base_module.f_id
      // (见 utils/generate-routes-backend.ts 的 modelId: node.id)。
      // 放在全局拦截器而不是各业务接口各传一次:表单保存、列表按钮、流程动作三类场景一次覆盖。
      // 用 router 实例而不是 useRouter()——后者是组合式 API,只能在 setup 上下文里调。
      const menuId = router.currentRoute.value?.meta?.modelId;
      if (menuId) config.headers['Jnpf-Menu-Id'] = String(menuId);
      return config;
    },
  });
 
  client.addRequestInterceptor(beforeRequestHook());
 
  // response数据解构
  client.addResponseInterceptor({
    fulfilled: (response) => {
      const { status } = response;
 
      if (status >= 200 && status < 400) {
        return response;
      }
      throw Object.assign({}, response, { response });
    },
  });
 
  // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
  client.addResponseInterceptor(
    errorMessageResponseInterceptor((msg: string, error) => {
      // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
      // 当前mock接口返回的错误字段是 error 或者 message
      const responseData = error?.response?.data ?? {};
      const errorMessage = responseData?.error ?? responseData?.msg ?? '';
      // 如果没有错误信息,则会根据状态码进行提示
      message.error(errorMessage || msg);
    }),
  );
 
  client.addResponseInterceptor(transformResponseHook());
 
  return client;
}
 
export const defHttp = createRequestClient({ requestOptions: { apiUrl: apiURL } });
 
// 报表接口
export const reportHttp = createRequestClient({ requestOptions: { apiUrl: reportApiURL } });