a
This commit is contained in:
parent
ce3a4953f5
commit
0a4bbd3132
|
|
@ -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))
|
||||||
sys.path.insert(0, str(project_root / 'backend'))
|
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 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__)
|
logger = logging.getLogger(__name__)
|
||||||
router = APIRouter()
|
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")
|
@router.get("/performance")
|
||||||
async def get_performance_stats(
|
async def get_performance_stats(
|
||||||
days: int = Query(7, ge=1, le=365),
|
days: int = Query(7, ge=1, le=365),
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import ConfigPanel from './components/ConfigPanel'
|
||||||
import ConfigGuide from './components/ConfigGuide'
|
import ConfigGuide from './components/ConfigGuide'
|
||||||
import TradeList from './components/TradeList'
|
import TradeList from './components/TradeList'
|
||||||
import StatsDashboard from './components/StatsDashboard'
|
import StatsDashboard from './components/StatsDashboard'
|
||||||
|
import AdminDashboard from './components/AdminDashboard'
|
||||||
import Recommendations from './components/Recommendations'
|
import Recommendations from './components/Recommendations'
|
||||||
import LogMonitor from './components/LogMonitor'
|
import LogMonitor from './components/LogMonitor'
|
||||||
import AccountSelector from './components/AccountSelector'
|
import AccountSelector from './components/AccountSelector'
|
||||||
|
|
@ -67,19 +68,23 @@ function App() {
|
||||||
<div className="nav-container">
|
<div className="nav-container">
|
||||||
<div className="nav-left">
|
<div className="nav-left">
|
||||||
<h1 className="nav-title">自动交易系统</h1>
|
<h1 className="nav-title">自动交易系统</h1>
|
||||||
<AccountSelector />
|
{!isAdmin && <AccountSelector />}
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-links">
|
<div className="nav-links">
|
||||||
<Link to="/">仪表板</Link>
|
<Link to="/">仪表板</Link>
|
||||||
<Link to="/recommendations">交易推荐</Link>
|
{!isAdmin && (
|
||||||
<Link to="/config">配置</Link>
|
<>
|
||||||
<Link to="/trades">交易记录</Link>
|
<Link to="/recommendations">交易推荐</Link>
|
||||||
{isAdmin ? (
|
<Link to="/config">配置</Link>
|
||||||
|
<Link to="/trades">交易记录</Link>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isAdmin && (
|
||||||
<>
|
<>
|
||||||
<Link to="/global-config">全局配置</Link>
|
<Link to="/global-config">全局配置</Link>
|
||||||
<Link to="/logs">日志监控</Link>
|
<Link to="/logs">日志监控</Link>
|
||||||
</>
|
</>
|
||||||
) : null}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="nav-user">
|
<div className="nav-user">
|
||||||
<span className="nav-user-name">
|
<span className="nav-user-name">
|
||||||
|
|
@ -104,7 +109,7 @@ function App() {
|
||||||
|
|
||||||
<main className="main-content">
|
<main className="main-content">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<StatsDashboard />} />
|
<Route path="/" element={isAdmin ? <AdminDashboard /> : <StatsDashboard />} />
|
||||||
<Route path="/recommendations" element={<Recommendations />} />
|
<Route path="/recommendations" element={<Recommendations />} />
|
||||||
<Route path="/config" element={<ConfigPanel />} />
|
<Route path="/config" element={<ConfigPanel />} />
|
||||||
<Route path="/config/guide" element={<ConfigGuide />} />
|
<Route path="/config/guide" element={<ConfigGuide />} />
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,15 @@ export const api = {
|
||||||
}
|
}
|
||||||
return response.json();
|
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) => {
|
closePosition: async (symbol) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user