Files

245 lines
7.4 KiB
JavaScript
Raw Permalink 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.
import AES from './aes.js'
import { getToken, checkLogin, removeToken, setToken } from './auth'
import { login } from '../api'
import { getDomain, getEnvName } from './env'
import { cachedRequest, getCacheKey, clearCache } from './apiCache.js'
const API_PREFIX = '/api/app'
const config = {
BASE_URL: getDomain() + API_PREFIX,
API_PREFIX: API_PREFIX,
DOMAIN: getDomain(),
ENV_NAME: getEnvName(),
TIMEOUT: 15000,
LOGIN_PAGE: '/pages/login/login',
CODE: {
SUCCESS: 200,
NEED_LOGIN: 1000,
TOKEN_EXPIRED: 1001,
TOKEN_INVALID: 1002
},
MAX_RETRY: 2
}
let isRefreshing = false
let refreshPromise = null
const isLoginPage = () => {
try {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
return currentPage?.route === 'pages/login/login'
} catch (err) {
return false
}
}
const getCurrentPageInfo = () => {
try {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
return {
route: currentPage?.route || 'unknown',
options: currentPage?.options || {}
}
} catch (err) {
return { route: 'unknown', options: {} }
}
}
const refreshToken = async () => {
if (isRefreshing) {
return refreshPromise
}
const currentPage = getCurrentPageInfo()
console.log(`📋 当前页面: ${currentPage.route}`)
if (isLoginPage()) {
console.log('⚠️ 当前在登录页,不执行自动刷新Token')
return Promise.reject(new Error('当前在登录页'))
}
isRefreshing = true
refreshPromise = new Promise(async (resolve, reject) => {
try {
console.log(`🔄 尝试自动刷新Token... (触发页面: ${currentPage.route})`)
const loginRes = await uni.login()
if (!loginRes.code) {
throw new Error('获取微信code失败')
}
const tokenData = await login(loginRes.code)
if (!tokenData?.token) {
throw new Error('刷新Token失败')
}
setToken(tokenData.token)
console.log(`✅ Token刷新成功 (触发页面: ${currentPage.route})`)
resolve(tokenData.token)
} catch (err) {
console.error(`❌ Token刷新失败(触发页面: ${currentPage.route}):`, err)
removeToken()
clearCache()
reject(err)
} finally {
isRefreshing = false
refreshPromise = null
}
})
return refreshPromise
}
const executeRequest = async (options, retryCount = 0) => {
const { url, data = {}, needLogin = true, showLoading = true, encrypt = true } = options
if (showLoading) {
uni.showLoading({ title: '处理中...', mask: true })
}
try {
if (needLogin && url !== '/user/login' && !checkLogin()) {
const currentPage = getCurrentPageInfo()
console.log(`🔴 未登录检测 - 页面: ${currentPage.route}, 接口: ${url}`)
if (retryCount === 0) {
try {
console.log(`🟡 准备调用 refreshToken - 页面: ${currentPage.route}`)
await refreshToken()
console.log(`🟢 refreshToken 成功,重试请求 - 页面: ${currentPage.route}`)
return await executeRequest(options, retryCount + 1)
} catch (err) {
console.log(`🔴 refreshToken 失败: ${err.message} - 页面: ${currentPage.route}`)
uni.showToast({ title: '请先登录', icon: 'none' })
redirectToLogin()
return Promise.reject(new Error('请先登录'))
}
}
uni.showToast({ title: '请先登录', icon: 'none' })
redirectToLogin()
return Promise.reject(new Error('请先登录'))
}
const token = getToken()
console.log(`🔵 [POST] 请求接口:${url},参数:`, data)
let requestData = {}
if (encrypt) {
requestData.data = await AES.encrypt(data)
} else {
requestData = data
}
const response = await new Promise((resolve, reject) => {
const requestTask = uni.request({
url: config.BASE_URL + url,
method: 'POST',
header: { token: token || '', 'Content-Type': 'application/json' },
data: requestData,
timeout: config.TIMEOUT,
success: (res) => resolve(res),
fail: (err) => reject(new Error(err.errMsg?.includes('timeout') ? '请求超时' : '网络异常'))
})
setTimeout(() => requestTask.abort(), config.TIMEOUT)
})
if (response.statusCode !== 200) {
console.log(`🔵 [POST] 请求接口:${url}Error${response.statusCode}`)
throw new Error(`请求出错啦 o(╥﹏╥)o`)
}
const encryptRes = response.data.data || ''
let result = {}
if (encrypt && encryptRes) {
const decryptStr = await AES.decrypt(encryptRes)
result = typeof decryptStr === 'string' ? JSON.parse(decryptStr) : decryptStr
} else {
result = response.data
}
console.log(`🟢 [POST] 请求接口:${url} 响应结果:`, result)
if (result.code === config.CODE.SUCCESS) {
return result.data ?? true
} else if (result.code === config.CODE.NEED_LOGIN ||
result.code === config.CODE.TOKEN_EXPIRED ||
result.code === config.CODE.TOKEN_INVALID) {
if (retryCount < config.MAX_RETRY) {
console.log(`🔄 Token失效,尝试刷新(第${retryCount + 1}次)`)
try {
await refreshToken()
return await executeRequest(options, retryCount + 1)
} catch (err) {
uni.showToast({ title: '登录已过期', icon: 'none' })
removeToken()
clearCache()
setTimeout(() => redirectToLogin(), 1500)
return Promise.reject(new Error('登录已过期'))
}
}
uni.showToast({ title: '登录已过期', icon: 'none' })
removeToken()
clearCache()
setTimeout(() => redirectToLogin(), 1500)
return Promise.reject(new Error('登录已过期'))
} else {
throw new Error(result.msg || '请求失败')
}
} catch (err) {
console.error(`🔴 [POST] 请求异常:`, err)
if (err.message.includes('网络异常')) {
uni.showToast({ title: '网络连接异常,请检查网络', icon: 'none', duration: 2000 })
} else if (err.message.includes('请求超时')) {
uni.showToast({ title: '请求超时,请稍后重试', icon: 'none', duration: 2000 })
} else {
uni.showToast({ title: err.message || '系统异常', icon: 'none', duration: 2000 })
}
return Promise.reject(err)
} finally {
if (showLoading) uni.hideLoading()
}
}
const request = async (options, retryCount = 0) => {
const { cache } = options
if (cache === true && retryCount === 0) {
const cacheKey = getCacheKey(options.url, options.data)
return cachedRequest(cacheKey, () => executeRequest(options, retryCount), options.cacheTTL)
}
if (cache === 'bypass' && retryCount === 0) {
const cacheKey = getCacheKey(options.url, options.data)
clearCache(cacheKey)
}
return executeRequest(options, retryCount)
}
function redirectToLogin() {
try {
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
if (currentPage?.route !== 'pages/login/login') {
uni.redirectTo({ url: config.LOGIN_PAGE })
}
} catch (err) {
uni.reLaunch({ url: config.LOGIN_PAGE })
}
}
request.post = function (url, data = {}, options = {}) {
return this({ url, data, ...options })
}
export const getRequestConfig = () => ({
domain: config.DOMAIN,
apiPrefix: config.API_PREFIX,
baseUrl: config.BASE_URL,
envName: config.ENV_NAME
})
export { clearCache }
export default request