97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
'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>
|
|
)
|
|
} |