/** * 网络请求工具类 * 封装统一的网络请求方法 */ import config from '@/common/env.js'; import md5 from 'js-md5'; import { getLocalStorage, setLocalStorage } from './cacheService'; import qs from 'qs'; import { clearUserStorage } from '@/common/server/user' class request { /** * 生成唯一的nonce值 * @returns {String} nonce值 */ static generateNonce() { return md5(Date.now() + Math.random().toString(36).substring(2, 15)); } /** * 创建签名 * @param {Object} data 请求数据 * @param {String} host 主机名 * @returns {Object} 带签名的数据和参数字符串 * @private */ static _createSignature(data, host) { // 添加时间戳 data.timestamp = Math.floor(Date.now() / 1000); // 添加nonce随机字符串 data.nonce = request.generateNonce(); // 按照键名对参数进行排序 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; // 使用MD5生成签名 const sign = md5(signStr); data.sign = sign; return { data, signStr }; } /** * 构建请求URL * @param {String} url 请求路径 * @returns {Object} 包含请求URL和主机名的对象 * @private */ static _buildRequestUrl(url) { let requestUrl = ''; if (url.startsWith('http://') || url.startsWith('https://')) { // 如果是完整的URL,直接使用 requestUrl = url; } else { // 否则拼接基础URL和相对路径 // 确保基础URL以/结尾,而请求路径不以/开头 const apiBaseUrl = config.apiBaseUrl; const baseUrlWithSlash = apiBaseUrl.endsWith('/') ? apiBaseUrl : apiBaseUrl + '/'; let routeMap = url; const path = routeMap.startsWith('/') ? routeMap.substring(1) : routeMap; requestUrl = baseUrlWithSlash + path; } // 使用正则表达式从URL中提取主机名 const hostRegex = /^(?:https?:\/\/)?([^\/]+)/i; const matches = requestUrl.match(hostRegex); const host = matches && matches[1] ? matches[1] : 'localhost'; return { requestUrl, host }; } /** * 发送请求 * @param {String} url 请求地址 * @param {Object} fromData 请求数据 * @param {String} method 请求方式 * @param {Boolean} showLoading 是否显示加载提示 * @returns {Promise} 返回请求Promise */ static request(url, fromData = {}, method = 'POST', showLoading = false) { return new Promise((resolve, reject) => { // 使用传入的method而不是重新声明 const requestMethod = method.toUpperCase(); let data = { ...fromData }; // 创建数据的深拷贝,避免修改原数据 // 构建请求URL和提取主机名 const { requestUrl, host } = request._buildRequestUrl(url); // 显示加载提示 if (showLoading) { uni.showLoading({ title: '正在加载中...', mask: true }); } // 创建签名并准备请求数据和头信息 const { data: signedData } = request._createSignature(data, host); data = signedData; // 根据请求方法设置不同的headers const header = { 'content-type': 'application/json' }; const tokenInfo = uni.getStorageSync('tokenInfo'); if (tokenInfo != null && tokenInfo != "") { header['Authorization'] = 'Bearer ' + tokenInfo.token; } const startDate = Date.now(); // 发起网络请求 uni.request({ url: requestUrl, method: requestMethod, header: header, data: data, timeout: 30000, // 设置30秒超时 success: res => { const endDate = Date.now(); console.log(requestUrl, "请求消耗时间", endDate - startDate); if (res.data.code == 14007) { //登录失效 clearUserStorage(); } resolve(res.data); }, fail: e => { console.error('网络请求失败:', e); uni.showToast({ title: e.errMsg || '发送请求失败,请稍后再试!', icon: 'none' }); reject(e); }, complete: () => { if (showLoading) { uni.hideLoading(); } } }); }); } /** * 发送GET请求 * @param {String} url 请求地址 * @param {Object} data 请求参数 * @param {Boolean} showLoading 是否显示加载提示 * @returns {Promise} 返回请求Promise */ static get(url, data = {}, showLoading = false) { return request.request(url, data, 'GET', showLoading); } /** * 发送POST请求 * @param {String} url 请求地址 * @param {Object} data 请求参数 * @param {Boolean} showLoading 是否显示加载提示 * @returns {Promise} 返回请求Promise */ static post(url, data = {}, showLoading = false) { return request.request(url, data, 'POST', showLoading); } /** * 发送get请求,如果缓存存在,则返回缓存数据,否则发送请求,并缓存数据 * @param {String} url 请求地址 * @param {Object} data 请求参数 * @param {Number} time 缓存时间,秒 * @param {Boolean} showLoading 是否显示加载提示 * @returns {Promise} 返回请求Promise */ static async getOrCache(url, data = {}, time = 300, showLoading = false) { const cacheKey = 'cache_' + url + '_' + qs.stringify(data); // console.log('getOrCache', cacheKey, '查询缓存'); const cacheData = getLocalStorage(cacheKey); if (cacheData) { console.log('getOrCache', cacheKey, '缓存命中'); return cacheData; } const res = await request.request(url, data, 'GET', showLoading); setLocalStorage(cacheKey, res, time); return res; } } export default request;