import config from "@/config"; import { getToken } from "@/utils/auth"; import errorCode from "@/utils/errorCode"; import constant from "@/utils/constant"; import storage from "@/utils/storage"; import { useUserStore } from "@/store/modules/user"; import { toast, showConfirm, tansParams } from "@/utils/common"; import { decryptTransportErrorResponse, decryptTransportResponse, encryptTransportRequest, invalidateTransportKeyMeta, resetTransportRequestConfig, shouldRetryTransportWithFreshKey, } from "@/utils/transportCrypto"; let timeout = 10000; const baseUrl = config.baseUrl; let testBatchRecoveryStarted = false; function getResponseBody(payload) { return payload?.data && typeof payload.data === "object" ? payload.data : payload || {}; } function getResponseData(payload) { const body = getResponseBody(payload); return body.data && typeof body.data === "object" ? body.data : body; } function isTestBatchUnavailable(payload) { return getResponseData(payload)?.reason === "TEST_BATCH_UNAVAILABLE"; } function recoverUnavailableTestBatch(payload) { if (testBatchRecoveryStarted) return; testBatchRecoveryStarted = true; const body = getResponseBody(payload); const message = body.msg || "当前测试批次不可用,已切回正式数据模式"; storage.remove(constant.testBatchId); storage.remove(constant.testBatchName); if (typeof uni.$emit === "function") { uni.$emit("test-batch-unavailable", message); } uni.showToast({ title: message, icon: "none", duration: 3500 }); uni.reLaunch({ url: "/pages/work/index" }); setTimeout(() => { testBatchRecoveryStarted = false; }, 1000); } const request = async (config) => { // 是否需要设置 token const isToken = (config.headers || {}).isToken === false; config.header = config.header || {}; config.headers = config.headers || {}; if (getToken() && !isToken) { config.header["Authorization"] = "Bearer " + getToken(); } const testBatchId = storage.get(constant.testBatchId); if (getToken() && testBatchId) { config.header["X-Test-Batch-Id"] = String(testBatchId); } try { config = await encryptTransportRequest(config); // get请求映射params参数 if (config.params) { let url = config.url + "?" + tansParams(config.params); url = url.slice(0, -1); config.url = url; } return await new Promise((resolve, reject) => { uni.request({ method: config.method || "get", timeout: config.timeout || timeout, url: config.baseUrl || baseUrl + config.url, data: config.data, header: config.header, dataType: "json", success: async (response) => { try { const res = await decryptTransportResponse(response, config); if (shouldRetryTransportWithFreshKey(res) && !config.__transportRetried) { invalidateTransportKeyMeta(); config.__transportRetried = true; config.headers.repeatSubmit = false; resetTransportRequestConfig(config); resolve(await request(config)); return; } if (isTestBatchUnavailable(res)) { recoverUnavailableTestBatch(res); const error = new Error(getResponseBody(res).msg || "当前测试批次不可用"); error.response = res; reject(error); return; } const body = getResponseBody(res); const status = res.statusCode ?? res.status; const code = body.code || 200; const msg = errorCode[code] || body.msg || errorCode["default"]; if (code === 401) { showConfirm( "登录状态已过期,您可以继续留在该页面,或者重新登录?", ).then((res) => { if (res.confirm) { useUserStore() .logOut() .then(() => { uni.reLaunch({ url: "/pages/login" }); }); } }); const error = new Error("无效的会话,或者会话已过期,请重新登录。"); error.response = res; reject(error); } else if (code === 500) { const error = new Error(msg); error.response = res; reject(error); } else if (code !== 200) { const error = new Error(msg); error.response = res; reject(error); } else if (status != null && (status < 200 || status >= 300)) { // uni.request 的 success 仅表示收到 HTTP 响应,不能据此确认业务成功。 const error = new Error(body.msg || `Request failed with status code ${status}`); error.response = res; reject(error); } else { resolve(body); } } catch (error) { reject(error); } }, fail: reject, }); }); } catch (error) { error = await decryptTransportErrorResponse(error, config); if (shouldRetryTransportWithFreshKey(error) && !config.__transportRetried) { invalidateTransportKeyMeta(); config.__transportRetried = true; config.headers.repeatSubmit = false; resetTransportRequestConfig(config); return request(config); } if (isTestBatchUnavailable(error?.response)) { recoverUnavailableTestBatch(error.response); } const response = error.response; const responseStatus = response?.status ?? response?.statusCode; const responseBody = getResponseBody(response); const responseCode = responseBody?.code; const responseMsg = responseBody?.msg; if (responseMsg) { uni.showToast({ title: responseMsg, icon: "none", duration: responseStatus === 429 || responseCode === 429 ? 5000 : 3000, }); throw error; } let { message } = error; if (message === "Network Error") { message = "后端接口连接异常"; } else if (message && message.includes("timeout")) { message = "系统接口请求超时"; } else if (message && message.includes("Request failed with status code")) { message = "系统接口" + message.substr(message.length - 3) + "异常"; } if (message) { toast(message); } throw error; } }; export default request;