/** * API工具类 - 统一封装后端API请求 */ console.log('📦 api.js 模块加载成功') // API基础URL const BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://localhost:7040/'; // 请求超时时间 const TIMEOUT = 10000; // 登录锁,防止并发登录 let loginLock = null; // 全局loading计数,避免多个请求重复显示/隐藏loading let loadingCount = 0; const showLoading = () => { if (loadingCount === 0) { uni.showLoading({ title: '加载中...', mask: true }); } loadingCount++; }; const hideLoading = () => { loadingCount--; if (loadingCount <= 0) { loadingCount = 0; uni.hideLoading(); } }; /** * 获取微信登录码 * @returns {Promise} - 返回微信登录码 */ const getWechatCode = () => { return new Promise((resolve, reject) => { uni.login({ provider: 'weixin', success: (res) => { if (res.code) { console.log('📱 微信登录码获取成功:', res.code); resolve(res.code); } else { console.error('📱 微信登录码获取失败:', res.errMsg); reject(new Error(`微信登录码获取失败: ${res.errMsg}`)); } }, fail: (err) => { console.error('📱 微信登录失败:', err); reject(new Error(`微信登录失败: ${err.errMsg}`)); } }); }); }; /** * 执行微信登录 * @returns {Promise} - 返回登录结果 */ const doWechatLogin = async () => { try { console.log('🔐 开始执行微信登录'); const code = await getWechatCode(); console.log('🔐 开始调用后端登录接口:', code); const res = await new Promise((resolve, reject) => { uni.request({ url: `${BASE_URL}api/auth/wechat-login`, method: 'POST', data: { code }, header: { 'Content-Type': 'application/json' }, timeout: TIMEOUT, success: resolve, fail: reject }); }); console.log('🔐 后端登录接口响应:', res); if (res.statusCode === 200 && res.data?.code === 200 && res.data?.data?.token) { console.log('✅ 微信登录成功,获取到token'); uni.setStorageSync('token', res.data.data.token); return res.data; } else { console.error('❌ 微信登录失败:', res.data?.message || '登录失败'); throw new Error(res.data?.message || '微信登录失败'); } } catch (error) { console.error('❌ 微信登录异常:', error); throw error; } }; /** * 带重试机制的请求方法 * @param {string} url - 请求URL * @param {string} method - 请求方法 * @param {object} data - 请求数据 * @param {object} headers - 请求头 * @param {number} retryCount - 重试次数 * @returns {Promise} - 返回Promise对象 */ const requestWithRetry = async (url, method = 'GET', data = {}, headers = {}, retryCount = 0) => { try { // 获取存储的token let token = uni.getStorageSync('token'); // 如果没有token,先执行微信登录(使用登录锁防止并发) if (!token) { if (loginLock) { console.log('🔒 等待其他请求完成登录...'); await loginLock; token = uni.getStorageSync('token'); } else { console.log('🔒 未检测到token,开始微信登录...'); loginLock = doWechatLogin(); await loginLock; loginLock = null; token = uni.getStorageSync('token'); console.log('🔒 微信登录成功,获取到token'); } } // 正确处理URL拼接,避免双斜杠 let fullUrl; if (BASE_URL.endsWith('/') && url.startsWith('/')) { fullUrl = BASE_URL + url.substring(1); } else if (!BASE_URL.endsWith('/') && !url.startsWith('/')) { fullUrl = BASE_URL + '/' + url; } else { fullUrl = BASE_URL + url; } // 请求开始日志 console.log('🚀 API 请求开始:', { method, url: fullUrl, data: method === 'GET' ? null : data, hasToken: !!token, retryCount, timestamp: new Date().toISOString() }); // 显示全局loading showLoading(); const res = await new Promise((resolve, reject) => { uni.request({ url: fullUrl, method, data, header: { 'Content-Type': 'application/json', 'Authorization': token ? `Bearer ${token}` : '', ...headers }, timeout: TIMEOUT, success: resolve, fail: reject }); }); // 响应日志 console.log('✅ API 请求成功:', { method, url: fullUrl, statusCode: res.statusCode, responseTime: new Date().toISOString(), data: res.data }); // 隐藏loading hideLoading(); if (res.statusCode === 200) { return res.data; } else if (res.statusCode === 401) { // 未授权,清除token并重新登录(使用登录锁防止并发) console.log('🔒 登录已过期,开始重新登录...'); uni.removeStorageSync('token'); // 重新登录后重试请求 if (retryCount < 3) { if (loginLock) { console.log('🔒 等待其他请求完成登录...'); await loginLock; } else { loginLock = doWechatLogin(); await loginLock; loginLock = null; } console.log('🔒 重新登录成功,开始重试请求...'); return await requestWithRetry(url, method, data, headers, retryCount + 1); } else { console.error('❌ 达到最大重试次数'); uni.$u.toast.error('系统异常,请稍后重试'); throw new Error('登录已过期,重试次数超限'); } } else { console.log('❌ API 请求失败:', { statusCode: res.statusCode, message: res.data?.message || `请求失败: ${res.statusCode}` }); throw new Error(`请求失败: ${res.statusCode}`); } } catch (error) { // 隐藏loading hideLoading(); // 请求失败日志 console.log('❌ API 请求失败:', { url, error, retryCount, timestamp: new Date().toISOString() }); // 如果是网络错误且未达到最大重试次数,尝试重试 if (retryCount < 3 && error.message && error.message.includes('网络请求失败')) { console.log('🔄 网络错误,开始重试...'); return await requestWithRetry(url, method, data, headers, retryCount + 1); } // 达到最大重试次数,提示用户 if (retryCount >= 2) { uni.showToast({ title: '系统异常,请稍后重试', icon: 'none', duration: 2000 }); } throw error; } }; /** * 基础请求方法 * @param {string} url - 请求URL * @param {string} method - 请求方法 * @param {object} data - 请求数据 * @param {object} headers - 请求头 * @returns {Promise} - 返回Promise对象 */ const request = (url, method = 'GET', data = {}, headers = {}) => { return requestWithRetry(url, method, data, headers); }; /** * GET请求 * @param {string} url - 请求URL * @param {object} params - 请求参数 * @param {object} headers - 请求头 * @returns {Promise} - 返回Promise对象 */ export const get = (url, params = {}, headers = {}) => { const queryString = Object.keys(params) .map(key => `${key}=${encodeURIComponent(params[key])}`) .join('&'); const fullUrl = queryString ? `${url}?${queryString}` : url; return request(fullUrl, 'GET', {}, headers); }; /** * POST请求 * @param {string} url - 请求URL * @param {object} data - 请求数据 * @param {object} headers - 请求头 * @returns {Promise} - 返回Promise对象 */ export const post = (url, data = {}, headers = {}) => { return request(url, 'POST', data, headers); }; /** * PUT请求 * @param {string} url - 请求URL * @param {object} data - 请求数据 * @param {object} headers - 请求头 * @returns {Promise} - 返回Promise对象 */ export const put = (url, data = {}, headers = {}) => { return request(url, 'PUT', data, headers); }; /** * DELETE请求 * @param {string} url - 请求URL * @param {object} headers - 请求头 * @returns {Promise} - 返回Promise对象 */ export const del = (url, headers = {}) => { return request(url, 'DELETE', {}, headers); }; /** * API接口封装 */ console.log('📦 api.js 开始导出api对象') export const api = { /** * 资产相关接口 */ assets: { /** * 获取资产数据 * @returns {Promise} 返回资产数据 */ getAssetData: () => { console.log('📤 发起 getAssetData 请求'); return get('/api/v1/portfolio/assets'); }, /** * 获取持仓信息 * @returns {Promise} 返回持仓信息 */ getHoldings: () => { console.log('📤 发起 getHoldings 请求'); return get('/api/v1/portfolio'); }, /** * 获取投资组合详情 * @param {string|number} id - 投资组合ID * @returns {Promise} 返回投资组合详情 */ getPortfolio: (id) => { console.log('📤 发起 getPortfolio 请求:', id); return get(`/api/v1/portfolio/${id}`); }, /** * 获取交易记录 * @param {object} params - 查询参数 * @returns {Promise} 返回交易记录列表 */ getTransactions: (params) => { console.log('📤 发起 getTransactions 请求:', params); return get('/api/v1/portfolio/transactions', params); }, /** * 创建交易记录 * @param {object} data - 交易数据 * @returns {Promise} 返回创建结果 */ createTransaction: (data) => { console.log('📤 发起 createTransaction 请求:', data); return post('/api/v1/portfolio/transactions', data); }, /** * 创建投资组合 * @param {object} data - 投资组合数据 * @returns {Promise} 返回创建结果 */ createPortfolio: (data) => { console.log('📤 发起 createPortfolio 请求:', data); return post('/api/v1/portfolio', data); }, /** * 获取投资组合策略信号 * @param {string|number} id - 投资组合ID * @returns {Promise} 返回策略信号 */ getPortfolioSignal: (id) => { console.log('📤 发起 getPortfolioSignal 请求:', id); return get(`/api/v1/portfolio/${id}/signal`); } }, /** * 策略相关接口 */ strategies: { /** * 获取策略列表 * @returns {Promise} 返回策略列表 */ getStrategies: () => { console.log('📤 发起 getStrategies 请求'); return get('/api/v1/strategy/strategies'); }, /** * 获取策略详情 * @param {string|number} id - 策略ID * @returns {Promise} 返回策略详情 */ getStrategy: (id) => { console.log('📤 发起 getStrategy 请求:', id); return get(`/api/v1/strategy/${id}`); }, /** * 创建策略 * @param {object} data - 策略数据 * @returns {Promise} 返回创建结果 */ createStrategy: (data) => { console.log('📤 发起 createStrategy 请求:', data); return post('/api/v1/strategy', data); }, /** * 更新策略 * @param {string|number} id - 策略ID * @param {object} data - 更新数据 * @returns {Promise} 返回更新结果 */ updateStrategy: (id, data) => { console.log('📤 发起 updateStrategy 请求:', id, data); return put(`/api/v1/strategy/${id}`, data); }, /** * 删除策略 * @param {string|number} id - 策略ID * @returns {Promise} 返回删除结果 */ deleteStrategy: (id) => { console.log('📤 发起 deleteStrategy 请求:', id); return del(`/api/v1/strategy/${id}`); } }, /** * 用户相关接口 */ user: { /** * 获取用户信息 * @returns {Promise} 返回用户信息 */ getUserInfo: () => { console.log('📤 发起 getUserInfo 请求'); return get('/api/v1/user/info'); }, /** * 获取用户统计数据 * @returns {Promise} 返回用户统计数据 */ getUserStats: () => { console.log('📤 发起 getUserStats 请求'); return get('/api/v1/user/stats'); }, /** * 更新用户信息 * @param {object} data - 更新数据 * @returns {Promise} 返回更新结果 */ updateUserInfo: (data) => { console.log('📤 发起 updateUserInfo 请求:', data); return put('/api/v1/user/info', data); } }, /** * 股票代码相关接口 */ ticker: { /** * 模糊搜索股票代码 * @param {string} keyword - 搜索关键词 * @param {number} limit - 返回数量上限 * @returns {Promise} 返回搜索结果 */ search: (keyword, limit = 20) => { console.log('📤 发起 ticker.search 请求:', { keyword, limit }); return get('/api/v1/ticker/search', { keyword, limit }); } } }; console.log('📦 api.js 导出默认api对象') export default api;