Files
sionrui/frontend/app/web-gold/src/api/http.js
2025-11-13 01:06:28 +08:00

169 lines
4.4 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.
/**
* 应用层 HTTP 客户端
* 使用 mono 级别的 axios 实例,添加应用特定的 401 处理
*/
import { message } from 'ant-design-vue'
import { clearAllTokens, getRefreshToken } from '@gold/utils/token-manager'
import { useUserStore } from '@/stores/user'
import { createClientAxios } from '@gold/api/axios/client'
import { refreshToken } from '@/api/auth'
// 刷新 token 的状态管理
let isRefreshing = false
let refreshPromise = null
/**
* 处理 403 禁止访问错误(应用层特定逻辑)
* 先尝试刷新 token如果失败或没有 refresh token 才提示用户
*/
async function handleApp403Error() {
// 避免重复处理
if (handleApp403Error.processed) {
return
}
handleApp403Error.processed = true
try {
// 检查是否有 refresh token
const refreshTokenValue = getRefreshToken()
if (refreshTokenValue) {
// 如果有 refresh token尝试刷新
try {
// 如果正在刷新,等待刷新完成
if (isRefreshing && refreshPromise) {
await refreshPromise
handleApp403Error.processed = false
return
}
// 开始刷新 token
isRefreshing = true
refreshPromise = refreshToken()
await refreshPromise
// 刷新成功,重置状态
isRefreshing = false
refreshPromise = null
handleApp403Error.processed = false
// 刷新成功,不提示用户(静默刷新)
return
} catch (refreshError) {
// 刷新失败,清除状态
isRefreshing = false
refreshPromise = null
console.error('刷新 token 失败:', refreshError)
// 刷新失败才提示用户
message.warning('登录状态已过期,请重新登录', 3)
handleApp403Error.processed = false
return
}
} else {
// 没有 refresh token提示用户
message.warning('登录状态已过期,请重新登录', 3)
handleApp403Error.processed = false
}
} catch (e) {
console.error('处理 403 错误失败:', e)
handleApp403Error.processed = false
}
}
/**
* 处理 401 未授权错误(应用层特定逻辑)
* 先尝试刷新 token如果失败或没有 refresh token 才退出登录
*/
async function handleApp401Error() {
// 避免重复处理
if (handleApp401Error.processed) {
return
}
handleApp401Error.processed = true
try {
// 检查是否有 refresh token
const refreshTokenValue = getRefreshToken()
if (refreshTokenValue) {
// 如果有 refresh token尝试刷新
try {
// 如果正在刷新,等待刷新完成
if (isRefreshing && refreshPromise) {
await refreshPromise
handleApp401Error.processed = false
return
}
// 开始刷新 token
isRefreshing = true
refreshPromise = refreshToken()
await refreshPromise
// 刷新成功,重置状态
isRefreshing = false
refreshPromise = null
handleApp401Error.processed = false
// 刷新成功,不提示用户(静默刷新)
return
} catch (refreshError) {
// 刷新失败,清除状态
isRefreshing = false
refreshPromise = null
console.error('刷新 token 失败:', refreshError)
// 继续执行退出登录逻辑
}
}
// 没有 refresh token 或刷新失败,执行退出登录
try {
clearAllTokens()
} catch (e) {
console.error('清空 token 失败:', e)
}
try {
const userStore = useUserStore()
userStore.logout()
} catch (e) {
console.error('退出登录失败:', e)
}
setTimeout(() => {
message.warning('登录已过期,请重新登录', 3)
}, 100)
} catch (e) {
console.error('处理 401 错误失败:', e)
} finally {
setTimeout(() => {
handleApp401Error.processed = false
}, 2000)
}
}
/**
* 创建应用层 HTTP 客户端
* 基于 mono 级别的 axios 实例,添加应用特定的错误处理
*/
const http = createClientAxios({
baseURL: '/',
timeout: 180000,
on401: handleApp401Error,
on403: handleApp403Error,
})
// 注意403 处理已在 createClientAxios 的响应拦截器中通过 on403 回调处理
export default http