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

120 lines
4.6 KiB
JavaScript

/**
* uni.request 封装 — 本地开发环境 (192.168.9.80)
* 双 Token 无感刷新 + 并发请求队列
*/
const BASE_URL = "http://192.168.9.80:8011/api/v1";
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: BASE_URL + "/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 : BASE_URL + 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 }); }