This commit is contained in:
薇薇安 2026-02-01 22:30:53 +08:00
parent ce3a4953f5
commit 0a4bbd3132
3 changed files with 76 additions and 9 deletions

View File

@ -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),

View File

@ -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() {
<div className="nav-container">
<div className="nav-left">
<h1 className="nav-title">自动交易系统</h1>
<AccountSelector />
{!isAdmin && <AccountSelector />}
</div>
<div className="nav-links">
<Link to="/">仪表板</Link>
<Link to="/recommendations">交易推荐</Link>
<Link to="/config">配置</Link>
<Link to="/trades">交易记录</Link>
{isAdmin ? (
{!isAdmin && (
<>
<Link to="/recommendations">交易推荐</Link>
<Link to="/config">配置</Link>
<Link to="/trades">交易记录</Link>
</>
)}
{isAdmin && (
<>
<Link to="/global-config">全局配置</Link>
<Link to="/logs">日志监控</Link>
</>
) : null}
)}
</div>
<div className="nav-user">
<span className="nav-user-name">
@ -104,7 +109,7 @@ function App() {
<main className="main-content">
<Routes>
<Route path="/" element={<StatsDashboard />} />
<Route path="/" element={isAdmin ? <AdminDashboard /> : <StatsDashboard />} />
<Route path="/recommendations" element={<Recommendations />} />
<Route path="/config" element={<ConfigPanel />} />
<Route path="/config/guide" element={<ConfigGuide />} />

View File

@ -376,6 +376,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) => {
const response = await fetch(buildUrl(`/api/account/positions/${symbol}/close`), {