Files
2026-01-21 01:37:34 +08:00

204 lines
5.7 KiB
TypeScript
Raw Permalink 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 { isDev, ignoreTokens, config } from "@/config";
import { locale, t } from "@/locale";
import { isNull, isObject, parse, storage } from "../utils";
import { useStore } from "../store";
// 请求参数类型定义
export type RequestOptions = {
url: string; // 请求地址
method?: RequestMethod; // 请求方法
data?: any; // 请求体数据
params?: any; // URL参数
header?: any; // 请求头
timeout?: number; // 超时时间
withCredentials?: boolean; // 是否携带凭证
firstIpv4?: boolean; // 是否优先使用IPv4
enableChunked?: boolean; // 是否启用分块传输
};
// 响应数据类型定义
export type Response = {
code?: number;
message?: string;
data?: any;
};
// 请求队列用于等待token刷新后继续请求
let requests: ((token: string) => void)[] = [];
// 标记token是否正在刷新
let isRefreshing = false;
// 判断当前url是否忽略token校验
const isIgnoreToken = (url: string) => {
return ignoreTokens.some((e) => {
const pattern = e.replace(/\*/g, ".*");
return new RegExp(pattern).test(url);
});
};
/**
* 通用请求方法
* @param options 请求参数
* @returns Promise<T>
*/
export function request(options: RequestOptions): Promise<any> {
let { url, method = "GET", data = {}, header = {}, timeout = 60000 } = options;
const { user } = useStore();
// 开发环境下打印请求信息
if (isDev) {
console.log(`[${method}] ${url}`);
}
// 拼接基础url
if (!url.startsWith("http")) {
url = config.baseUrl + url;
}
// 获取当前token
let Authorization: string | null = user.token;
// 如果是忽略token的接口则不携带token
if (isIgnoreToken(url)) {
Authorization = null;
}
return new Promise((resolve, reject) => {
// 发起请求的实际函数
const next = () => {
uni.request({
url,
method,
data,
header: {
Authorization,
language: locale.value,
...(header as UTSJSONObject)
},
timeout,
success(res) {
if (isDev) console.log(`[Response] ${url} status: ${res.statusCode}`, JSON.stringify(res.data));
// 401 无权限 或 业务码 1001 (未登录/失效)
const resData = res.data;
if (resData != null && isObject(resData)) {
const dataObj = resData as UTSJSONObject;
const bizCode = dataObj["code"];
if (res.statusCode == 401 || bizCode == 1001) {
user.logout();
reject({ message: t("登录已失效"), code: 1001 } as Response);
return;
}
} else if (res.statusCode == 401) {
user.logout();
reject({ message: t("登录已失效"), code: 1001 } as Response);
return;
}
// 502 服务异常
else if (res.statusCode == 502) {
reject({ message: t("服务异常") } as Response);
}
// 404 未找到
else if (res.statusCode == 404) {
return reject({
message: `[404] ${url}`
} as Response);
}
// 200 正常响应
else if (res.statusCode == 200) {
if (res.data == null) {
resolve(null);
} else if (!isObject(res.data as any)) {
// 处理非对象格式的数据(如字符串)
console.warn(`[Service] Received non-object data from ${url}:`, res.data);
resolve(res.data);
} else {
// 解析响应数据
try {
const resDataObj = res.data as UTSJSONObject;
const { code, message, data } = parse<Response>(
resDataObj ?? { code: 0 }
)!;
switch (code) {
case 1000:
console.log(`[Service] Request success for ${url}, data:`, JSON.stringify(data));
resolve(data);
break;
default:
console.warn(`[Service] Business error from ${url}:`, code, message);
reject({ message: message || t("请求失败"), code } as Response);
break;
}
} catch (e : any) {
console.error(`[Service] Parse error for ${url}:`, e);
reject({ message: e.message || t("服务异常") } as Response);
}
}
} else {
reject({ message: t("服务异常") } as Response);
}
},
// 网络请求失败
fail(err) {
console.error(`[Request Failed] ${url}`, err);
reject({ message: err.errMsg } as Response);
}
});
if (isDev) console.log(`[Request Sent] ${url}`);
};
// 非刷新token接口才进行token有效性校验
if (!options.url.includes("/refreshToken")) {
if (!isNull(Authorization)) {
// 判断token是否过期
if (storage.isExpired("token")) {
// 判断refreshToken是否过期
if (storage.isExpired("refreshToken")) {
// 刷新token也过期直接退出登录
user.logout();
return;
}
// 如果当前没有在刷新token则发起刷新
if (!isRefreshing) {
isRefreshing = true;
user.refreshToken()
.then((token) => {
// 刷新成功后,执行队列中的请求
requests.forEach((cb) => cb(token));
requests = [];
isRefreshing = false;
})
.catch((err) => {
reject(err);
user.logout();
});
}
// 将当前请求加入队列等待token刷新后再执行
new Promise((resolve) => {
requests.push((token: string) => {
// 重新设置token
Authorization = token;
next();
resolve(true);
});
});
// 此处return等待token刷新
return;
}
}
}
// token有效直接发起请求
next();
});
}