313 lines
6.6 KiB
JavaScript
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
|
|
}
|