/** * 请求拦截器封装 * 统一处理HTTP请求和响应 */ import { getBaseUrl, getTimeout, getStorageKey, statusCode } from '../config/index.js' // 请求队列(用于取消重复请求) const pendingRequests = new Map() // 是否正在刷新token let isRefreshing = false // 等待刷新token的请求队列 let refreshSubscribers = [] /** * 生成请求唯一标识 */ const generateRequestKey = (config) => { const { url, method, data } = config return `${method}_${url}_${JSON.stringify(data || {})}` } /** * 添加请求到队列 */ const addPendingRequest = (config) => { const key = generateRequestKey(config) if (!pendingRequests.has(key)) { pendingRequests.set(key, config) } } /** * 从队列移除请求 */ const removePendingRequest = (config) => { const key = generateRequestKey(config) pendingRequests.delete(key) } /** * 取消重复请求 */ const cancelPendingRequest = (config) => { const key = generateRequestKey(config) if (pendingRequests.has(key)) { return true } return false } /** * 获取请求头 */ const getHeaders = (needAuth = true) => { const headers = { 'Content-Type': 'application/json' } if (needAuth) { const token = uni.getStorageSync(getStorageKey('token')) if (token) { headers['Authorization'] = `Bearer ${token}` } } return headers } /** * 处理响应错误 * 优先使用后端返回的 message */ const handleResponseError = (response) => { const { statusCode: code, data } = response // 优先使用后端返回的 message const message = data?.message switch (code) { case 400: uni.showToast({ title: message || '请求参数错误', icon: 'none' }) break case 401: // Token过期,清除登录状态并跳转登录页 uni.removeStorageSync(getStorageKey('token')) uni.removeStorageSync(getStorageKey('userInfo')) uni.showToast({ title: message || '登录已过期,请重新登录', icon: 'none' }) setTimeout(() => { uni.reLaunch({ url: '/pages/login/login' }) }, 1500) break case 403: uni.showToast({ title: message || '没有权限执行此操作', icon: 'none' }) break case 404: uni.showToast({ title: message || '请求的资源不存在', icon: 'none' }) break case 500: uni.showToast({ title: message || '服务器错误,请稍后重试', icon: 'none' }) break default: uni.showToast({ title: message || '请求失败', icon: 'none' }) } } /** * 统一请求方法 * @param {Object} options - 请求配置 * @param {boolean} needAuth - 是否需要认证 * @returns {Promise} */ const request = (options = {}, needAuth = true) => { const baseUrl = getBaseUrl() const timeout = getTimeout() const config = { url: options.url.startsWith('http') ? options.url : `${baseUrl}${options.url}`, method: options.method || 'GET', data: options.data || {}, header: { ...getHeaders(needAuth), ...options.header }, timeout: options.timeout || timeout } // 检查重复请求 if (cancelPendingRequest(config)) { return Promise.reject(new Error('重复请求')) } // 添加请求到队列 addPendingRequest(config) return new Promise((resolve, reject) => { uni.request({ ...config, success: (response) => { const { statusCode: code, data } = response // 请求成功 if (code >= 200 && code < 300) { // 如果后端返回的数据没有 code 字段,直接视为成功 if (!data.code || data.code === 200) { resolve(data) } else { // 业务逻辑失败 uni.showToast({ title: data.message || '操作失败', icon: 'none' }) reject(data) } } else { // HTTP错误 handleResponseError(response) reject(response) } }, fail: (error) => { uni.showToast({ title: '网络错误,请检查网络连接', icon: 'none' }) reject(error) }, complete: () => { // 从队列移除请求 removePendingRequest(config) } }) }) } /** * GET请求 */ export const get = (url, params = {}, needAuth = true) => { // 构建查询字符串 const queryString = Object.keys(params) .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) .join('&') const fullUrl = queryString ? `${url}?${queryString}` : url return request({ url: fullUrl, method: 'GET' }, needAuth) } /** * POST请求 */ export const post = (url, data = {}, needAuth = true) => { return request({ url, method: 'POST', data }, needAuth) } /** * PUT请求 */ export const put = (url, data = {}, needAuth = true) => { return request({ url, method: 'PUT', data }, needAuth) } /** * DELETE请求 */ export const del = (url, data = {}, needAuth = true) => { return request({ url, method: 'DELETE', data }, needAuth) } /** * 上传文件 */ export const upload = (url, filePath, formData = {}, needAuth = true) => { const baseUrl = getBaseUrl() const token = uni.getStorageSync(getStorageKey('token')) return new Promise((resolve, reject) => { uni.uploadFile({ url: `${baseUrl}${url}`, filePath, name: 'file', formData, header: needAuth && token ? { 'Authorization': `Bearer ${token}` } : {}, success: (response) => { const data = JSON.parse(response.data) if (data.code === 200) { resolve(data) } else { uni.showToast({ title: data.message || '上传失败', icon: 'none' }) reject(data) } }, fail: (error) => { uni.showToast({ title: '上传失败', icon: 'none' }) reject(error) } }) }) } /** * 下载文件 */ export const download = (url, needAuth = true) => { const baseUrl = getBaseUrl() const token = uni.getStorageSync(getStorageKey('token')) return new Promise((resolve, reject) => { uni.downloadFile({ url: `${baseUrl}${url}`, header: needAuth && token ? { 'Authorization': `Bearer ${token}` } : {}, success: (response) => { if (response.statusCode === 200) { resolve(response) } else { reject(response) } }, fail: (error) => { reject(error) } }) }) } export default { request, get, post, put, delete: del, upload, download }