增加性能统计
This commit is contained in:
+92
-3
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user