feat:初版
This commit is contained in:
97
apps/web/app/admin/stats/page.tsx
Normal file
97
apps/web/app/admin/stats/page.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000'
|
||||
|
||||
interface Stats {
|
||||
date: string
|
||||
request_count: number
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
cached_count: number
|
||||
error_count: number
|
||||
}
|
||||
|
||||
interface Realtime {
|
||||
rpm: number
|
||||
tpm: number
|
||||
}
|
||||
|
||||
export default function StatsPage() {
|
||||
const [providerId, setProviderId] = useState(1)
|
||||
const [stats, setStats] = useState<Stats | null>(null)
|
||||
const [realtime, setRealtime] = useState<Realtime>({ rpm: 0, tpm: 0 })
|
||||
|
||||
const getToken = () => localStorage.getItem('admin_token') || ''
|
||||
|
||||
const fetchStats = async () => {
|
||||
const res = await fetch(
|
||||
`${API_BASE}/api/v1/admin/stats/daily/${providerId}`,
|
||||
{ headers: { Authorization: `Bearer ${getToken()}` } }
|
||||
)
|
||||
if (res.ok) setStats(await res.json())
|
||||
}
|
||||
|
||||
const fetchRealtime = async () => {
|
||||
const res = await fetch(
|
||||
`${API_BASE}/api/v1/admin/stats/realtime/${providerId}`,
|
||||
{ headers: { Authorization: `Bearer ${getToken()}` } }
|
||||
)
|
||||
if (res.ok) setRealtime(await res.json())
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
fetchStats()
|
||||
fetchRealtime()
|
||||
const interval = setInterval(fetchRealtime, 5000)
|
||||
return () => clearInterval(interval)
|
||||
}, [providerId])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold mb-6">使用统计</h1>
|
||||
|
||||
{/* 实时指标 */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div className="bg-white p-4 rounded shadow">
|
||||
<h3 className="text-gray-500 text-sm">RPM (每分钟请求)</h3>
|
||||
<p className="text-2xl font-bold">{realtime.rpm}</p>
|
||||
</div>
|
||||
<div className="bg-white p-4 rounded shadow">
|
||||
<h3 className="text-gray-500 text-sm">TPM (每分钟Token)</h3>
|
||||
<p className="text-2xl font-bold">{realtime.tpm}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 今日统计 */}
|
||||
{stats && (
|
||||
<div className="bg-white p-6 rounded shadow">
|
||||
<h2 className="text-lg font-bold mb-4">今日统计 ({stats.date})</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">总请求</p>
|
||||
<p className="text-xl font-bold">{stats.request_count}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">输入 Token</p>
|
||||
<p className="text-xl font-bold">{stats.input_tokens}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">输出 Token</p>
|
||||
<p className="text-xl font-bold">{stats.output_tokens}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">缓存命中</p>
|
||||
<p className="text-xl font-bold">{stats.cached_count}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-gray-500 text-sm">错误数</p>
|
||||
<p className="text-xl font-bold text-red-600">{stats.error_count}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user