229 lines
5.1 KiB
JavaScript
229 lines
5.1 KiB
JavaScript
/**
|
|
* 请求封装模块
|
|
* 封装 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
|
|
}
|