Files
track/track-uniapp/src/utils/request.js

158 lines
5.8 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.

/**
* uni.request 封装 — 双 Token 无感刷新 + 并发请求队列
*
* 网络环境选择(三级优先级):
* 1. env_base_url 缓存(App 内运行时动态切换,最高优先级)
* 2. process.env.NODE_ENV 自动判断(development → 本地 IP / production → 公网域名)
* 3. 生产默认 PROD_URL
*/
const PROD_URL = "http://track_back.iris-rs.cn/api/v1";
const LOCAL_URL = "http://192.168.9.80:8011/api/v1";
// ============================================================
// 环境 URL(自动感知:缓存配置 > NODE_ENV > 生产默认)
// ============================================================
export function getBaseUrl() {
// 1. 最高优先级:动态切换的缓存配置(如通过 App 内隐藏菜单切换)
const envUrl = uni.getStorageSync("env_base_url");
if (envUrl) {
return envUrl;
}
// 2. 自动环境判断
if (process.env.NODE_ENV === 'development') {
// 本地运行/真机调试时,自动使用本地开发机 IP
return LOCAL_URL;
}
// 3. 生产打包发行时,自动使用公网域名
return PROD_URL;
}
// ============================================================
// 双 Token 存储
// ============================================================
function getAccessToken() { return uni.getStorageSync("access_token") || ""; }
function getRefreshToken() { return uni.getStorageSync("refresh_token") || ""; }
function setTokens(accessToken, refreshToken) {
uni.setStorageSync("access_token", accessToken);
uni.setStorageSync("refresh_token", refreshToken);
}
function clearTokens() {
uni.removeStorageSync("access_token");
uni.removeStorageSync("refresh_token");
uni.removeStorageSync("token");
uni.removeStorageSync("user");
}
// ============================================================
// 并发请求队列
// ============================================================
let isRefreshing = false;
let retryQueue = [];
function processQueue(error, newToken) {
retryQueue.forEach((p) => {
if (newToken) p.resolve(newToken);
else p.reject(error);
});
retryQueue = [];
}
async function refreshAccessToken() {
const refreshToken = getRefreshToken();
if (!refreshToken) throw new Error("无 Refresh Token");
return new Promise((resolve, reject) => {
uni.request({
url: getBaseUrl() + "/auth/refresh",
method: "POST",
data: { refresh_token: refreshToken },
header: { "Content-Type": "application/json" },
timeout: 10000,
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) resolve(res.data);
else reject(res);
},
fail(err) { reject(err); },
});
});
}
export default function request(options) {
return new Promise((resolve, reject) => {
const url = options.url.startsWith("http") ? options.url : getBaseUrl() + options.url;
const accessToken = getAccessToken();
uni.request({
url,
method: options.method || "GET",
data: options.data || {},
header: {
"Content-Type": "application/json",
...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
...(options.header || {}),
},
timeout: 15000,
success(res) {
const code = res.statusCode;
if (code >= 200 && code < 300) { resolve(res.data); return; }
if (code === 401) {
if (url.includes("/auth/refresh") || url.includes("/auth/login")) { reject(res); return; }
if (isRefreshing) {
retryQueue.push({
resolve: (newToken) => {
options.header = options.header || {};
options.header.Authorization = `Bearer ${newToken}`;
request(options).then(resolve).catch(reject);
},
reject: (err) => reject(err),
});
return;
}
isRefreshing = true;
refreshAccessToken()
.then((data) => {
setTokens(data.access_token, getRefreshToken());
processQueue(null, data.access_token);
options.header = options.header || {};
options.header.Authorization = `Bearer ${data.access_token}`;
request(options).then(resolve).catch(reject);
})
.catch(() => {
console.error("[Request] Refresh Token 也过期,清除登录态");
processQueue(new Error("refresh_failed"), null);
clearTokens();
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" });
setTimeout(() => uni.reLaunch({ url: "/pages/login/login" }), 1000);
reject(res);
})
.finally(() => { isRefreshing = false; });
return;
}
if (code === 403) uni.showToast({ title: res.data?.detail || "无权操作", icon: "none", duration: 3000 });
else if (code === 400) uni.showToast({ title: res.data?.detail || "请求参数有误", icon: "none", duration: 2500 });
else if (code === 409) uni.showToast({ title: res.data?.detail || "操作冲突", icon: "none", duration: 3000 });
else { const d = res.data?.detail || ""; uni.showToast({ title: d || `请求失败 (${code})`, icon: "none", duration: 3000 }); }
reject(res);
},
fail() { uni.showToast({ title: "网络连接失败", icon: "none" }); reject(new Error("network")); },
});
});
}
export function get(url, params = {}) {
const query = Object.entries(params).filter(([, v]) => v != null && v !== "").map(([k, v]) => `${k}=${encodeURIComponent(v)}`).join("&");
return request({ url: query ? `${url}?${query}` : url, method: "GET" });
}
export function post(url, data = {}) { return request({ url, method: "POST", data }); }
export function patch(url, data = {}) { return request({ url, method: "PATCH", data }); }
export function put(url, data = {}) { return request({ url, method: "PUT", data }); }