245 lines
7.4 KiB
JavaScript
245 lines
7.4 KiB
JavaScript
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 |