diff --git a/backend/api/routes/stats.py b/backend/api/routes/stats.py index 5e462a6..a8d3276 100644 --- a/backend/api/routes/stats.py +++ b/backend/api/routes/stats.py @@ -11,14 +11,67 @@ project_root = Path(__file__).parent.parent.parent.parent sys.path.insert(0, str(project_root)) sys.path.insert(0, str(project_root / 'backend')) -from database.models import AccountSnapshot, Trade, MarketScan, TradingSignal +from database.models import AccountSnapshot, Trade, MarketScan, TradingSignal, Account from fastapi import HTTPException -from api.auth_deps import get_account_id +from api.auth_deps import get_account_id, get_admin_user +from typing import Dict, Any logger = logging.getLogger(__name__) router = APIRouter() +@router.get("/admin/dashboard") +async def get_admin_dashboard_stats(user: Dict[str, Any] = Depends(get_admin_user)): + """获取管理员仪表板数据(所有用户统计)""" + try: + accounts = Account.list_all() + stats = [] + + total_assets = 0 + total_pnl = 0 + active_accounts = 0 + + for acc in accounts: + aid = acc['id'] + # 获取最新快照 + snapshots = AccountSnapshot.get_recent(1, account_id=aid) + acc_stat = { + "id": aid, + "name": acc['name'], + "status": acc['status'], + "total_balance": 0, + "total_pnl": 0, + "open_positions": 0 + } + + if snapshots: + snap = snapshots[0] + acc_stat["total_balance"] = snap.get('total_balance', 0) + acc_stat["total_pnl"] = snap.get('total_pnl', 0) + acc_stat["open_positions"] = snap.get('open_positions', 0) + + total_assets += float(acc_stat["total_balance"]) + total_pnl += float(acc_stat["total_pnl"]) + + if acc['status'] == 'active': + active_accounts += 1 + + stats.append(acc_stat) + + return { + "summary": { + "total_accounts": len(accounts), + "active_accounts": active_accounts, + "total_assets_usdt": total_assets, + "total_pnl_usdt": total_pnl + }, + "accounts": stats + } + except Exception as e: + logger.error(f"获取管理员仪表板数据失败: {e}", exc_info=True) + raise HTTPException(status_code=500, detail=str(e)) + + @router.get("/performance") async def get_performance_stats( days: int = Query(7, ge=1, le=365), diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 294b4fa..9eb8985 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -5,6 +5,7 @@ import ConfigPanel from './components/ConfigPanel' import ConfigGuide from './components/ConfigGuide' import TradeList from './components/TradeList' import StatsDashboard from './components/StatsDashboard' +import AdminDashboard from './components/AdminDashboard' import Recommendations from './components/Recommendations' import LogMonitor from './components/LogMonitor' import AccountSelector from './components/AccountSelector' @@ -67,19 +68,23 @@ function App() {

自动交易系统

- + {!isAdmin && }
仪表板 - 交易推荐 - 配置 - 交易记录 - {isAdmin ? ( + {!isAdmin && ( + <> + 交易推荐 + 配置 + 交易记录 + + )} + {isAdmin && ( <> 全局配置 日志监控 - ) : null} + )}
@@ -104,7 +109,7 @@ function App() {
- } /> + : } /> } /> } /> } /> diff --git a/frontend/src/services/api.js b/frontend/src/services/api.js index a6a6bf1..9d5765f 100644 --- a/frontend/src/services/api.js +++ b/frontend/src/services/api.js @@ -375,6 +375,15 @@ export const api = { } return response.json(); }, + + getAdminDashboard: async () => { + const response = await fetch(buildUrl('/api/stats/admin/dashboard'), { headers: withAuthHeaders() }); + if (!response.ok) { + const error = await response.json().catch(() => ({ detail: '获取管理员仪表板数据失败' })); + throw new Error(error.detail || '获取管理员仪表板数据失败'); + } + return response.json(); + }, // 平仓操作 closePosition: async (symbol) => {