// 如果设置了VITE_API_URL环境变量,使用它;否则在开发环境使用相对路径(通过vite代理),生产环境使用默认值 const API_BASE_URL = import.meta.env.VITE_API_URL || (import.meta.env.DEV ? '' : 'http://localhost:8000'); // 登录鉴权:JWT token(Authorization: Bearer xxx) const AUTH_TOKEN_STORAGE_KEY = 'ats_auth_token'; export const getAuthToken = () => { try { return localStorage.getItem(AUTH_TOKEN_STORAGE_KEY) || ''; } catch (e) { return ''; } }; export const setAuthToken = (token) => { try { const t = String(token || '').trim(); if (!t) { localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY); return; } localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, t); } catch (e) { // ignore } }; export const clearAuthToken = () => setAuthToken(''); // 多账号:前端通过 Header 选择账号(默认 1) const ACCOUNT_ID_STORAGE_KEY = 'ats_account_id'; export const getCurrentAccountId = () => { try { const v = localStorage.getItem(ACCOUNT_ID_STORAGE_KEY); const n = parseInt(v || '1', 10); return Number.isFinite(n) && n > 0 ? n : 1; } catch (e) { return 1; } }; export const setCurrentAccountId = (accountId) => { try { const n = parseInt(String(accountId || '1'), 10); localStorage.setItem(ACCOUNT_ID_STORAGE_KEY, String(Number.isFinite(n) && n > 0 ? n : 1)); } catch (e) { // ignore } }; export const clearCurrentAccountId = () => { try { localStorage.removeItem(ACCOUNT_ID_STORAGE_KEY); } catch (e) { // ignore } }; const withAuthHeaders = (headers = {}) => { const token = getAuthToken(); if (!token) return { ...headers }; return { ...headers, Authorization: `Bearer ${token}` }; }; const withAccountHeaders = (headers = {}) => { const aid = getCurrentAccountId(); return withAuthHeaders({ ...headers, 'X-Account-Id': String(aid) }); }; // 构建API URL的辅助函数,避免双斜杠和格式问题 const buildUrl = (path) => { const baseUrl = API_BASE_URL.endsWith('/') ? API_BASE_URL.slice(0, -1) : API_BASE_URL; const cleanPath = path.startsWith('/') ? path : `/${path}`; return baseUrl ? `${baseUrl}${cleanPath}` : cleanPath; }; export const api = { // 登录 login: async (username, password) => { const response = await fetch(buildUrl('/api/auth/login'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '登录失败' })); throw new Error(error.detail || '登录失败'); } const data = await response.json(); if (data?.access_token) setAuthToken(data.access_token); return data; }, me: async () => { const response = await fetch(buildUrl('/api/auth/me'), { headers: withAuthHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取登录信息失败' })); throw new Error(error.detail || '获取登录信息失败'); } return response.json(); }, // 公共状态(非管理员也可用) getPublicStatus: async () => { const response = await fetch(buildUrl('/api/public/status')) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取服务状态失败' })) throw new Error(error.detail || '获取服务状态失败') } return response.json() }, // 账号管理 getAccounts: async () => { const response = await fetch(buildUrl('/api/accounts'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取账号列表失败' })); throw new Error(error.detail || '获取账号列表失败'); } return response.json(); }, createAccount: async (data) => { const response = await fetch(buildUrl('/api/accounts'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify(data || {}), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '创建账号失败' })); throw new Error(error.detail || '创建账号失败'); } return response.json(); }, updateAccount: async (accountId, data) => { const response = await fetch(buildUrl(`/api/accounts/${accountId}`), { method: 'PUT', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify(data || {}), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '更新账号失败' })); throw new Error(error.detail || '更新账号失败'); } return response.json(); }, updateAccountCredentials: async (accountId, data) => { const response = await fetch(buildUrl(`/api/accounts/${accountId}/credentials`), { method: 'PUT', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify(data || {}), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '更新账号密钥失败' })); throw new Error(error.detail || '更新账号密钥失败'); } return response.json(); }, // 交易进程(按账号;需要 owner 或 admin) getAccountTradingStatus: async (accountId) => { const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/status`), { headers: withAccountHeaders() }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取交易进程状态失败' })) throw new Error(error.detail || '获取交易进程状态失败') } return response.json() }, startAccountTrading: async (accountId) => { const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/start`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '启动交易进程失败' })) throw new Error(error.detail || '启动交易进程失败') } return response.json() }, stopAccountTrading: async (accountId) => { const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/stop`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '停止交易进程失败' })) throw new Error(error.detail || '停止交易进程失败') } return response.json() }, restartAccountTrading: async (accountId) => { const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/restart`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '重启交易进程失败' })) throw new Error(error.detail || '重启交易进程失败') } return response.json() }, ensureAccountTradingProgram: async (accountId) => { const response = await fetch(buildUrl(`/api/accounts/${accountId}/trading/ensure-program`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '生成/刷新 supervisor 配置失败' })) throw new Error(error.detail || '生成/刷新 supervisor 配置失败') } return response.json() }, // 配置管理 getConfigMeta: async () => { const response = await fetch(buildUrl('/api/config/meta'), { headers: withAuthHeaders() }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取配置元信息失败' })) throw new Error(error.detail || '获取配置元信息失败') } return response.json() }, getConfigs: async () => { const response = await fetch(buildUrl('/api/config'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取配置失败' })); throw new Error(error.detail || '获取配置失败'); } return response.json(); }, getConfig: async (key) => { const response = await fetch(buildUrl(`/api/config/${key}`), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取配置失败' })); throw new Error(error.detail || '获取配置失败'); } return response.json(); }, updateConfig: async (key, data) => { const response = await fetch(buildUrl(`/api/config/${key}`), { method: 'PUT', headers: withAccountHeaders({'Content-Type': 'application/json'}), body: JSON.stringify(data) }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || '更新配置失败'); } return response.json(); }, updateConfigsBatch: async (configs) => { const response = await fetch(buildUrl('/api/config/batch'), { method: 'POST', headers: withAccountHeaders({'Content-Type': 'application/json'}), body: JSON.stringify(configs) }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '批量更新配置失败' })); throw new Error(error.detail || '批量更新配置失败'); } return response.json(); }, // 检查配置可行性 checkConfigFeasibility: async () => { const response = await fetch(buildUrl('/api/config/feasibility-check'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '检查配置可行性失败' })); throw new Error(error.detail || '检查配置可行性失败'); } return response.json(); }, // 交易记录 getTrades: async (params = {}) => { const query = new URLSearchParams(params).toString(); const url = query ? `${buildUrl('/api/trades')}?${query}` : buildUrl('/api/trades'); const response = await fetch(url, { method: 'GET', headers: { ...withAccountHeaders({ 'Content-Type': 'application/json' }), }, }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取交易记录失败' })); throw new Error(error.detail || '获取交易记录失败'); } return response.json(); }, getTradeStats: async (params = {}) => { const query = new URLSearchParams(params).toString(); const url = query ? `${buildUrl('/api/trades/stats')}?${query}` : buildUrl('/api/trades/stats'); const response = await fetch(url, { method: 'GET', headers: { ...withAccountHeaders({ 'Content-Type': 'application/json' }), }, }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取交易统计失败' })); throw new Error(error.detail || '获取交易统计失败'); } return response.json(); }, // 统计 getPerformance: async (days = 7) => { const response = await fetch(buildUrl(`/api/stats/performance?days=${days}`), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取性能统计失败' })); throw new Error(error.detail || '获取性能统计失败'); } return response.json(); }, getDashboard: async () => { const response = await fetch(buildUrl('/api/dashboard'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取仪表板数据失败' })); throw new Error(error.detail || '获取仪表板数据失败'); } return response.json(); }, // 平仓操作 closePosition: async (symbol) => { const response = await fetch(buildUrl(`/api/account/positions/${symbol}/close`), { method: 'POST', headers: { ...withAccountHeaders({ 'Content-Type': 'application/json' }), }, }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '平仓失败' })); throw new Error(error.detail || '平仓失败'); } return response.json(); }, // 补挂止盈止损(交易所保护单) ensurePositionSLTP: async (symbol) => { const response = await fetch(buildUrl(`/api/account/positions/${symbol}/sltp/ensure`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '补挂止盈止损失败' })); throw new Error(error.detail || '补挂止盈止损失败'); } return response.json(); }, ensureAllPositionsSLTP: async (limit = 50) => { const response = await fetch(buildUrl(`/api/account/positions/sltp/ensure-all?limit=${encodeURIComponent(limit)}`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '批量补挂止盈止损失败' })); throw new Error(error.detail || '批量补挂止盈止损失败'); } return response.json(); }, // 同步持仓状态 syncPositions: async () => { const response = await fetch(buildUrl('/api/account/positions/sync'), { method: 'POST', headers: { ...withAccountHeaders({ 'Content-Type': 'application/json' }), }, }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '同步失败' })); throw new Error(error.detail || '同步失败'); } return response.json(); }, // 交易推荐 getRecommendations: async (params = {}) => { // 默认使用实时推荐 if (!params.type) { params.type = 'realtime' } const query = new URLSearchParams(params).toString(); const url = query ? `${buildUrl('/api/recommendations')}?${query}` : buildUrl('/api/recommendations'); const response = await fetch(url, { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取推荐失败' })); throw new Error(error.detail || '获取推荐失败'); } return response.json(); }, bookmarkRecommendation: async (recommendationData) => { const response = await fetch(buildUrl('/api/recommendations/bookmark'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify(recommendationData) }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '标记推荐失败' })); throw new Error(error.detail || '标记推荐失败'); } return response.json(); }, getActiveRecommendations: async () => { const response = await fetch(buildUrl('/api/recommendations/active'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取有效推荐失败' })); throw new Error(error.detail || '获取有效推荐失败'); } return response.json(); }, getRecommendation: async (id) => { const response = await fetch(buildUrl(`/api/recommendations/${id}`), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取推荐详情失败' })); throw new Error(error.detail || '获取推荐详情失败'); } return response.json(); }, generateRecommendations: async (minSignalStrength = 5, maxRecommendations = 20) => { const response = await fetch( buildUrl(`/api/recommendations/generate?min_signal_strength=${minSignalStrength}&max_recommendations=${maxRecommendations}`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), } ); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '生成推荐失败' })); throw new Error(error.detail || '生成推荐失败'); } return response.json(); }, markRecommendationExecuted: async (id, tradeId = null) => { const response = await fetch(buildUrl(`/api/recommendations/${id}/execute`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ trade_id: tradeId }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '标记执行失败' })); throw new Error(error.detail || '标记执行失败'); } return response.json(); }, cancelRecommendation: async (id, notes = null) => { const response = await fetch(buildUrl(`/api/recommendations/${id}/cancel`), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ notes }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '取消推荐失败' })); throw new Error(error.detail || '取消推荐失败'); } return response.json(); }, // 系统控制(supervisor) clearSystemCache: async () => { const response = await fetch(buildUrl('/api/system/clear-cache'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '清理缓存失败' })); throw new Error(error.detail || '清理缓存失败'); } return response.json(); }, getTradingSystemStatus: async () => { const response = await fetch(buildUrl('/api/system/trading/status'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取交易系统状态失败' })); throw new Error(error.detail || '获取交易系统状态失败'); } return response.json(); }, startTradingSystem: async () => { const response = await fetch(buildUrl('/api/system/trading/start'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '启动交易系统失败' })); throw new Error(error.detail || '启动交易系统失败'); } return response.json(); }, stopTradingSystem: async () => { const response = await fetch(buildUrl('/api/system/trading/stop'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '停止交易系统失败' })); throw new Error(error.detail || '停止交易系统失败'); } return response.json(); }, restartTradingSystem: async () => { const response = await fetch(buildUrl('/api/system/trading/restart'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '重启交易系统失败' })); throw new Error(error.detail || '重启交易系统失败'); } return response.json(); }, restartAllTradingSystems: async (opts = {}) => { const params = new URLSearchParams() if (opts?.prefix) params.set('prefix', String(opts.prefix)) if (opts?.include_default) params.set('include_default', 'true') if (opts?.do_update === false) params.set('do_update', 'false') const url = params.toString() ? `${buildUrl('/api/system/trading/restart-all')}?${params.toString()}` : buildUrl('/api/system/trading/restart-all') const response = await fetch(url, { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }) if (!response.ok) { const error = await response.json().catch(() => ({ detail: '批量重启失败' })) throw new Error(error.detail || '批量重启失败') } return response.json() }, // 后端控制(uvicorn) getBackendStatus: async () => { const response = await fetch(buildUrl('/api/system/backend/status'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取后端状态失败' })); throw new Error(error.detail || '获取后端状态失败'); } return response.json(); }, restartBackend: async () => { const response = await fetch(buildUrl('/api/system/backend/restart'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '重启后端失败' })); throw new Error(error.detail || '重启后端失败'); } return response.json(); }, // 日志监控(Redis List) getSystemLogs: async (params = {}) => { const query = new URLSearchParams(params).toString(); const url = query ? `${buildUrl('/api/system/logs')}?${query}` : buildUrl('/api/system/logs'); const response = await fetch(url, { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取日志失败' })); throw new Error(error.detail || '获取日志失败'); } return response.json(); }, getLogsOverview: async () => { const response = await fetch(buildUrl('/api/system/logs/overview'), { headers: withAccountHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取日志概览失败' })); throw new Error(error.detail || '获取日志概览失败'); } return response.json(); }, updateLogsConfig: async (data) => { const response = await fetch(buildUrl('/api/system/logs/config'), { method: 'PUT', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify(data || {}), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '更新日志配置失败' })); throw new Error(error.detail || '更新日志配置失败'); } return response.json(); }, writeLogsTest: async () => { const response = await fetch(buildUrl('/api/system/logs/test-write'), { method: 'POST', headers: withAccountHeaders({ 'Content-Type': 'application/json' }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '写入测试日志失败' })); throw new Error(error.detail || '写入测试日志失败'); } return response.json(); }, // 管理员:获取用户列表 getUsers: async () => { const response = await fetch(buildUrl('/api/admin/users'), { headers: withAuthHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取用户列表失败' })); throw new Error(error.detail || '获取用户列表失败'); } return response.json(); }, // 管理员:获取用户关联的账号列表 getUserAccounts: async (userId) => { const response = await fetch(buildUrl(`/api/admin/users/${userId}/accounts`), { headers: withAuthHeaders() }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '获取用户账号列表失败' })); throw new Error(error.detail || '获取用户账号列表失败'); } return response.json(); }, // 管理员:创建用户 createUser: async (data) => { const response = await fetch(buildUrl('/api/admin/users'), { method: 'POST', headers: withAuthHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify(data), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '创建用户失败' })); throw new Error(error.detail || '创建用户失败'); } return response.json(); }, // 管理员:更新用户密码 updateUserPassword: async (userId, password) => { const response = await fetch(buildUrl(`/api/admin/users/${userId}/password`), { method: 'PUT', headers: withAuthHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ password }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '更新密码失败' })); throw new Error(error.detail || '更新密码失败'); } return response.json(); }, // 管理员:更新用户角色 updateUserRole: async (userId, role) => { const response = await fetch(buildUrl(`/api/admin/users/${userId}/role`), { method: 'PUT', headers: withAuthHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ role }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '更新角色失败' })); throw new Error(error.detail || '更新角色失败'); } return response.json(); }, // 管理员:更新用户状态 updateUserStatus: async (userId, status) => { const response = await fetch(buildUrl(`/api/admin/users/${userId}/status`), { method: 'PUT', headers: withAuthHeaders({ 'Content-Type': 'application/json' }), body: JSON.stringify({ status }), }); if (!response.ok) { const error = await response.json().catch(() => ({ detail: '更新状态失败' })); throw new Error(error.detail || '更新状态失败'); } return response.json(); }, };