AssetManager.UniApp/utils/api.js
niannian zheng dc857a98a3 refactor(auth): 重构用户认证逻辑并迁移至Pinia存储
将微信静默登录逻辑从App.vue迁移至Pinia的user store,实现状态集中管理
全局注册api模块并优化请求重试机制,增强错误处理和日志记录
移除App.vue中的冗余代码,简化页面组件的数据获取方式
2026-02-26 13:33:57 +08:00

305 lines
9.0 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.

/**
* API工具类 - 统一封装后端API请求
*/
console.log('📦 api.js 模块加载成功')
// API基础URL
const BASE_URL = 'https://localhost:7040/';
// 请求超时时间
const TIMEOUT = 10000;
// 登录锁,防止并发登录
let loginLock = null;
/**
* 获取微信登录码
* @returns {Promise<string>} - 返回微信登录码
*/
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<object>} - 返回登录结果
*/
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和userId');
uni.setStorageSync('token', res.data.data.token);
// 存储userId到本地存储
const userId = res.data.data.userId || res.data.data.user?.userId;
if (userId) {
uni.setStorageSync('userId', userId);
console.log('✅ userId已存储:', userId);
// 验证存储是否成功
const storedUserId = uni.getStorageSync('userId');
console.log('✅ 验证userId存储结果:', storedUserId);
} else {
console.warn('⚠️ 登录响应中未找到userId');
console.log('登录响应数据:', res.data.data);
}
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()
});
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
});
if (res.statusCode === 200) {
return res.data;
} else if (res.statusCode === 401) {
// 未授权清除token和userId并重新登录使用登录锁防止并发
console.log('🔒 登录已过期,开始重新登录...');
uni.removeStorageSync('token');
uni.removeStorageSync('userId');
// 重新登录后重试请求
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.showToast({ title: '系统异常,请稍后重试', icon: 'none', duration: 2000 });
throw new Error('登录已过期,重试次数超限');
}
} else {
console.log('❌ API 请求失败:', {
statusCode: res.statusCode,
message: res.data?.message || `请求失败: ${res.statusCode}`
});
throw new Error(`请求失败: ${res.statusCode}`);
}
} catch (error) {
// 请求失败日志
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);
};
/**
* API接口封装
*/
console.log('📦 api.js 开始导出api对象')
export const api = {
assets: {
getAssetData: () => {
const userId = uni.getStorageSync('userId');
console.log('📤 发起 getAssetData 请求userId:', userId);
return get('/api/v1/portfolio/assets', { userId });
},
getHoldings: () => {
const userId = uni.getStorageSync('userId');
console.log('📤 发起 getHoldings 请求userId:', userId);
return get('/api/v1/portfolio', { userId });
}
},
strategies: {
getStrategies: () => {
const userId = uni.getStorageSync('userId');
console.log('📤 发起 getStrategies 请求userId:', userId);
return get('/api/v1/strategies', { userId });
}
},
user: {
getUserInfo: () => {
const userId = uni.getStorageSync('userId');
console.log('📤 发起 getUserInfo 请求userId:', userId);
return get('/api/user/info', { userId });
},
getUserStats: () => {
const userId = uni.getStorageSync('userId');
console.log('📤 发起 getUserStats 请求userId:', userId);
return get('/api/user/stats', { userId });
}
}
};
console.log('📦 api.js 导出默认api对象')
export default api;