/** * 请求封装模块 * 封装 uni.request 支持 GET/POST/PUT/DELETE * 实现 JWT Token 自动携带 * 实现 401 状态码拦截和重新认证 * 支持请求重试机制 * 支持请求取消机制 * Requirements: 1.2, 1.4 */ import { getToken, removeToken, removeUserInfo } from '../utils/storage' import config from '../config/index' // 从统一配置获取 API 基础地址 const BASE_URL = config.API_BASE_URL // 存储进行中的请求任务,用于取消 const pendingRequests = new Map() /** * 生成请求唯一标识 * @param {Object} options 请求配置 * @returns {string} */ function generateRequestKey(options) { const { url, method = 'GET', data = {} } = options return `${method}:${url}:${JSON.stringify(data)}` } /** * 处理401错误 - 清除token * Property 2: Auth Redirect on Invalid Token */ export function handleUnauthorized() { removeToken() removeUserInfo() } /** * 延迟函数 * @param {number} ms 延迟毫秒数 */ const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)) /** * 取消指定请求 * @param {string} requestKey 请求标识 */ export function cancelRequest(requestKey) { if (pendingRequests.has(requestKey)) { const task = pendingRequests.get(requestKey) if (task && typeof task.abort === 'function') { task.abort() } pendingRequests.delete(requestKey) } } /** * 取消所有进行中的请求 */ export function cancelAllRequests() { pendingRequests.forEach((task) => { if (task && typeof task.abort === 'function') { task.abort() } }) pendingRequests.clear() } /** * 取消指定URL前缀的所有请求 * @param {string} urlPrefix URL前缀 */ export function cancelRequestsByPrefix(urlPrefix) { pendingRequests.forEach((task, key) => { if (key.includes(urlPrefix)) { if (task && typeof task.abort === 'function') { task.abort() } pendingRequests.delete(key) } }) } /** * 发起请求(带重试和取消机制) * @param {Object} options 请求配置 * @param {number} retryCount 当前重试次数 * @returns {Promise} */ export function request(options, retryCount = 0) { const { url, method = 'GET', data = {}, header = {}, needAuth = true, retry = true, cancelable = true, cancelPrevious = false } = options const requestKey = generateRequestKey(options) if (cancelPrevious && pendingRequests.has(requestKey)) { cancelRequest(requestKey) } return new Promise((resolve, reject) => { const token = getToken() const requestHeader = { 'Content-Type': 'application/json', ...header } if (needAuth && token) { requestHeader['Authorization'] = `Bearer ${token}` } const requestTask = uni.request({ url: `${BASE_URL}${url}`, method, data, header: requestHeader, timeout: config.REQUEST_TIMEOUT, success: (res) => { if (cancelable) { pendingRequests.delete(requestKey) } const { statusCode, data: responseData } = res if (statusCode === 401) { handleUnauthorized() reject(new Error('未授权,请重新登录')) return } if (statusCode >= 400) { const errorMsg = responseData?.message || '请求失败' reject(new Error(errorMsg)) return } if (responseData.success === false) { reject(new Error(responseData.message || '操作失败')) return } resolve(responseData) }, fail: async (err) => { if (cancelable) { pendingRequests.delete(requestKey) } if (err.errMsg && err.errMsg.includes('abort')) { reject(new Error('请求已取消')) return } console.error('Request failed:', err) if (retry && retryCount < config.REQUEST_RETRY_COUNT) { console.log(`请求失败,${config.REQUEST_RETRY_DELAY}ms 后进行第 ${retryCount + 1} 次重试...`) await delay(config.REQUEST_RETRY_DELAY) try { const result = await request(options, retryCount + 1) resolve(result) } catch (retryErr) { reject(retryErr) } return } reject(new Error('网络连接失败,请检查网络设置')) } }) if (cancelable && requestTask) { pendingRequests.set(requestKey, requestTask) } }) } /** * GET 请求 */ export function get(url, data = {}, options = {}) { return request({ url, method: 'GET', data, ...options }) } /** * POST 请求 */ export function post(url, data = {}, options = {}) { return request({ url, method: 'POST', data, ...options }) } /** * PUT 请求 */ export function put(url, data = {}, options = {}) { return request({ url, method: 'PUT', data, ...options }) } /** * DELETE 请求 */ export function del(url, data = {}, options = {}) { return request({ url, method: 'DELETE', data, ...options }) } export default { request, get, post, put, del, handleUnauthorized, cancelRequest, cancelAllRequests, cancelRequestsByPrefix }