Files
ckb-SuperAdmin/nkebao/src/api/request.ts
笔记本里的永平 95c90fb1c9 feat: 本次提交更新内容如下
优化
2025-07-22 15:46:16 +08:00

122 lines
3.4 KiB
TypeScript
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, {
AxiosInstance,
AxiosRequestConfig,
Method,
AxiosResponse,
} from "axios";
import { Toast } from "antd-mobile";
const DEFAULT_DEBOUNCE_GAP = 1000;
const debounceMap = new Map<string, number>();
// 死循环请求拦截配置
const FAIL_LIMIT = 3;
const BLOCK_TIME = 30 * 1000; // 30秒
const failMap = new Map<
string,
{ count: number; lastFail: number; blockedUntil?: number }
>();
const instance: AxiosInstance = axios.create({
baseURL: (import.meta as any).env?.VITE_API_BASE_URL || "/api",
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});
instance.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers = config.headers || {};
config.headers["Authorization"] = `Bearer ${token}`;
}
return config;
});
instance.interceptors.response.use(
(res: AxiosResponse) => {
const { code, success, msg } = res.data || {};
if (code === 200 || success) {
return res.data.data ?? res.data;
}
Toast.show({ content: msg || "接口错误", position: "top" });
if (code === 401) {
localStorage.removeItem("token");
const currentPath = window.location.pathname + window.location.search;
if (currentPath === "/login") {
window.location.href = "/login";
} else {
window.location.href = `/login?redirect=${encodeURIComponent(currentPath)}`;
}
}
return Promise.reject(msg || "接口错误");
},
(err) => {
Toast.show({ content: err.message || "网络异常", position: "top" });
return Promise.reject(err);
}
);
export function request(
url: string,
data?: any,
method: Method = "GET",
config?: AxiosRequestConfig,
debounceGap?: number
): Promise<any> {
const gap =
typeof debounceGap === "number" ? debounceGap : DEFAULT_DEBOUNCE_GAP;
const key = `${method}_${url}_${JSON.stringify(data)}`;
const now = Date.now();
const last = debounceMap.get(key) || 0;
// 死循环拦截如果被block直接拒绝
const failInfo = failMap.get(key);
if (failInfo && failInfo.blockedUntil && now < failInfo.blockedUntil) {
Toast.show({ content: "请求失败过多,请稍后再试", position: "top" });
return Promise.reject("请求失败过多,请稍后再试");
}
if (gap > 0 && now - last < gap) {
Toast.show({ content: "请求过于频繁,请稍后再试", position: "top" });
return Promise.reject("请求过于频繁,请稍后再试");
}
debounceMap.set(key, now);
const axiosConfig: AxiosRequestConfig = {
url,
method,
...config,
};
if (method.toUpperCase() === "GET") {
axiosConfig.params = data;
} else {
axiosConfig.data = data;
}
return instance(axiosConfig)
.then((res) => {
// 成功则清除失败计数
failMap.delete(key);
return res;
})
.catch((err) => {
debounceMap.delete(key);
// 失败计数
const fail = failMap.get(key) || { count: 0, lastFail: 0 };
const newCount = now - fail.lastFail < BLOCK_TIME ? fail.count + 1 : 1;
if (newCount >= FAIL_LIMIT) {
failMap.set(key, {
count: newCount,
lastFail: now,
blockedUntil: now + BLOCK_TIME,
});
} else {
failMap.set(key, { count: newCount, lastFail: now });
}
throw err;
});
}
export default request;