Files
m2pool_web_frontend/mining-pool/src/utils/request.js

406 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import axios from 'axios'
import errorCode from './errorCode'
import { Notification, MessageBox, Message } from 'element-ui'
import loadingManager from './loadingManager';
import errorNotificationManager from './errorNotificationManager';
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000,
})
// 网络错误相关配置
const NETWORK_ERROR_THROTTLE_TIME = 5000; // 错误提示节流时间
const RETRY_DELAY = 2000; // 重试间隔时间
const MAX_RETRY_TIMES = 3; // 最大重试次数
const RETRY_WINDOW = 60000; // 60秒重试窗口
let lastNetworkErrorTime = 0; // 上次网络错误提示时间
let pendingRequests = new Map();
// 网络状态监听器
// 网络状态最后提示时间
let lastNetworkStatusTime = {
online: 0,
offline: 0
};
// 创建一个全局标志,确保每次网络恢复只显示一次提示
let networkRecoveryInProgress = false;
// 网络状态监听器
window.addEventListener('online', () => {
const now = Date.now();
// 避免短时间内多次触发
if (networkRecoveryInProgress) {
console.log('[网络] 网络恢复处理已在进行中,忽略重复事件');
return;
}
networkRecoveryInProgress = true;
// 严格检查是否应该显示提示
if (now - lastNetworkStatusTime.online > 30000) { // 30秒内不重复提示
lastNetworkStatusTime.online = now;
try {
if (window.vm && window.vm.$message) {
// 确保消息只显示一次
window.vm.$message({
message: window.vm.$i18n.t('home.networkReconnected') || '网络已重新连接,正在恢复数据...',
type: 'success',
duration: 5000,
showClose: true,
});
console.log('[网络] 显示网络恢复提示, 时间:', new Date().toLocaleTimeString());
}
} catch (e) {
console.error('[网络] 显示网络恢复提示失败:', e);
}
} else {
console.log('[网络] 抑制重复的网络恢复提示, 间隔过短:', now - lastNetworkStatusTime.online + 'ms');
}
// 网络恢复时,重试所有待处理的请求
const pendingPromises = [];
pendingRequests.forEach(async (request, key) => {
if (now - request.timestamp <= RETRY_WINDOW) {
try {
// 获取新的响应数据
const response = await service(request.config);
pendingPromises.push(response);
// 执行请求特定的回调
if (request.callback && typeof request.callback === 'function') {
request.callback(response);
}
// 处理特定类型的请求
if (window.vm) {
// 处理图表数据请求
if (request.config.url.includes('getPoolPower') && response && response.data) {
// 触发图表更新事件
window.dispatchEvent(new CustomEvent('chart-data-updated', {
detail: { type: 'poolPower', data: response.data }
}));
}
else if (request.config.url.includes('getNetPower') && response && response.data) {
window.dispatchEvent(new CustomEvent('chart-data-updated', {
detail: { type: 'netPower', data: response.data }
}));
}
else if (request.config.url.includes('getBlockInfo') && response && response.rows) {
window.dispatchEvent(new CustomEvent('chart-data-updated', {
detail: { type: 'blockInfo', data: response.rows }
}));
}
}
pendingRequests.delete(key);
} catch (error) {
console.error('重试请求失败:', error);
pendingRequests.delete(key);
}
} else {
pendingRequests.delete(key);
}
});
// 等待所有请求完成
Promise.allSettled(pendingPromises).then(() => {
// 重置所有 loading 状态
if (loadingManager) {
loadingManager.resetAllLoadingStates();
}
// 手动重置一些关键的 loading 状态
if (window.vm) {
// 常见的加载状态
const commonLoadingProps = [
'minerChartLoading', 'reportBlockLoading', 'apiPageLoading',
'MiningLoading', 'miniLoading'
];
commonLoadingProps.forEach(prop => {
if (typeof window.vm[prop] !== 'undefined') {
window.vm[prop] = false;
}
});
}
// 触发网络重试完成事件
window.dispatchEvent(new CustomEvent('network-retry-complete'));
// 重置网络恢复标志
setTimeout(() => {
networkRecoveryInProgress = false;
}, 5000); // 5秒后允许再次处理网络恢复
});
});
// 使用错误提示管理器控制网络断开提示
window.addEventListener('offline', () => {
if (window.vm && window.vm.$message && errorNotificationManager.canShowError('networkOffline')) {
window.vm.$message({
message: window.vm.$i18n.t('home.networkOffline') || '网络连接已断开,系统将在恢复连接后自动重试',
type: 'error',
duration: 5000,
showClose: true,
});
}
});
service.defaults.retry = 2;// 重试次数
service.defaults.retryDelay = 2000;
service.defaults.shouldRetry = (error) => {
// 只有网络错误或超时错误才进行重试
return error.message === "Network Error" || error.message.includes("timeout");
};
localStorage.setItem('superReportError', "")
let superReportError = localStorage.getItem('superReportError')
window.addEventListener("setItem", () => {
superReportError = localStorage.getItem('superReportError')
});
// request拦截器
service.interceptors.request.use(config => {
superReportError = ""
// retryCount =0
localStorage.setItem('superReportError', "")
// 是否需要设置 token
let token
try {
token = JSON.parse(localStorage.getItem('token'))
} catch (e) {
console.log(e);
}
if (token) {
config.headers['Authorization'] = token
}
if (config.method == 'get' && config.data) {
config.params = config.data
}
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?';
for (const propName of Object.keys(config.params)) {
const value = config.params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']';
let subPart = encodeURIComponent(params) + '=';
url += subPart + encodeURIComponent(value[key]) + '&';
}
}
} else {
url += part + encodeURIComponent(value) + "&";
}
}
}
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
return config
}, error => {
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
if (code === 421) {
localStorage.removeItem('token')
// 系统状态已过期请重新点击SUPPORT按钮进入
superReportError = localStorage.getItem('superReportError')
if (!superReportError) {
superReportError = 421
localStorage.setItem('superReportError', superReportError)
MessageBox.confirm(window.vm.$i18n.t(`user.loginExpired`), window.vm.$i18n.t(`user.overduePrompt`), {
distinguishCancelAndClose: true,
confirmButtonText: window.vm.$i18n.t(`user.login`),
cancelButtonText: window.vm.$i18n.t(`user.Home`),
// showCancelButton: false, // 隐藏取消按钮
closeOnClickModal: false, // 点击空白处不关闭对话框
showClose: false, // 隐藏关闭按钮
type: 'warning'
}
).then(() => {
window.vm.$router.push("/login")
localStorage.removeItem('token')
}).catch(() => {
window.vm.$router.push("/")
localStorage.removeItem('token')
});
}
return Promise.reject('登录状态已过期')
} else if (code >= 500 && !superReportError) {
superReportError = 500
localStorage.setItem('superReportError', superReportError)
Message({
dangerouslyUseHTMLString: true,
message: msg,
type: 'error',
showClose: true
})
// throw msg; // 抛出错误,中断请求链并触发后续的错误处理逻辑
// return Promise.reject(new Error(msg))
} else if (code !== 200) {
Notification.error({
title: msg
})
return Promise.reject('error')
} else {
return res.data
}
},
error => {
let { message } = error;
if (message == "Network Error" || message.includes("timeout")) {
if (!navigator.onLine) {
// 断网状态,添加到重试队列
const requestKey = JSON.stringify({
url: error.config.url,
method: error.config.method,
params: error.config.params,
data: error.config.data
});
// 根据URL确定请求类型并记录回调
let callback = null;
if (error.config.url.includes('getPoolPower')) {
callback = (data) => {
if (window.vm) {
// 清除loading状态
window.vm.minerChartLoading = false;
}
};
} else if (error.config.url.includes('getBlockInfo')) {
callback = (data) => {
if (window.vm) {
window.vm.reportBlockLoading = false;
}
};
}
if (!pendingRequests.has(requestKey)) {
pendingRequests.set(requestKey, {
config: error.config,
timestamp: Date.now(),
retryCount: 0,
callback: callback
});
console.log('请求已加入断网重连队列:', error.config.url);
}
} else {
// 网络已连接,但请求失败,尝试重试
// 确保 config 中有 __retryCount 字段
error.config.__retryCount = error.config.__retryCount || 0;
// 判断是否可以重试
if (error.config.__retryCount < service.defaults.retry && service.defaults.shouldRetry(error)) {
// 增加重试计数
error.config.__retryCount += 1;
console.log(`[请求重试] ${error.config.url} - 第 ${error.config.__retryCount} 次重试`);
// 创建新的Promise等待一段时间后重试
return new Promise(resolve => {
setTimeout(() => {
resolve(service(error.config));
}, service.defaults.retryDelay);
});
}
// 达到最大重试次数,不再重试
console.log(`[请求失败] ${error.config.url} - 已达到最大重试次数`);
}
}
if (!superReportError) {
superReportError = "error"
localStorage.setItem('superReportError', superReportError)
//使用错误提示管理器errorNotificationManager
if (errorNotificationManager.canShowError(message)) {
if (message == "Network Error") {
Message({
message: window.vm.$i18n.t(`home.NetworkError`),
type: 'error',
duration: 4 * 1000,
showClose: true
});
}
else if (message.includes("timeout")) {
Message({
message: window.vm.$i18n.t(`home.requestTimeout`),
type: 'error',
duration: 5 * 1000,
showClose: true
});
}
else if (message.includes("Request failed with status code")) {
Message({
message: "系统接口" + message.substr(message.length - 3) + "异常",
type: 'error',
duration: 5 * 1000,
showClose: true
});
} else {
Message({
message: message,
type: 'error',
duration: 5 * 1000,
showClose: true
});
}
} else {
// 避免完全不提示,可以在控制台记录被抑制的错误
console.log('[错误提示] 已抑制重复错误:', message);
}
}
return Promise.reject(error)
}
)
export default service