index.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import axios, { AxiosRequestConfig } from 'axios'
  2. import { v4 } from 'uuid'
  3. //import qs from 'qs'
  4. import cryptojs from 'crypto-js'
  5. //import { addPending, removePending } from './pending'
  6. import { encryptBody, decryptBody } from '@/services/websocket/package/crypto'
  7. import { IMessageHead } from '@/types/proto/proto'
  8. import { useGlobalStore, useLoginStore, useAccountStore, useErrorInfoStore } from '@/stores'
  9. import { CommonResult, ResultCode, SendMsgToMQ } from './types'
  10. import { FunCode } from '@/constants/funcode'
  11. import service from '@/services'
  12. import tradeService from '@/services/websocket/trade'
  13. import eventBus from '@/services/bus'
  14. export default new (class {
  15. private readonly axiosInstance = axios.create({
  16. timeout: 30000,
  17. })
  18. constructor() {
  19. // 请求拦截器
  20. this.axiosInstance.interceptors.request.use(
  21. (config) => {
  22. const globalStore = useGlobalStore()
  23. const loginStore = useLoginStore()
  24. //addPending(config) //将当前请求添加到列表中
  25. //设置请求头
  26. if (globalStore.getSystemInfo('tradeChannel') === 'ws') {
  27. const key = 'dZChvstdjmqIt5fP'
  28. const timestamp = globalStore.getTimestamp()
  29. const verification = cryptojs.HmacSHA256(loginStore.token + timestamp, key)
  30. config.headers = {
  31. Authorization: loginStore.token,
  32. Timestamp: timestamp,
  33. Verification: verification.toString()
  34. }
  35. } else {
  36. config.headers = {
  37. Authorization: loginStore.token,
  38. LoginID: loginStore.loginId,
  39. Group: loginStore.getClientType(),
  40. 'x-token': loginStore.token
  41. }
  42. }
  43. return config
  44. },
  45. (err) => {
  46. console.error(err)
  47. return Promise.reject('发生错误,请稍后再试')
  48. }
  49. )
  50. // 响应拦截器
  51. this.axiosInstance.interceptors.response.use(
  52. (res) => {
  53. //removePending(res) //在请求结束后,移除本次请求
  54. return res
  55. },
  56. (err) => {
  57. if (err.message === 'Network Error') {
  58. return Promise.reject('网络或服务器错误')
  59. }
  60. if (err.response) {
  61. const { code, msg, message } = err.response.data ?? {}
  62. switch (err.response.status) {
  63. case 408: {
  64. return Promise.reject('请求超时,请稍后再试')
  65. }
  66. default: {
  67. const loginStore = useLoginStore()
  68. if (loginStore.token && code === ResultCode.InvalidToken) {
  69. eventBus.$emit('LogoutNotify', '登录失效,请重新登录')
  70. }
  71. return Promise.reject(msg || message)
  72. }
  73. }
  74. }
  75. return Promise.reject('发生错误,请稍后再试')
  76. }
  77. )
  78. }
  79. /** 请求响应集合 */
  80. private resultMap = new Set<() => void>()
  81. /**
  82. * 建议在过渡动画中启用,动画结束时禁用
  83. * 可防止在动画过程中同时请求数据,数据请求先于动画完成,双绑数据的页面会实时渲染,导致动画卡顿的问题
  84. */
  85. private _pending = false
  86. /** 全局 pending 状态 */
  87. get pending() {
  88. return this._pending
  89. }
  90. /** 在动画开始时设置一个 pending 状态,在动画结束后取消 pending 状态 */
  91. set pending(status: boolean) {
  92. this._pending = status
  93. if (!status) {
  94. // 当 pending 状态为 false 时返回所有请求的回调
  95. this.resultMap.forEach((fn) => {
  96. fn()
  97. this.resultMap.delete(fn)
  98. })
  99. }
  100. }
  101. /**
  102. * Http 请求
  103. * @param config
  104. * @param errMsg
  105. * @returns
  106. */
  107. request<T>(config: AxiosRequestConfig, errMsg?: string) {
  108. return new Promise<T>((resolve, reject) => {
  109. this.axiosInstance(config).then((res) => {
  110. if (this.pending) {
  111. this.resultMap.add(() => resolve(res.data))
  112. } else {
  113. resolve(res.data)
  114. }
  115. }).catch((err) => {
  116. const msg = err ?? (errMsg ? '请求失败: ' + errMsg : '请求失败,请稍后重试')
  117. reject(msg)
  118. })
  119. })
  120. }
  121. /**
  122. * Http 通用请求
  123. * @param config
  124. * @param errMsg
  125. * @returns
  126. */
  127. async commonRequest<T>(config: AxiosRequestConfig, errMsg?: string) {
  128. const baseUrl = service.getConfig('goCommonSearchUrl')
  129. config.url = baseUrl + config.url
  130. const res = await this.request<CommonResult<T>>(config, errMsg)
  131. switch (res.code) {
  132. case ResultCode.InvalidToken: {
  133. return Promise.reject('令牌无效')
  134. }
  135. case ResultCode.Success: {
  136. return res
  137. }
  138. default: {
  139. return Promise.reject(res.msg)
  140. }
  141. }
  142. }
  143. /**
  144. * Http 通用请求
  145. * @param config
  146. * @param errMsg
  147. * @returns
  148. */
  149. async goRequest<T>(config: AxiosRequestConfig, errMsg?: string) {
  150. const baseUrl = service.getConfig('goAccess')
  151. config.url = baseUrl + config.url
  152. const res = await this.request<CommonResult<T>>(config, errMsg)
  153. switch (res.code) {
  154. case ResultCode.InvalidToken: {
  155. return Promise.reject('令牌无效')
  156. }
  157. case 0: {
  158. return res
  159. }
  160. default: {
  161. return Promise.reject(res.msg)
  162. }
  163. }
  164. }
  165. /**
  166. * Http 通用请求
  167. * @param config
  168. * @param errMsg
  169. * @returns
  170. */
  171. async mqRequest<T extends {
  172. RetCode: number;
  173. RetDesc: string;
  174. Status?: number;
  175. }>({ data, requestCode, responseCode, marketId = 0 }: SendMsgToMQ<{ Header?: IMessageHead }>, errMsg?: string) {
  176. const globalStore = useGlobalStore()
  177. const loginStore = useLoginStore()
  178. const accountStore = useAccountStore()
  179. const requestId = FunCode[requestCode]
  180. const responseId = FunCode[responseCode]
  181. if (data) {
  182. data.Header = {
  183. AccountID: accountStore.currentAccountId,
  184. FunCode: requestId,
  185. UUID: v4(),
  186. UserID: loginStore.userId,
  187. MarketID: marketId,
  188. ...data.Header,
  189. }
  190. }
  191. console.log(requestCode, data)
  192. try {
  193. if (globalStore.getSystemInfo('tradeChannel') === 'ws') {
  194. // websocket 通道
  195. const res = await tradeService.send<T>({
  196. data,
  197. requestCode,
  198. responseCode
  199. })
  200. return await this.pkg50Response<T>(res, responseCode)
  201. } else {
  202. // http 通道
  203. const config: AxiosRequestConfig = {
  204. method: 'post',
  205. url: '/MQ/SendMsgToMQ',
  206. data: {
  207. data: encryptBody(JSON.stringify(data ?? '{}')),
  208. funCodeReq: requestId,
  209. funCodeRsp: responseId,
  210. isEncrypted: true,
  211. }
  212. }
  213. const res = await this.goRequest<{ data: string }>(config, errMsg)
  214. const decryptedData = decryptBody(res.data.data) // 解析 package50 数据
  215. return await this.pkg50Response<T>(JSON.parse(decryptedData), responseCode)
  216. }
  217. } catch (err) {
  218. return await Promise.reject(err)
  219. }
  220. }
  221. /**
  222. * 处理 package50 响应
  223. * @param responseCode
  224. * @param data
  225. * @returns
  226. */
  227. private pkg50Response<T extends {
  228. RetCode: number;
  229. RetDesc: string;
  230. Status?: number;
  231. }>(response: T, responseCode: keyof typeof FunCode) {
  232. console.log(responseCode, response)
  233. const res = { ...response } // websocket 消息对象未定义的属性可能会有默认值
  234. switch (res.RetCode) {
  235. case 0: {
  236. return Promise.resolve(res)
  237. }
  238. case -1: {
  239. return Promise.reject(res.RetDesc)
  240. }
  241. case 12018: {
  242. if (res.RetDesc) {
  243. const word = cryptojs.enc.Base64.parse(res.RetDesc) // 解析base64
  244. res.RetDesc = cryptojs.enc.Utf8.stringify(word)
  245. }
  246. return Promise.reject(res.RetDesc)
  247. }
  248. default: {
  249. // 银行 业务 以 Status 作为判断依据
  250. if (res.Status === 0 || res.Status == 6007) {
  251. return Promise.resolve(res)
  252. }
  253. const { getErrorInfoByCode } = useErrorInfoStore()
  254. const msg = getErrorInfoByCode(res.RetCode ?? res.Status)
  255. const error = String(res.RetDesc || res.RetCode || res.Status)
  256. return Promise.reject(msg ?? error)
  257. }
  258. }
  259. }
  260. })