From 259601ed7bbe68e41c01cc5a55d04291d10ae4a1 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 19:57:33 +0800 Subject: [PATCH] feat(platform): add operations handoff summary --- .../apps/web/src/pages/Dashboard.tsx | 29 +++++++++ .../apps/web/src/test/App.test.tsx | 59 +++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index 33088ebe..d503e5c4 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -489,6 +489,34 @@ export function Dashboard({ ] } ]; + const copyOperationsHandoff = () => { + const unhealthyLinks = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok'); + const priorityAction = serviceActionQueue[0]; + const highPriorityLookupKey = highPriorityLookup?.key; + const highPriorityFilters = highPriorityEvidenceFilters; + const lines = [ + '【车辆数据中台运营交接摘要】', + `在线车辆:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} / ${formatCount(serviceSummary?.totalVehicles)}`, + `今日活跃:${formatCount(summary?.activeToday)}`, + `今日帧量:${formatCount(summary?.frameToday)}`, + `实时地图有效定位:${commandLocatedCount.toLocaleString()}`, + `告警车辆:${formatCount(summary?.issueVehicles)}`, + `Kafka Lag:${formatLag(summary?.kafkaLag)}`, + `高德地图:${amapConfigured ? '已配置' : '待配置'}`, + `链路健康:${unhealthyLinks.length > 0 ? unhealthyLinks.map((item) => `${item.name}=${item.status}`).join(';') : '正常'}`, + `优先动作:${priorityAction ? `${priorityAction.label} ${priorityAction.count.toLocaleString()}辆 - ${priorityAction.detail}` : '暂无待办'}`, + highPriorityIssue ? `最高告警:${highPriorityIssue.severity === 'error' ? 'P0' : 'P1'} ${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)} / ${highPriorityIssue.lastSeen || '-'}` : '最高告警:暂无', + highPriorityIssue ? `告警详情:${highPriorityIssue.detail || '-'}` : '', + `实时监控:${appURL(buildAppHash({ page: 'realtime', filters: { online: 'online' } }))}`, + `实时地图:${appURL(buildAppHash({ page: 'map', filters: { online: 'online' } }))}`, + `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: highPriorityFilters?.keyword, protocol: highPriorityFilters?.protocol, filters: highPriorityFilters }))}`, + `历史RAW:${appURL(buildAppHash({ page: 'history-query', keyword: highPriorityFilters?.keyword, protocol: highPriorityFilters?.protocol, filters: { ...highPriorityFilters, tab: 'raw', includeFields: 'true' } }))}`, + `告警事件:${appURL(buildAppHash({ page: 'alert-events', filters: highPriorityIssue?.issueType ? { issueType: highPriorityIssue.issueType } : {} }))}`, + `统计查询:${appURL(buildAppHash({ page: 'mileage', keyword: highPriorityLookupKey, protocol: highPriorityIssue?.protocol }))}`, + `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: highPriorityLookupKey, protocol: highPriorityIssue?.protocol }))}` + ].filter(Boolean); + copyText(lines.join('\n'), '运营交接摘要'); + }; return (
@@ -515,6 +543,7 @@ export function Dashboard({ 0 ? 'orange' : 'green'}>Kafka Lag {formatLag(summary?.kafkaLag)} + 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 d3e77b64..2f98254a 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -40,6 +40,48 @@ test('exposes AMap operations shortcuts when map key is configured', async () => }) } as Response; } + if (path.includes('/api/dashboard/summary')) { + return { + ok: true, + json: async () => ({ + data: { + onlineVehicles: 0, + activeToday: 0, + frameToday: 0, + issueVehicles: 0, + kafkaLag: 0, + protocols: [], + serviceStatuses: [], + linkHealth: [] + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } + if (path.includes('/api/vehicle-service/summary')) { + return { + ok: true, + json: async () => ({ + data: { + totalVehicles: 0, + boundVehicles: 0, + onlineVehicles: 0, + singleSourceVehicles: 0, + multiSourceVehicles: 0, + noDataVehicles: 0, + identityRequiredVehicles: 0, + archiveIncompleteVehicles: 0, + serviceStatuses: [], + protocols: [], + missingSources: [], + archiveMissingFields: [] + }, + traceId: 'trace-test', + timestamp: 1783094400000 + }) + } as Response; + } if (path.includes('/api/mileage/summary')) { return { ok: true, @@ -1170,6 +1212,23 @@ test('dashboard copies highest priority quality issue notification text', async expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情:JT808 数据缺少 VIN 映射')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据:http://localhost:3000/#/history?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('RAW证据:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true')); + + fireEvent.click(screen.getByRole('button', { name: '复制运营交接摘要' })); + + await waitFor(() => { + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台运营交接摘要】')); + }); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆:88 / 1,033')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日活跃:96')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日帧量:1,286,320')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('Kafka Lag:0')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最高告警:P0 VIN 缺失 / 粤A告警1 / 13307795425 / 2026-07-03 20:12:10')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时监控:http://localhost:3000/#/realtime?online=online')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时地图:http://localhost:3000/#/map?online=online')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放:http://localhost:3000/#/history?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史RAW:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警事件:http://localhost:3000/#/alert-events?issueType=VIN_MISSING')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808')); }); test('dashboard keeps core vehicle service metrics when preview requests fail', async () => {