From fd93ce8d972498caddb7e95090eaa4a4a762de82 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 15:53:29 +0800 Subject: [PATCH] feat(platform): include alert escalation in digest --- .../apps/web/src/pages/Quality.tsx | 16 ++++++++++-- .../apps/web/src/test/App.test.tsx | 26 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Quality.tsx b/vehicle-data-platform/apps/web/src/pages/Quality.tsx index adcc7e6f..d1e4194c 100644 --- a/vehicle-data-platform/apps/web/src/pages/Quality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Quality.tsx @@ -341,10 +341,21 @@ function priorityIssueTicketText(row: PriorityIssueRow) { ].join('\n'); } -function priorityIssueDigestText(rows: PriorityIssueRow[], summary: QualitySummary, platformRelease?: string) { +function priorityDigestPolicyLines(policies?: QualityNotificationPolicy[]) { + const policy = policies?.find((item) => item.name.startsWith('P0')) ?? policies?.[0]; + if (!policy) return []; + const escalation = formatEscalationMinutes(policy.escalationMinutes); + return [ + `通知升级:${[policy.name, escalation, policy.target].filter(Boolean).join(' / ')}`, + ...(policy.acceptanceCriteria ? [`验收标准:${policy.acceptanceCriteria}`] : []) + ]; +} + +function priorityIssueDigestText(rows: PriorityIssueRow[], summary: QualitySummary, platformRelease?: string, policies?: QualityNotificationPolicy[]) { const p0Count = rows.filter((row) => row.priority === 'P0').length; const p1Count = rows.filter((row) => row.priority === 'P1').length; const release = platformRelease?.trim(); + const policyLines = priorityDigestPolicyLines(policies); const lines = rows.map((row, index) => [ `${index + 1}. [${row.priority}] ${row.vehicleLabel}`, ` 来源:${qualityProtocolLabel(row.protocol)} / 问题:${qualityIssueLabel(row.issueType)}`, @@ -356,6 +367,7 @@ function priorityIssueDigestText(rows: PriorityIssueRow[], summary: QualitySumma `问题车辆:${summary.issueVehicleCount.toLocaleString()},问题记录:${summary.issueRecordCount.toLocaleString()}`, `P0:${p0Count.toLocaleString()} 条,P1:${p1Count.toLocaleString()} 条`, ...(release ? [`运行版本:${release}`] : []), + ...policyLines, '', ...lines, '', @@ -615,7 +627,7 @@ export function Quality({ Toast.warning('当前没有可复制的优先告警'); return; } - copyText(priorityIssueDigestText(priorityRows, notificationPlan?.summary ?? summary, health?.runtime?.platformRelease), '优先队列通知汇总'); + copyText(priorityIssueDigestText(priorityRows, notificationPlan?.summary ?? summary, health?.runtime?.platformRelease, policies), '优先队列通知汇总'); }; return ( diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index 467f30bd..1b088eba 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -524,6 +524,30 @@ test('dashboard exposes vehicle command center map actions', async () => { }) } as Response; } + if (path.includes('/api/quality/notification-plan')) { + return { + ok: true, + json: async () => ({ + data: { + summary: { + issueVehicleCount: 2, + issueRecordCount: 2, + errorCount: 1, + warningCount: 1, + protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }, { name: 'JT808', count: 1 }], + issueTypes: [{ name: 'NO_SOURCE', count: 1 }, { name: 'VIN_MISSING', count: 1 }] + }, + rules: [], + policies: [{ name: 'P0 实时中断', target: '接入运维 + 业务责任人', channel: '站内告警 / 邮件 / 企业微信', condition: '无来源、VIN 缺失、存储不可写', escalationMinutes: 30, acceptanceCriteria: '来源恢复并持续 10 分钟,车辆服务可查到实时与历史证据' }], + priorityIssues: [], + activeRuleCount: 1, + p0RuleCount: 1 + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } if (path.includes('/api/quality/issues')) { return { ok: true, @@ -3419,6 +3443,8 @@ test('copies priority queue notification digest on quality page', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警优先队列汇总】')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本:platform-20260704153951')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知升级:P0 实时中断 / 30 分钟升级 / 接入运维 + 业务责任人')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准:来源恢复并持续 10 分钟,车辆服务可查到实时与历史证据')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0:1 条,P1:1 条')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 粤A汇总1 / VIN-DIGEST-001')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('确认平台转发'));