微信小程序页面迁移校验之前 P5任务处理之前

This commit is contained in:
Neo
2026-03-09 01:19:21 +08:00
parent 263bf96035
commit 6e20987d2f
1112 changed files with 153824 additions and 219694 deletions

View File

@@ -0,0 +1,204 @@
/**
* 统一请求封装
*
* - 自动附加 Authorization: Bearer <token>
* - 401 时自动尝试 refresh_token 刷新,刷新成功后重试原请求
* - 刷新失败时清除 token 并跳转 login 页面
* - BASE_URL 从 config.ts 读取(按运行环境自动切换)
*/
import { API_BASE } from "./config"
export interface RequestOptions {
url: string
method?: "GET" | "POST" | "PUT" | "DELETE"
data?: any
header?: Record<string, string>
/** 是否需要认证,默认 true */
needAuth?: boolean
}
/** 是否正在刷新 token防止并发刷新 */
let isRefreshing = false
/** 刷新期间排队的请求(等刷新完成后统一重试) */
let pendingQueue: Array<{
resolve: (value: any) => void
reject: (reason: any) => void
options: RequestOptions
}> = []
/**
* 从 globalData 或 Storage 获取 access_token
*/
function getAccessToken(): string | undefined {
const app = getApp<IAppOption>()
return app.globalData.token || wx.getStorageSync("token") || undefined
}
/**
* 从 globalData 或 Storage 获取 refresh_token
*/
function getRefreshToken(): string | undefined {
const app = getApp<IAppOption>()
return app.globalData.refreshToken || wx.getStorageSync("refreshToken") || undefined
}
/**
* 清除本地存储的所有 token
*/
function clearTokens(): void {
const app = getApp<IAppOption>()
app.globalData.token = undefined
app.globalData.refreshToken = undefined
wx.removeStorageSync("token")
wx.removeStorageSync("refreshToken")
}
/**
* 保存 token 到 globalData 和 Storage
*/
function saveTokens(accessToken: string, refreshToken: string): void {
const app = getApp<IAppOption>()
app.globalData.token = accessToken
app.globalData.refreshToken = refreshToken
wx.setStorageSync("token", accessToken)
wx.setStorageSync("refreshToken", refreshToken)
}
/**
* 跳转到登录页(使用 reLaunch 清空页面栈)
*/
function redirectToLogin(): void {
wx.reLaunch({ url: "/pages/login/login" })
}
/**
* 执行底层 wx.request返回 Promise
*/
function wxRequest(options: RequestOptions): Promise<any> {
return new Promise((resolve, reject) => {
wx.request({
url: `${API_BASE}${options.url}`,
method: options.method || "GET",
data: options.data,
header: options.header || {},
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data)
} else {
reject({ statusCode: res.statusCode, data: res.data })
}
},
fail(err) {
reject({ statusCode: 0, data: err })
},
})
})
}
/**
* 尝试用 refresh_token 刷新令牌
*
* 成功 → 保存新 token 并返回 true
* 失败 → 清除 token、跳转登录页、返回 false
*/
async function tryRefreshToken(): Promise<boolean> {
const rt = getRefreshToken()
if (!rt) {
clearTokens()
redirectToLogin()
return false
}
try {
const data = await wxRequest({
url: "/api/xcx/refresh",
method: "POST",
data: { refresh_token: rt },
header: { "Content-Type": "application/json" },
needAuth: false,
})
if (data.access_token && data.refresh_token) {
saveTokens(data.access_token, data.refresh_token)
return true
}
// 响应格式异常,视为刷新失败
clearTokens()
redirectToLogin()
return false
} catch {
clearTokens()
redirectToLogin()
return false
}
}
/**
* 处理排队中的请求:刷新成功后全部重试,失败则全部拒绝
*/
function flushPendingQueue(success: boolean): void {
const queue = [...pendingQueue]
pendingQueue = []
for (const item of queue) {
if (success) {
// 重试时会自动附加新 token
request(item.options).then(item.resolve, item.reject)
} else {
item.reject({ statusCode: 401, data: { detail: "刷新令牌失败" } })
}
}
}
/**
* 统一请求入口
*
* @param options 请求配置
* @returns 响应数据(已解析的 JSON
*/
export function request(options: RequestOptions): Promise<any> {
const needAuth = options.needAuth !== false
const headers: Record<string, string> = {
"Content-Type": "application/json",
...options.header,
}
// 自动附加 Authorization header
if (needAuth) {
const token = getAccessToken()
if (token) {
headers["Authorization"] = `Bearer ${token}`
}
}
const finalOptions: RequestOptions = { ...options, header: headers }
return wxRequest(finalOptions).catch(async (err) => {
// 非 401 或不需要认证的请求,直接抛出
if (err.statusCode !== 401 || !needAuth) {
throw err
}
// 401 → 尝试刷新 token
if (isRefreshing) {
// 已有刷新请求在进行中,排队等待
return new Promise((resolve, reject) => {
pendingQueue.push({ resolve, reject, options })
})
}
isRefreshing = true
try {
const ok = await tryRefreshToken()
flushPendingQueue(ok)
if (!ok) {
throw { statusCode: 401, data: { detail: "刷新令牌失败" } }
}
// 刷新成功,用新 token 重试原请求
return request(options)
} finally {
isRefreshing = false
}
})
}
export default request