HaniBlindBox/honey_box/common/request.js
2026-01-20 21:49:37 +08:00

401 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 网络请求工具类
* 封装统一的网络请求方法
*/
import EnvConfig from '@/common/env.js'
import md5 from 'js-md5'
import { apiWhiteList } from '@/common/config.js'
import RouterManager from '@/common/router.js'
import { platform } from '@/common/platform/PlatformFactory'
class RequestManager {
// 缓存对象
static cache = {
data: new Map(),
// 缓存过期时间(毫秒)
expireTime: 5 * 60 * 1000, // 5分钟
// 缓存时间戳
timestamps: new Map()
};
/**
* 检查缓存是否存在且未过期
* @param {string} cacheKey 缓存键
* @returns {boolean} 缓存是否有效
*/
static isCacheValid(cacheKey) {
const now = Date.now();
if (this.cache.data.has(cacheKey)) {
const timestamp = this.cache.timestamps.get(cacheKey);
return now - timestamp < this.cache.expireTime;
}
return false;
}
/**
* 更新缓存
* @param {string} cacheKey 缓存键
* @param {any} data 缓存数据
*/
static updateCache(cacheKey, data) {
this.cache.data.set(cacheKey, data);
this.cache.timestamps.set(cacheKey, Date.now());
}
/**
* 发送带缓存的GET请求
* @param {String} url 请求地址
* @param {Object} data 请求参数
* @param {Boolean} showLoading 是否显示加载提示
* @returns {Promise} 返回请求Promise
*/
static getCache(url, data = {}, showLoading = false) {
// 生成缓存键
const cacheKey = url + JSON.stringify(data);
// 检查缓存是否有效
if (this.isCacheValid(cacheKey)) {
console.log(cacheKey + '缓存有效');
return Promise.resolve(this.cache.data.get(cacheKey));
}
// 如果缓存无效,发起新请求
return this.get(url, data, showLoading).then(result => {
// 更新缓存
this.updateCache(cacheKey, result);
return result;
});
}
/**
* 判断URL是否在白名单中
* @param {String} url 请求地址
* @returns {Boolean} 是否在白名单中
*/
static isUrlInWhitelist(url) {
// 检查URL是否包含白名单中的任一项
return apiWhiteList.some(whiteItem => url.indexOf(whiteItem) > -1);
}
/**
* 生成唯一的nonce值
* @returns {String} nonce值
*/
static generateNonce() {
return md5(Date.now() + Math.random().toString(36).substring(2, 15));
}
/**
* 发送网络请求
* @param {Object} param 请求参数
* @param {String} param.url 请求地址
* @param {Object} param.data 请求数据
* @param {Function} param.success 成功回调
* @param {Function} param.fail 失败回调
* @param {Function} param.complete 完成回调
* @param {Boolean} param.Loading 是否显示加载提示
* @param {String} backpage 返回页面
* @param {String} backtype 返回类型
* @returns {Promise} 返回请求Promise
*/
static request(param, backpage, backtype) {
return new Promise((resolve, reject) => {
// 参数检查
if (!param || typeof param !== 'object') {
reject(new Error('请求参数错误'))
return
}
uni.getNetworkType({
success: function (res) {
if (res.networkType == 'none') {
uni.showToast({
title: '网络连接异常,请检查网络',
icon: 'none'
})
reject(new Error('网络连接异常'))
return
}
}
})
const url = param.url || ''
const method = param.method || 'POST'
const data = param.data || {}
const Loading = param.Loading || false
const token = uni.getStorageSync('token')
let client = platform.code
// 获取API基础URL
const apiBaseUrl = EnvConfig.apiBaseUrl
// 拼接完整请求地址确保URL格式正确
let requestUrl = ''
if (url.startsWith('http://') || url.startsWith('https://')) {
// 如果是完整的URL直接使用
requestUrl = url
} else {
// 否则拼接基础URL和相对路径
// 确保基础URL以/结尾,而请求路径不以/开头
const baseUrlWithSlash = apiBaseUrl.endsWith('/') ? apiBaseUrl : apiBaseUrl + '/'
const path = url.startsWith('/') ? url.substring(1) : url
requestUrl = baseUrlWithSlash + path
}
// console.log('请求URL:', requestUrl)
// 使用正则表达式从URL中提取主机名
const hostRegex = /^(?:https?:\/\/)?([^\/]+)/i
const matches = requestUrl.match(hostRegex)
const host = matches && matches[1] ? matches[1] : 'localhost'
let header = {}
// 添加签名和防重放攻击参数
// 1. 添加时间戳
data.timestamp = Math.floor(Date.now() / 1000)
// 2. 添加nonce随机字符串
data.nonce = RequestManager.generateNonce()
console.log(requestUrl);
if (method.toUpperCase() == 'POST') {
// 按照键名对参数进行排序
const sortedParams = {}
Object.keys(data).sort().forEach(key => {
sortedParams[key] = data[key]
})
// 组合参数为字符串
let signStr = ''
for (const key in sortedParams) {
if (typeof sortedParams[key] === 'object') {
signStr += key + '=' + JSON.stringify(sortedParams[key]) + '&'
} else {
signStr += key + '=' + sortedParams[key] + '&'
}
}
// 获取时间戳,组合为密钥
const timestamp = data.timestamp
const appSecret = host + timestamp
// 添加密钥并去除最后的&
signStr = signStr.substring(0, signStr.length - 1) + appSecret
// console.log('签名字符串:', signStr)
// 使用MD5生成签名
const sign = md5(signStr)
data.sign = sign
header = {
'content-type': 'application/json',
client: client,
'Authorization': token ? `Bearer ${token}` : '',
adid: uni.getStorageSync('_ad_id'),
clickid: uni.getStorageSync('_click_id')
}
} else {
// GET请求添加签名
// 按照键名对参数进行排序
const sortedParams = {}
Object.keys(data).sort().forEach(key => {
sortedParams[key] = data[key]
})
// 组合参数为字符串
let signStr = ''
for (const key in sortedParams) {
signStr += key + '=' + sortedParams[key] + '&'
}
// 获取时间戳,组合为密钥
const timestamp = data.timestamp
const appSecret = host + timestamp
// 添加密钥并去除最后的&
signStr = signStr.substring(0, signStr.length - 1) + appSecret
// console.log('签名字符串:', signStr)
// 使用MD5生成签名
const sign = md5(signStr)
// 添加签名到请求参数
data.sign = sign
header = {
'content-type': 'application/json',
'Authorization': token ? `Bearer ${token}` : '',
client: client,
}
}
// 显示加载提示
if (!Loading) {
uni.showLoading({
title: '加载中...'
})
}
// 发起网络请求
uni.request({
url: requestUrl,
method: method.toUpperCase(),
header: header,
data: data,
success: res => {
console.log("res.data.status", res.data.status)
var pages = getCurrentPages()
if (res.data.status == 1) {
// 请求成功
resolve(res.data)
} else if (res.data.status == 2222) {
// 特殊状态码处理
resolve(res.data)
} else if (res.data.status == -9) {
let pages = getCurrentPages()
console.log(pages[pages.length - 1].route)
setTimeout(() => {
uni.showToast({
title: res.data.msg,
icon: 'none',
success() {
setTimeout(() => {
// #ifdef H5
uni.navigateTo({
url: '/pages/user/bangdingweb'
})
// #endif
// #ifdef MP-WEIXIN
uni.navigateTo({
url: '/pages/user/bangding'
})
// #endif
}, 1500)
}
})
}, 100)
reject(res.data)
} else if (res.data.status == 0) {
setTimeout(function () {
uni.showToast({
title: res.data.msg,
icon: 'none'
})
}, 100)
reject(res.data)
} else if (res.data.status < 0) {
var pages = getCurrentPages()
if (res.data.msg != null) {
if (res.data.msg.indexOf("没有找到用户信息") > -1) {
uni.removeStorageSync('token');
uni.removeStorageSync('userinfo');
}
}
// 获取当前页面路径和参数
var currentPage = pages[pages.length - 1];
if (currentPage) {
var currentRoute = currentPage.route;
var currentParams = currentPage.options || {};
// 只有非登录页面才保存重定向信息
if (currentRoute && currentRoute !== 'pages/user/login') {
// 构建完整的重定向URL
var redirectPath = '/' + currentRoute;
// 如果有参数,拼接参数
if (Object.keys(currentParams).length > 0) {
var paramString = Object.keys(currentParams)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(currentParams[key])}`)
.join('&');
redirectPath += '?' + paramString;
}
// 保存重定向URL到缓存
console.log('保存重定向URL:', redirectPath);
uni.setStorageSync('redirect', redirectPath);
}
}
console.log(requestUrl);
if (RequestManager.isUrlInWhitelist(requestUrl)) {
reject(res.data)
return;
}
setTimeout(() => {
uni.showToast({
title: '请先登录',
icon: 'none'
})
}, 100)
uni.removeStorageSync('token');
uni.removeStorageSync('userinfo');
// 使用新的路由守卫方法进行跳转
RouterManager.navigateTo('/pages/user/login', {}, 'navigateTo')
.catch(err => {
console.error('登录页面跳转失败:', err);
});
reject(res.data)
} else {
reject(res.data)
}
typeof param.success == 'function' && param.success(res.data)
},
fail: e => {
console.error('网络请求失败:', e)
uni.showToast({
title: e.errMsg || '请求失败',
icon: 'none'
})
typeof param.fail == 'function' && param.fail(e)
reject(e)
},
complete: () => {
uni.hideLoading()
typeof param.complete == 'function' && param.complete()
}
})
})
}
/**
* 发送GET请求
* @param {String} url 请求地址
* @param {Object} data 请求参数
* @param {Boolean} showLoading 是否显示加载提示
* @returns {Promise} 返回请求Promise
*/
static get(url, data = {}, showLoading = true) {
return this.request({
url,
data,
method: 'GET',
Loading: !showLoading
})
}
/**
* 发送POST请求
* @param {String} url 请求地址
* @param {Object} data 请求参数
* @param {Boolean} showLoading 是否显示加载提示
* @returns {Promise} 返回请求Promise
*/
static post(url, data = {}, showLoading = true) {
return this.request({
url,
data,
method: 'POST',
Loading: !showLoading
})
}
}
export default RequestManager;