bugfix:未登录循环注册

This commit is contained in:
2026-09-13 01:08:11 +08:00
parent 0a8d60d87f
commit f897e1a391
12 changed files with 268 additions and 38 deletions
+49 -12
View File
@@ -19,8 +19,12 @@ export interface RequestOptions {
header?: Record<string, any>
auth?: boolean // 默认 true,需要鉴权
showError?: boolean // 默认 true,业务错误自动 toast
silentAuth?: boolean // 非关键请求(如未读红点轮询):401 只失败,不触发全局跳登录
}
// 未登录错误码:调用方可通过 e.code === 11001 区分
export const NOT_LOGIN_CODE = 11001
// 登录失效 / 账号禁用后的跳转处理(由 app.ts 注入,避免循环依赖)
let onAuthExpired: (() => void) | null = null
export function setAuthExpiredHandler(handler: () => void): void {
@@ -76,12 +80,35 @@ function showToast(title: string): void {
wx.showToast({ title, icon: 'none', duration: 2000 })
}
function redirectToLogin(): void {
if (onAuthExpired) {
onAuthExpired()
} else {
wx.reLaunch({ url: '/pages/login/login' })
}
// 跳转登录页的幂等锁:并发的多个 401 只会触发一次路由,避免互相打断 webview 生命周期
let redirecting = false
/**
* 跳转登录页(全局唯一入口,幂等):
* 1. 同一时间只允许一次 reLaunch,其余调用直接丢弃;
* 2. 当前已在登录页时不重复跳转。
*/
export function redirectToLogin(): void {
if (redirecting) return
const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : []
const current = pages[pages.length - 1]
if (current && current.route === 'pages/login/login') return
redirecting = true
wx.reLaunch({
url: '/pages/login/login',
complete: () => {
setTimeout(() => {
redirecting = false
}, 1000)
},
})
}
// 登录失效统一处理:先清理登录态(由 app.ts 注入),再走幂等跳转
function handleAuthExpired(): void {
if (onAuthExpired) onAuthExpired()
redirectToLogin()
}
export function request<T = any>(options: RequestOptions): Promise<T> {
@@ -92,11 +119,18 @@ export function request<T = any>(options: RequestOptions): Promise<T> {
header = {},
auth: needAuth = true,
showError = true,
silentAuth = false,
} = options
const fullUrl = url.startsWith('http') ? url : `${BASE_URL}${API_PREFIX}${url}`
return new Promise<T>((resolve, reject) => {
// 未登录:直接失败,不发请求,避免无 token 时批量 401 引发重定向风暴
if (needAuth && !auth.isLoggedIn()) {
reject(new ApiError(NOT_LOGIN_CODE, '未登录'))
return
}
const doCall = (): void => {
const finalHeader: Record<string, any> = {
'Content-Type': 'application/json',
@@ -138,12 +172,15 @@ export function request<T = any>(options: RequestOptions): Promise<T> {
doRefresh().then((ok) => {
if (ok) {
doCall()
} else {
auth.clearTokens()
redirectToLogin()
if (showError) showToast('登录已过期,请重新登录')
reject(new ApiError(code, body.msg || '登录已过期'))
return
}
auth.clearTokens()
// 非关键请求(红点轮询等)静默失败,不打断用户当前页面
if (!silentAuth) {
handleAuthExpired()
if (showError) showToast('登录已过期,请重新登录')
}
reject(new ApiError(code, body.msg || '登录已过期'))
})
return
}
@@ -151,7 +188,7 @@ export function request<T = any>(options: RequestOptions): Promise<T> {
// 账号禁用(HTTP 200,需单独处理)
if (code === 21001) {
auth.clearTokens()
redirectToLogin()
handleAuthExpired()
if (showError) showToast(ERROR_MESSAGES[21001] || '账号已被禁用')
reject(new ApiError(code, body.msg || '账号已被禁用'))
return