Files
ZhiQiXiaoYuan/utils/request.js
ChuXun eaab9a762a 1
2025-10-19 20:28:31 +08:00

313 lines
6.6 KiB
JavaScript

/**
* 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
}