Files
pj_h5_cloud/src/sheep/utils/request.ts
T
2025-05-19 15:37:59 +08:00

317 lines
9.1 KiB
TypeScript

import Request from "luch-request"
import { router, constant } from "@@/index"
import { userApi } from "../api"
import { isEmpty } from "radash"
import isDevEnv from "./isDevEnv"
import { getAppInfo } from "@@/utils/getAppInfo"
import showVerifyDeviceImpModel from "../components/verifyDeviceImpModel/verifyDeviceImpModel"
const TOKEN_KEY = constant.storageConstant.TOKEN
const USER_INFO_KEY = constant.storageConstant.USERINFO
const SKIP_AUTH_URLS = ["/thirdparty/thirdPartyOAth2", "/sys/auth/refresh-token", "/auth/oauth2/token"]
// 是否正在刷新token
let isRefreshing = false
// 定义一个队列,用于存储失败的请求
let failedQueue: any[] = []
// 存储刷新token的Promise
let refreshTokenPromise: Promise<any> | null = null
// 处理队列中的请求
const processQueue = (error: any, token: any = null) => {
failedQueue.forEach((prom) => {
if (error) {
prom.reject(error)
} else {
prom.resolve(token)
}
})
if (!error) {
failedQueue = []
}
}
// 读取token并处理Authorization请求头
const getAuthHeader = (tokenStorage: any) => {
if (isDevEnv()) {
return tokenStorage?.access_token ? `Bearer ${tokenStorage.access_token}` : null
}
return tokenStorage?.accessToken?.tokenValue ? `Bearer ${tokenStorage.accessToken.tokenValue}` : null
}
// 浙政钉登录 登录成功返回true,否则返回false
async function handleZzdLogin(): Promise<boolean> {
try {
uni.showLoading({ title: "加载中...", mask: true })
const token = new URL(window.location.href).searchParams.get("uabaseToken")
if (!token) {
uni.showToast({ title: "用户凭证已失效,请重新进入智慧后勤应用", icon: "none", duration: 6000 })
return false
}
const res = await userApi.auth(token)
if (res?.code == 0) {
uni.setStorageSync(TOKEN_KEY, res.data)
const { data: userData } = await userApi.userInfo()
uni.setStorageSync(USER_INFO_KEY, userData)
return true
} else {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
if (res.msg == "token已失效") {
uni.showToast({ title: "用户凭证已失效,请退出并重新进入智慧后勤应用", icon: "none", duration: 6000 })
} else {
uni.showToast({ title: res.msg || "登录失败", icon: "none" })
}
return false
}
} catch (error: any) {
uni.showToast({ title: error, icon: "none" })
return false
} finally {
uni.hideLoading()
}
}
// 诗画登录 登录成功返回true,否则返回false
async function handleShLogin(): Promise<boolean> {
try {
uni.showLoading({ title: "登录中...", mask: true })
const userInfo = await (window as any).CP2.getUserInfo()
if (userInfo.code == 0 || !userInfo.data.accountId || !userInfo.data.sessionId) {
uni.showToast({ title: "获取诗画用户信息失败", icon: "none" })
return false
}
const { accountId, sessionId } = userInfo.data
const response = await userApi.authsh({ accountId, sessionId })
// const response = await userApi.authsh({ accountId: "67e790d6a95d5a7b507e53dd", sessionId: "68215b03a95d5a4899d20cb4" }) // 测试
if (response.code == 0) {
uni.setStorageSync(TOKEN_KEY, { accessToken: response.data.accessToken })
const { data: userData } = await userApi.userInfo()
uni.setStorageSync(USER_INFO_KEY, userData)
uni.showToast({ title: "登录成功", icon: "none" })
return true
} else {
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
if (response.msg == "token已失效") {
uni.showToast({ title: "用户凭证已失效,请退出并重新进入智慧后勤应用", icon: "none", duration: 6000 })
return false
}
// 如果是设备未验证,保存响应数据
if (response.data == -1) {
uni.setStorageSync(TOKEN_KEY, { data: -1 })
}
uni.showToast({ title: response.msg || "登录失败", icon: "none" })
return false
}
} catch (error) {
uni.showToast({ title: "登录错误", icon: "none" })
return false
} finally {
uni.hideLoading()
}
}
// 根据应用环境自动登录 返回登录结果
async function autoLogin(): Promise<boolean> {
const appInfo = await getAppInfo()
// 浙政钉环境
if (appInfo == "zzd") {
return await handleZzdLogin()
}
// 诗画环境
else if (appInfo == "sh") {
return await handleShLogin()
} else {
uni.showToast({ title: "app环境错误", icon: "none" })
return false
}
}
// 统一的token刷新函数
async function refreshToken(): Promise<any> {
if (refreshTokenPromise) {
return refreshTokenPromise
}
if (!isRefreshing) {
isRefreshing = true
refreshTokenPromise = (async () => {
try {
const loginResult = await autoLogin()
if (loginResult) {
const newTokenStorage = uni.getStorageSync(TOKEN_KEY)
processQueue(null, newTokenStorage)
return newTokenStorage
} else {
// 检查是否需要设备验证
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
if (tokenStorage?.data === -1) {
// 显示设备验证弹窗
await showVerifyDeviceImpModel(async () => {
// 验证完成后重新登录
const newLoginResult = await autoLogin()
if (newLoginResult) {
const newTokenStorage = uni.getStorageSync(TOKEN_KEY)
processQueue(null, newTokenStorage)
return newTokenStorage
} else {
processQueue(new Error("登录失败"), null)
throw new Error("登录失败")
}
})
} else {
processQueue(new Error("登录失败"), null)
throw new Error("登录失败")
}
}
} catch (error) {
processQueue(error, null)
throw error
} finally {
isRefreshing = false
refreshTokenPromise = null
}
})()
}
return refreshTokenPromise
}
// token过期刷新检查
async function checkAndRefreshToken(tokenStorage: any) {
const { accessToken } = tokenStorage
if (!accessToken) return
const expiresDate = new Date(accessToken.expiresAt).getTime()
// token已过期,尝试刷新
if (new Date().getTime() >= expiresDate) {
return await refreshToken()
}
}
// 创建请求实例
const http = new Request({
baseURL: import.meta.env.VITE_API_URL,
timeout: 15000,
method: "GET",
header: {
Accept: "*/*",
"Content-Type": "application/json;charset=UTF-8",
},
withCredentials: true,
custom: {
showSuccess: false, // 成功提醒 默认使用后端返回值
successMsg: "", // 成功消息
showError: true, // 显示失败消息 默认显示
errorMsg: "", // 失败提醒 默认使用后端返回信息
loadingMsg: "加载中", // loading提醒文字
auth: false, // 需要授权才能请求 默认放开
},
})
// 请求拦截器
http.interceptors.request.use(async (config) => {
// console.log("config", config)
const tokenStorage = uni.getStorageSync(TOKEN_KEY)
const config_header: any = config.header || {}
// 设置租户请求头
config_header["TENANT-ID"] = "1"
if (import.meta.env.VITE_IS_TOC === "true") config_header["CLIENT-TOC"] = "Y"
// 判断是否需要设置认证头
if (!SKIP_AUTH_URLS.some((url) => config.url?.startsWith(url))) {
if (!isEmpty(tokenStorage)) {
// token存在,token过期刷新检查
const newTokenStorage = await checkAndRefreshToken(tokenStorage)
if (newTokenStorage) {
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader) config_header.Authorization = authHeader
} else {
const authHeader = getAuthHeader(tokenStorage)
if (authHeader) config_header.Authorization = authHeader
}
} else {
// token不存在,使用统一的刷新函数
const newTokenStorage = await refreshToken()
if (newTokenStorage) {
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader) config_header.Authorization = authHeader
}
}
}
return config
}, Promise.reject)
// 响应拦截器
http.interceptors.response.use(
async (response) => {
// console.log("response", response)
// 账号密码登录响应结果特殊处理
if (response.config.url === "/auth/oauth2/token") {
return Promise.resolve({
code: 0,
data: response.data,
msg: null,
})
}
return Promise.resolve(response.data)
},
async (error) => {
// console.log("error", error)
// 处理 424 错误(token过期)
if (error.statusCode === 424) {
if (isDevEnv()) {
// 开发环境直接清除token并跳转登录
uni.removeStorageSync(TOKEN_KEY)
uni.removeStorageSync(USER_INFO_KEY)
uni.showToast({ title: "登录已过期,请重新登录", icon: "none" })
router.jump("/pages/test/login")
} else {
try {
// 使用统一的刷新函数
const newTokenStorage = await refreshToken()
if (newTokenStorage) {
// 重新发起失败的请求
const config = error.config
const authHeader = getAuthHeader(newTokenStorage)
if (authHeader && config.header) config.header.Authorization = authHeader
return http.request(config)
}
} catch (err) {
// 错误已经在refreshToken中处理
return Promise.reject(err)
}
}
}
// 显示错误信息
if (error?.data?.msg) {
uni.showToast({ title: error.data.msg, icon: "none", mask: true })
}
return Promise.resolve(error.data)
},
)
// 请求函数
const request = (config: any) => {
return http.middleware(config)
}
export default request