From 8c35bf9f2ca1e57c15c5170dc0c9111a19e118fa Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 13:00:10 +0800 Subject: [PATCH] feat(platform): include evidence links in alert notifications --- .../apps/web/src/pages/Quality.tsx | 18 +++++++++++++++++- .../apps/web/src/test/App.test.tsx | 6 +++++- 2 files changed, 22 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 a5fdb3ec..c12db50e 100644 --- a/vehicle-data-platform/apps/web/src/pages/Quality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Quality.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'; import { api } from '../api/client'; import type { OpsHealth, QualitySummary, QualityIssueRow } from '../api/types'; import { PageHeader } from '../components/PageHeader'; +import { buildAppHash } from '../domain/appRoute'; import { qualityIssueLabel, qualityIssueOptions, qualityProtocolLabel, qualityProtocolOptions } from '../domain/qualityIssue'; import { qualityIssueVehicleLookup } from '../domain/vehicleLookup'; @@ -121,6 +122,10 @@ function qualityShareURL() { return `${window.location.origin}${window.location.pathname}${window.location.hash}`; } +function appURL(hash: string) { + return `${window.location.origin}${window.location.pathname}${hash}`; +} + function issueEvidenceDate(value?: string) { const match = String(value ?? '').match(/^(\d{4})-(\d{2})-(\d{2})/); return match ? `${match[1]}-${match[2]}-${match[3]}` : ''; @@ -237,6 +242,13 @@ function priorityIssueRows(rows: QualityIssueRow[]): PriorityIssueRow[] { } function priorityIssueNotificationText(row: PriorityIssueRow) { + const lookup = qualityIssueVehicleLookup(row); + const dateFrom = issueEvidenceDate(row.lastSeen); + const dateTo = nextDate(dateFrom); + const evidenceFilters = { + ...(dateFrom ? { dateFrom } : {}), + ...(dateTo ? { dateTo } : {}) + }; return [ `【${row.priority} 告警通知】${qualityIssueLabel(row.issueType)}`, `车辆:${row.vehicleLabel}`, @@ -246,7 +258,11 @@ function priorityIssueNotificationText(row: PriorityIssueRow) { `SLA:${row.sla}`, `最后时间:${row.lastSeen || '-'}`, `详情:${row.detail || '-'}`, - `证据入口:${qualityShareURL()}` + `实时定位:${appURL(buildAppHash({ page: 'realtime', keyword: lookup.key, protocol: row.protocol }))}`, + `轨迹证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: evidenceFilters }))}`, + `RAW证据:${appURL(buildAppHash({ page: 'history', keyword: lookup.key, protocol: row.protocol, filters: { tab: 'raw', ...evidenceFilters, includeFields: 'true' } }))}`, + `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: lookup.key, protocol: row.protocol }))}`, + `告警筛选:${qualityShareURL()}` ].join('\n'); } 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 d1874575..af881548 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -2495,7 +2495,11 @@ test('copies notification text from quality priority queue', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('SLA:2 小时修复')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最后时间:2026-07-03 20:12:10')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情:手机号未映射 VIN,需要维护 binding')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('/#/quality')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位:http://localhost:3000/#/realtime?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据:http://localhost:3000/#/history?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警筛选:http://localhost:3000/#/quality')); }); test('opens realtime map context from quality priority queue', async () => {