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