增加性能统计

This commit is contained in:
2026-04-15 11:46:42 +08:00
parent 20ae332706
commit 6bb80d7c8c
5 changed files with 343 additions and 20 deletions
+92 -3
View File
@@ -2,8 +2,9 @@ import Link from "next/link";
import { ArrowUpRight, Clock3, Smartphone, Sparkles } from "lucide-react";
import { DashboardCharts } from "@/components/admin/dashboard-charts";
import type { DashboardData } from "@/lib/analytics/contracts";
import { getDashboardData, parseDashboardQuery } from "@/lib/analytics/server";
import { formatDateTime, formatNumber } from "@/lib/utils";
import { formatDateTime, formatDurationMs, formatNumber } from "@/lib/utils";
export const dynamic = "force-dynamic";
@@ -14,6 +15,41 @@ const ranges = [
{ label: "90 天", value: "90" },
] as const;
type RecentEventItem = DashboardData["recentEvents"][number];
type ConversationItem = DashboardData["recentConversations"][number];
function describeConversationMode(mode: string | null) {
if (mode === "clone") {
return "专属音色模式";
}
if (mode === "standard") {
return "标准模式";
}
return null;
}
function buildEventNote(event: RecentEventItem) {
const notes = [
event.durationMs && event.durationMs > 0 ? `耗时 ${formatDurationMs(event.durationMs)}` : null,
event.value?.trim() || null,
event.elementText?.trim() || null,
].filter(Boolean);
return notes[0] ?? "无额外说明";
}
function buildConversationNote(item: ConversationItem) {
const notes = [
item.turnIndex ? `第 ${item.turnIndex} 轮` : null,
describeConversationMode(item.mode),
item.durationMs && item.durationMs > 0
? `${item.role === "用户发言" ? "识别完成" : "文本完成"} ${formatDurationMs(item.durationMs)}`
: null,
].filter(Boolean);
return notes.join(" · ");
}
type AdminPageProps = {
searchParams: Promise<{
range?: string;
@@ -44,7 +80,7 @@ export default async function AdminPage({ searchParams }: AdminPageProps) {
</h1>
</div>
<p className="max-w-3xl text-base leading-8 text-[var(--ink-2)]">
这里持续汇总老年陪伴助手在 Android 应用中的使用情况,方便你查看哪些功能最常被打开、哪些设备最近还在使用,以及哪里出现了中断或失败。
这里持续汇总老年陪伴助手在 Android 应用中的使用情况,除了常用功能和异常提醒,也会单独整理回复耗时、连接速度和最近对话片段,方便你直接判断体验是不是顺畅。
</p>
</div>
@@ -93,6 +129,27 @@ export default async function AdminPage({ searchParams }: AdminPageProps) {
<DashboardCharts activityTrend={data.activityTrend} topEvents={data.topEvents} />
<section className="soft-card p-6">
<div className="mb-5 flex items-center justify-between gap-4">
<div>
<p className="eyebrow">性能概览</p>
<h2 className="text-2xl font-semibold text-[var(--ink-1)]">陪伴流程现在快不快</h2>
</div>
<p className="text-sm text-[var(--ink-3)]">只看真实耗时,不看空洞的成功失败计数</p>
</div>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
{data.performanceMetrics.map((metric) => (
<article key={metric.label} className="rounded-[24px] border border-[var(--line-soft)] bg-white/80 px-5 py-5 shadow-[var(--shadow-card)]">
<p className="mb-2 text-sm text-[var(--ink-3)]">{metric.label}</p>
<strong className="text-3xl font-semibold text-[var(--ink-1)]">{formatDurationMs(metric.valueMs)}</strong>
<p className="mt-3 text-sm leading-7 text-[var(--ink-2)]">{metric.hint}</p>
<p className="mt-2 text-xs text-[var(--ink-3)]">{metric.sampleCount > 0 ? `基于 ${formatNumber(metric.sampleCount)} 次记录` : "暂无样本"}</p>
</article>
))}
</div>
</section>
<section className="grid gap-6 xl:grid-cols-[1.05fr_0.95fr]">
<article className="soft-card p-6">
<div className="mb-5 flex items-center justify-between gap-4">
@@ -155,6 +212,38 @@ export default async function AdminPage({ searchParams }: AdminPageProps) {
</article>
</section>
<section className="soft-card p-6">
<div className="mb-5 flex items-center justify-between gap-4">
<div>
<p className="eyebrow">最近对话</p>
<h2 className="text-2xl font-semibold text-[var(--ink-1)]">刚刚聊了些什么</h2>
</div>
<p className="text-sm text-[var(--ink-3)]">聊天记录会自动上传,这里只展示最新片段</p>
</div>
<div className="grid gap-4 lg:grid-cols-2">
{data.recentConversations.length > 0 ? (
data.recentConversations.map((item) => (
<article key={item.id} className="rounded-[24px] border border-[var(--line-soft)] bg-white/80 px-5 py-5 shadow-[var(--shadow-card)]">
<div className="mb-3 flex items-center justify-between gap-3">
<span className="inline-flex rounded-full bg-[var(--surface-2)] px-3 py-1 text-sm font-medium text-[var(--ink-1)]">
{item.role}
</span>
<span className="text-xs text-[var(--ink-3)]">{formatDateTime(item.occurredAt)}</span>
</div>
<p className="text-base leading-8 text-[var(--ink-1)]">{item.text}</p>
<p className="mt-3 text-sm text-[var(--ink-3)]">{buildConversationNote(item) || "已自动归档到聊天记录"}</p>
<p className="mt-1 text-xs text-[var(--ink-3)]">设备会话 {item.sessionKey.slice(0, 18)}...</p>
</article>
))
) : (
<div className="rounded-[24px] border border-dashed border-[var(--line-soft)] bg-white/60 px-5 py-8 text-sm text-[var(--ink-3)] lg:col-span-2">
当前时间范围内还没有自动上传的聊天片段。
</div>
)}
</div>
</section>
<section className="soft-card p-6">
<div className="mb-5 flex items-center justify-between gap-4">
<div>
@@ -189,7 +278,7 @@ export default async function AdminPage({ searchParams }: AdminPageProps) {
<p className="mt-1 text-xs text-[var(--ink-3)]">{event.component ?? "无额外标签"}</p>
</td>
<td className="rounded-r-[20px] px-4 py-4">
<p>{event.elementText ?? "无额外说明"}</p>
<p>{buildEventNote(event)}</p>
<p className="mt-1 text-xs text-[var(--ink-3)]">设备会话 {event.sessionKey.slice(0, 18)}...</p>
</td>
</tr>