import axios, { AxiosRequestConfig } from 'axios' import { v4 } from 'uuid' //import qs from 'qs' import cryptojs from 'crypto-js' //import { addPending, removePending } from './pending' import { encryptBody, decryptBody } from '@/services/websocket/package/crypto' import { IMessageHead } from '@/types/proto/proto' import { useGlobalStore, useLoginStore, useAccountStore, useErrorInfoStore } from '@/stores' import { CommonResult, ResultCode, SendMsgToMQ } from './types' import { FunCode } from '@/constants/funcode' import service from '@/services' import tradeService from '@/services/websocket/trade' import eventBus from '@/services/bus' export default new (class { private readonly axiosInstance = axios.create({ timeout: 30000, }) constructor() { // 请求拦截器 this.axiosInstance.interceptors.request.use( (config) => { const globalStore = useGlobalStore() const loginStore = useLoginStore() //addPending(config) //将当前请求添加到列表中 //设置请求头 if (globalStore.getSystemInfo('tradeChannel') === 'ws') { const key = 'dZChvstdjmqIt5fP' const timestamp = globalStore.getTimestamp() const verification = cryptojs.HmacSHA256(loginStore.token + timestamp, key) config.headers = { Authorization: loginStore.token, Timestamp: timestamp, Verification: verification.toString() } } else { config.headers = { Authorization: loginStore.token, LoginID: loginStore.loginId, Group: loginStore.getClientType(), 'x-token': loginStore.token } } return config }, (err) => { console.error(err) return Promise.reject('发生错误,请稍后再试') } ) // 响应拦截器 this.axiosInstance.interceptors.response.use( (res) => { //removePending(res) //在请求结束后,移除本次请求 return res }, (err) => { if (err.message === 'Network Error') { return Promise.reject('网络或服务器错误') } if (err.response) { const { code, msg, message } = err.response.data ?? {} switch (err.response.status) { case 408: { return Promise.reject('请求超时,请稍后再试') } default: { const loginStore = useLoginStore() if (loginStore.token && code === ResultCode.InvalidToken) { eventBus.$emit('LogoutNotify', '登录失效,请重新登录') } return Promise.reject(msg || message) } } } return Promise.reject('发生错误,请稍后再试') } ) } /** 请求响应集合 */ private resultMap = new Set<() => void>() /** * 建议在过渡动画中启用,动画结束时禁用 * 可防止在动画过程中同时请求数据,数据请求先于动画完成,双绑数据的页面会实时渲染,导致动画卡顿的问题 */ private _pending = false /** 全局 pending 状态 */ get pending() { return this._pending } /** 在动画开始时设置一个 pending 状态,在动画结束后取消 pending 状态 */ set pending(status: boolean) { this._pending = status if (!status) { // 当 pending 状态为 false 时返回所有请求的回调 this.resultMap.forEach((fn) => { fn() this.resultMap.delete(fn) }) } } /** * Http 请求 * @param config * @param errMsg * @returns */ request(config: AxiosRequestConfig, errMsg?: string) { return new Promise((resolve, reject) => { this.axiosInstance(config).then((res) => { if (this.pending) { this.resultMap.add(() => resolve(res.data)) } else { resolve(res.data) } }).catch((err) => { const msg = err ?? (errMsg ? '请求失败: ' + errMsg : '请求失败,请稍后重试') reject(msg) }) }) } /** * Http 通用请求 * @param config * @param errMsg * @returns */ async commonRequest(config: AxiosRequestConfig, errMsg?: string) { const baseUrl = service.getConfig('goCommonSearchUrl') config.url = baseUrl + config.url const res = await this.request>(config, errMsg) switch (res.code) { case ResultCode.InvalidToken: { return Promise.reject('令牌无效') } case ResultCode.Success: { return res } default: { return Promise.reject(res.msg) } } } /** * Http 通用请求 * @param config * @param errMsg * @returns */ async goRequest(config: AxiosRequestConfig, errMsg?: string) { const baseUrl = service.getConfig('goAccess') config.url = baseUrl + config.url const res = await this.request>(config, errMsg) switch (res.code) { case ResultCode.InvalidToken: { return Promise.reject('令牌无效') } case 0: { return res } default: { return Promise.reject(res.msg) } } } /** * Http 通用请求 * @param config * @param errMsg * @returns */ async mqRequest({ data, requestCode, responseCode, marketId = 0 }: SendMsgToMQ<{ Header?: IMessageHead }>, errMsg?: string) { const globalStore = useGlobalStore() const loginStore = useLoginStore() const accountStore = useAccountStore() const requestId = FunCode[requestCode] const responseId = FunCode[responseCode] if (data) { data.Header = { AccountID: accountStore.currentAccountId, FunCode: requestId, UUID: v4(), UserID: loginStore.userId, MarketID: marketId, ...data.Header, } } console.log(requestCode, data) try { if (globalStore.getSystemInfo('tradeChannel') === 'ws') { // websocket 通道 const res = await tradeService.send({ data, requestCode, responseCode }) return await this.pkg50Response(res, responseCode) } else { // http 通道 const config: AxiosRequestConfig = { method: 'post', url: '/MQ/SendMsgToMQ', data: { data: encryptBody(JSON.stringify(data ?? '{}')), funCodeReq: requestId, funCodeRsp: responseId, isEncrypted: true, } } const res = await this.goRequest<{ data: string }>(config, errMsg) const decryptedData = decryptBody(res.data.data) // 解析 package50 数据 return await this.pkg50Response(JSON.parse(decryptedData), responseCode) } } catch (err) { return await Promise.reject(err) } } /** * 处理 package50 响应 * @param responseCode * @param data * @returns */ private pkg50Response(response: T, responseCode: keyof typeof FunCode) { console.log(responseCode, response) const res = { ...response } // websocket 消息对象未定义的属性可能会有默认值 switch (res.RetCode) { case 0: { return Promise.resolve(res) } case -1: { return Promise.reject(res.RetDesc) } case 12018: { if (res.RetDesc) { const word = cryptojs.enc.Base64.parse(res.RetDesc) // 解析base64 res.RetDesc = cryptojs.enc.Utf8.stringify(word) } return Promise.reject(res.RetDesc) } default: { // 银行 业务 以 Status 作为判断依据 if (res.Status === 0 || res.Status == 6007) { return Promise.resolve(res) } const { getErrorInfoByCode } = useErrorInfoStore() const msg = getErrorInfoByCode(res.RetCode ?? res.Status) const error = String(res.RetDesc || res.RetCode || res.Status) return Promise.reject(msg ?? error) } } } })