1
This commit is contained in:
312
utils/request.js
Normal file
312
utils/request.js
Normal file
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* API 请求管理器
|
||||
* 统一处理网络请求、错误处理、请求拦截
|
||||
*/
|
||||
|
||||
const { logger } = require('./logger.js')
|
||||
const { cacheManager } = require('./performance.js')
|
||||
|
||||
class Request {
|
||||
constructor(config = {}) {
|
||||
this.baseURL = config.baseURL || ''
|
||||
this.timeout = config.timeout || 10000
|
||||
this.header = config.header || {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
this.interceptors = {
|
||||
request: [],
|
||||
response: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求拦截器
|
||||
*/
|
||||
useRequestInterceptor(handler) {
|
||||
this.interceptors.request.push(handler)
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应拦截器
|
||||
*/
|
||||
useResponseInterceptor(handler) {
|
||||
this.interceptors.response.push(handler)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*/
|
||||
async request(options) {
|
||||
let config = {
|
||||
url: this.baseURL + options.url,
|
||||
method: options.method || 'GET',
|
||||
data: options.data || {},
|
||||
header: { ...this.header, ...options.header },
|
||||
timeout: options.timeout || this.timeout
|
||||
}
|
||||
|
||||
// 执行请求拦截器
|
||||
for (const interceptor of this.interceptors.request) {
|
||||
config = await interceptor(config)
|
||||
}
|
||||
|
||||
// 检查缓存
|
||||
if (config.method === 'GET' && options.cache) {
|
||||
const cacheKey = this._getCacheKey(config)
|
||||
const cached = cacheManager.get(cacheKey)
|
||||
if (cached) {
|
||||
logger.debug('使用缓存数据', { url: config.url })
|
||||
return cached
|
||||
}
|
||||
}
|
||||
|
||||
const startTime = Date.now()
|
||||
|
||||
try {
|
||||
const response = await this._wxRequest(config)
|
||||
|
||||
// 执行响应拦截器
|
||||
let result = response
|
||||
for (const interceptor of this.interceptors.response) {
|
||||
result = await interceptor(result)
|
||||
}
|
||||
|
||||
// 缓存GET请求结果
|
||||
if (config.method === 'GET' && options.cache) {
|
||||
const cacheKey = this._getCacheKey(config)
|
||||
cacheManager.set(cacheKey, result, options.cacheTTL)
|
||||
}
|
||||
|
||||
// 记录性能
|
||||
const duration = Date.now() - startTime
|
||||
logger.debug('请求成功', {
|
||||
url: config.url,
|
||||
duration: `${duration}ms`
|
||||
})
|
||||
|
||||
return result
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime
|
||||
logger.error('请求失败', {
|
||||
url: config.url,
|
||||
duration: `${duration}ms`,
|
||||
error
|
||||
})
|
||||
|
||||
throw this._handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信请求封装
|
||||
*/
|
||||
_wxRequest(config) {
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.request({
|
||||
...config,
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(res.data)
|
||||
} else {
|
||||
reject({
|
||||
statusCode: res.statusCode,
|
||||
message: res.data.message || '请求失败',
|
||||
data: res.data
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: (err) => {
|
||||
reject({
|
||||
statusCode: 0,
|
||||
message: err.errMsg || '网络错误',
|
||||
error: err
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误处理
|
||||
*/
|
||||
_handleError(error) {
|
||||
const errorMap = {
|
||||
0: '网络连接失败',
|
||||
400: '请求参数错误',
|
||||
401: '未授权,请登录',
|
||||
403: '拒绝访问',
|
||||
404: '请求的资源不存在',
|
||||
500: '服务器错误',
|
||||
502: '网关错误',
|
||||
503: '服务不可用',
|
||||
504: '网关超时'
|
||||
}
|
||||
|
||||
return {
|
||||
code: error.statusCode,
|
||||
message: errorMap[error.statusCode] || error.message || '未知错误',
|
||||
data: error.data
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存键
|
||||
*/
|
||||
_getCacheKey(config) {
|
||||
return `${config.method}:${config.url}:${JSON.stringify(config.data)}`
|
||||
}
|
||||
|
||||
/**
|
||||
* GET 请求
|
||||
*/
|
||||
get(url, params = {}, options = {}) {
|
||||
return this.request({
|
||||
url,
|
||||
method: 'GET',
|
||||
data: params,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* POST 请求
|
||||
*/
|
||||
post(url, data = {}, options = {}) {
|
||||
return this.request({
|
||||
url,
|
||||
method: 'POST',
|
||||
data,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT 请求
|
||||
*/
|
||||
put(url, data = {}, options = {}) {
|
||||
return this.request({
|
||||
url,
|
||||
method: 'PUT',
|
||||
data,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE 请求
|
||||
*/
|
||||
delete(url, data = {}, options = {}) {
|
||||
return this.request({
|
||||
url,
|
||||
method: 'DELETE',
|
||||
data,
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
upload(url, filePath, formData = {}, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const uploadTask = wx.uploadFile({
|
||||
url: this.baseURL + url,
|
||||
filePath,
|
||||
name: options.name || 'file',
|
||||
formData,
|
||||
header: { ...this.header, ...options.header },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(JSON.parse(res.data))
|
||||
} else {
|
||||
reject({
|
||||
statusCode: res.statusCode,
|
||||
message: '上传失败'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
|
||||
// 进度回调
|
||||
if (options.onProgress) {
|
||||
uploadTask.onProgressUpdate(options.onProgress)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
download(url, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const downloadTask = wx.downloadFile({
|
||||
url: this.baseURL + url,
|
||||
header: { ...this.header, ...options.header },
|
||||
success: (res) => {
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(res.tempFilePath)
|
||||
} else {
|
||||
reject({
|
||||
statusCode: res.statusCode,
|
||||
message: '下载失败'
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
|
||||
// 进度回调
|
||||
if (options.onProgress) {
|
||||
downloadTask.onProgressUpdate(options.onProgress)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
const request = new Request({
|
||||
baseURL: 'https://api.example.com', // TODO: 替换为实际API地址
|
||||
timeout: 10000
|
||||
})
|
||||
|
||||
// 添加请求拦截器
|
||||
request.useRequestInterceptor(async (config) => {
|
||||
// 添加 token
|
||||
const token = wx.getStorageSync('token')
|
||||
if (token) {
|
||||
config.header['Authorization'] = `Bearer ${token}`
|
||||
}
|
||||
|
||||
// 显示加载提示
|
||||
if (config.loading !== false) {
|
||||
wx.showLoading({
|
||||
title: '加载中...',
|
||||
mask: true
|
||||
})
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
|
||||
// 添加响应拦截器
|
||||
request.useResponseInterceptor(async (response) => {
|
||||
// 隐藏加载提示
|
||||
wx.hideLoading()
|
||||
|
||||
// 统一处理业务错误码
|
||||
if (response.code !== 0 && response.code !== 200) {
|
||||
wx.showToast({
|
||||
title: response.message || '操作失败',
|
||||
icon: 'none'
|
||||
})
|
||||
throw response
|
||||
}
|
||||
|
||||
return response.data || response
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
Request,
|
||||
request
|
||||
}
|
||||
Reference in New Issue
Block a user