From 358bb642f48cce788721dff7d9f02ac3774f60be Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 5 Jul 2026 11:54:23 +0800 Subject: [PATCH] feat(platform): position dashboard as vehicle service center --- .../apps/web/src/pages/Dashboard.tsx | 36 +++++++++---------- .../apps/web/src/test/App.test.tsx | 18 +++++----- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index ff37be61..6a2560ca 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -525,7 +525,7 @@ export function Dashboard({ { title: '按时间窗复盘', value: taskTimeMonitorSummary, - detail: '同一时间窗贯穿轨迹、里程、RAW 和告警,不需要跨页面重复输入。', + detail: '同一时间窗贯穿轨迹、里程、原始记录和告警,不需要跨页面重复输入。', color: 'blue' as const, primaryAction: '轨迹回放', secondaryAction: '里程统计', @@ -871,7 +871,7 @@ export function Dashboard({ { label: '数据可导出', value: `${formatCount(summary?.frameToday)} 今日数据`, - detail: '历史查询默认带 RAW 与解析字段,支持按字段缩小导出体积。', + detail: '历史查询默认带原始记录与解析字段,支持按字段缩小导出体积。', color: (summary?.frameToday ?? 0) > 0 ? 'blue' as const : 'orange' as const }, { @@ -894,7 +894,7 @@ export function Dashboard({ `时间范围:${scope.dateFrom || '-'} 至 ${scope.dateTo || '-'}`, `轨迹证据:今日活跃 ${formatCount(summary?.activeToday)},用于回放位置、速度、里程断点`, `统计证据:车辆口径 ${formatCount(serviceSummary?.totalVehicles)},用于区间里程和日统计闭合`, - `历史证据:今日数据 ${formatCount(summary?.frameToday)},用于 RAW 与解析字段复核`, + `历史证据:今日数据 ${formatCount(summary?.frameToday)},用于原始记录与解析字段复核`, `告警事件:${formatCount(summary?.issueVehicles)} 辆车存在告警`, '交付核对:', ...timeMonitorChecklist.map((item, index) => `${index + 1}. ${item.label}:${item.value};${item.detail}`), @@ -926,7 +926,7 @@ export function Dashboard({ { label: '历史证据', value: `${formatCount(summary?.frameToday)} 帧`, - detail: '按车辆、时间和字段裁剪导出 RAW 与解析字段。', + detail: '按车辆、时间和字段裁剪导出原始记录与解析字段。', action: '导出证据', color: 'blue' as const, onClick: openTimeMonitorRaw @@ -943,10 +943,10 @@ export function Dashboard({ const dataFlowStages = [ { stage: '01', - title: '协议接入', - owner: '网关接收', + title: '车辆接入', + owner: '接入网关', evidence: `32960 / 808 / MQTT,连接 ${formatCount(opsHealth?.activeConnections)}`, - detail: '只接收真实实时数据帧和必要鉴权注册帧,协议不是最终产品。', + detail: '只接收真实实时数据帧和必要鉴权注册帧,接入来源最终归并到车辆服务。', action: '运维质量', onClick: () => onOpenQuality() }, @@ -981,8 +981,8 @@ export function Dashboard({ stage: '05', title: '历史证据', owner: 'TDengine / MySQL', - evidence: `轨迹 ${formatCount(summary?.activeToday)} 活跃,RAW ${formatCount(summary?.frameToday)} 帧`, - detail: '位置、RAW 和解析字段可分页查询,并能回跳轨迹、车辆服务和告警。', + evidence: `轨迹 ${formatCount(summary?.activeToday)} 活跃,原始记录 ${formatCount(summary?.frameToday)} 帧`, + detail: '位置、原始记录和解析字段可分页查询,并能回跳轨迹、车辆服务和告警。', action: '轨迹回放', onClick: () => onOpenHistory() }, @@ -999,7 +999,7 @@ export function Dashboard({ const scenarioNavigation = [ { title: '实时监控', - objective: '确认车辆是否在线、位置是否可信、三类来源是否齐全。', + objective: '确认车辆是否在线、位置是否可信、必要接入来源是否齐全。', evidence: `Redis 在线态 / 最新定位 / 有效坐标 ${commandLocatedCount.toLocaleString()}`, sla: '目标 0-1 秒内进入实时视图', primaryAction: '实时监控', @@ -1054,7 +1054,7 @@ export function Dashboard({ const highPriorityLookupKey = highPriorityLookup?.key; const highPriorityFilters = highPriorityEvidenceFilters; const lines = [ - '【车辆数据中台运营交接摘要】', + '【车辆服务中台运营交接摘要】', `在线车辆:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)} / ${formatCount(serviceSummary?.totalVehicles)}`, `今日活跃:${formatCount(summary?.activeToday)}`, `今日帧量:${formatCount(summary?.frameToday)}`, @@ -1076,8 +1076,8 @@ export function Dashboard({ }; const copyScenarioBlueprint = () => { const lines = [ - '【车辆数据中台功能蓝图】', - '定位:32960 / 808 / 宇通 MQTT 都只是车辆服务证据源,最终围绕一辆车提供实时、轨迹、历史、告警、统计能力。', + '【车辆服务中台功能蓝图】', + '定位:32960 / 808 / 宇通 MQTT 都只是车辆接入来源,最终围绕一辆车提供实时、轨迹、历史、告警、统计能力。', `高德地图:Web JS ${amapConfigured ? '已配置' : '待配置'};服务端 API ${amapApiConfigured ? '已配置' : '待配置'};安全代理 ${amapSecurityProxyEnabled ? '已启用' : '未启用'};安全码${amapSecurityCodeExposed ? '已暴露' : '未暴露'}`, `车辆规模:${formatCount(serviceSummary?.totalVehicles)};在线:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)};告警:${formatCount(summary?.issueVehicles)}`, '', @@ -1094,8 +1094,8 @@ export function Dashboard({ }; const copyDataFlowBlueprint = () => { const lines = [ - '【车辆数据中台数据流转图】', - '原则:32960 / 808 / 宇通 MQTT 是接入证据,最终都归并为同一辆车的车辆服务。', + '【车辆服务中台数据流转图】', + '原则:32960 / 808 / 宇通 MQTT 是车辆接入来源,最终都归并为同一辆车的车辆服务。', `运行态:连接 ${formatCount(opsHealth?.activeConnections)};在线 Key ${formatCount(opsHealth?.redisOnlineKeys)};Kafka Lag ${formatLag(summary?.kafkaLag)};今日帧 ${formatCount(summary?.frameToday)}`, '', ...dataFlowStages.map((item) => [ @@ -1272,7 +1272,7 @@ export function Dashboard({ { title: '复盘时间', value: dayRangeText(timeMonitorScopeValue.dateFrom, timeMonitorScopeValue.dateTo), - detail: '同一个时间窗贯穿轨迹回放、里程统计、RAW 证据和告警复盘。', + detail: '同一个时间窗贯穿轨迹回放、里程统计、原始记录和告警复盘。', action: '轨迹回放', color: 'blue' as const, onClick: openTimeMonitorHistory @@ -1299,7 +1299,7 @@ export function Dashboard({ const rawFilters = { ...scope, tab: 'raw', includeFields: 'true' }; const lines = [ '【车辆客户服务说明】', - '服务对象:车辆,不是协议数据源。', + '服务对象:车辆,不是接入来源。', `车辆规模:${formatCount(serviceSummary?.totalVehicles)},在线车辆:${formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles)},有效定位:${commandLocatedCount.toLocaleString()}`, `当前时间窗:${timeMonitorSummary}(${dayRangeText(scope.dateFrom, scope.dateTo)})`, `告警车辆:${formatCount(summary?.issueVehicles)},最高优先级:${highPriorityIssue ? `${qualityIssueLabel(highPriorityIssue.issueType)} / ${priorityIssueVehicleLabel(highPriorityIssue)}` : '暂无'}`, @@ -1308,7 +1308,7 @@ export function Dashboard({ '1. 找车:先按 VIN、车牌、手机号或 OEM 锁定车辆。', '2. 看位置:打开实时地图确认车辆在线、坐标和最新上报。', '3. 复盘时间:同一时间窗查看轨迹、里程和告警。', - '4. 交付数据:按车辆、时间、字段导出位置历史、RAW 和字段明细。', + '4. 交付数据:按车辆、时间、字段导出位置历史、原始记录和字段明细。', '5. 闭环异常:告警事件进入通知和处置流程。', `车辆中心:${appURL(buildAppHash({ page: 'vehicles' }))}`, `实时地图:${appURL(buildAppHash({ page: 'map', filters: { online: 'online' } }))}`, 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 db11ec67..e4e62cdc 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -891,12 +891,12 @@ test('dashboard exposes vehicle data center capability matrix', async () => { expect(screen.getAllByText('今日帧 1,286,320').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('告警 7').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('统计车辆 1,033').length).toBeGreaterThanOrEqual(1); - expect(screen.getByText('确认车辆是否在线、位置是否可信、三类来源是否齐全。')).toBeInTheDocument(); + expect(screen.getByText('确认车辆是否在线、位置是否可信、必要接入来源是否齐全。')).toBeInTheDocument(); expect(screen.getByText('按车辆复盘位置、速度、里程和断点,定位平台转发或车辆异常。')).toBeInTheDocument(); expect(screen.getByText('优先返回必要字段,避免大 JSON 拖慢查询')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '复制功能蓝图' })); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台功能蓝图】')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('32960 / 808 / 宇通 MQTT 都只是车辆服务证据源')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆服务中台功能蓝图】')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('32960 / 808 / 宇通 MQTT 都只是车辆接入来源')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆规模:1,033;在线:208;告警:7')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('高德地图:Web JS 已配置;服务端 API 已配置;安全代理 已启用;安全码未暴露')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 实时监控')); @@ -1281,8 +1281,8 @@ test('dashboard shows vehicle service action queue', async () => { test.each([ { buttonName: '查看在线车辆', expectedHash: '#/vehicles?online=online' }, - { buttonName: '实时地图', expectedHash: '#/map?online=online' }, - { buttonName: '查看告警事件', expectedHash: '#/alert-events' } + { buttonName: '打开实时地图', expectedHash: '#/map?online=online' }, + { buttonName: '顶部告警事件', expectedHash: '#/alert-events' } ])('dashboard posture opens %s', async ({ buttonName, expectedHash }) => { window.history.replaceState(null, '', '/#/dashboard'); vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => { @@ -1448,7 +1448,7 @@ test('dashboard exposes end-to-end operations workflow entries', async () => { expect(screen.getByText('今天车辆轨迹')).toBeInTheDocument(); expect(screen.getByText('历史时间窗')).toBeInTheDocument(); expect(screen.getByText('区间里程')).toBeInTheDocument(); - expect(screen.getByText('告警复盘')).toBeInTheDocument(); + expect(screen.getAllByText('告警复盘').length).toBeGreaterThanOrEqual(1); fireEvent.click(screen.getByRole('button', { name: '时间监控 今天车辆轨迹' })); @@ -1570,7 +1570,7 @@ test('dashboard surfaces highest priority quality issue with evidence shortcuts' expect(screen.getByText('实时证据')).toBeInTheDocument(); expect(screen.getAllByText('轨迹证据').length).toBeGreaterThanOrEqual(1); expect(screen.getByText('告警证据')).toBeInTheDocument(); - expect(screen.getByText('统计证据')).toBeInTheDocument(); + expect(screen.getAllByText('统计证据').length).toBeGreaterThanOrEqual(1); fireEvent.click(screen.getByRole('button', { name: '复制处置卡' })); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【重点车辆服务处置卡】')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆:粤A告警1 / 13307795425')); @@ -1578,7 +1578,7 @@ test('dashboard surfaces highest priority quality issue with evidence shortcuts' expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&tab=raw&includeFields=true')); expect(await screen.findByText('最高优先级告警')).toBeInTheDocument(); expect(screen.getAllByText('粤A告警1 / 13307795425').length).toBeGreaterThanOrEqual(1); - expect(screen.getByText('VIN 缺失')).toBeInTheDocument(); + expect(screen.getAllByText('VIN 缺失').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('JT808 数据缺少 VIN 映射').length).toBeGreaterThanOrEqual(1); fireEvent.click(screen.getByRole('button', { name: '首页告警轨迹证据' })); @@ -1713,7 +1713,7 @@ test('dashboard copies highest priority quality issue notification text', async fireEvent.click(screen.getByRole('button', { name: '复制运营交接摘要' })); await waitFor(() => { - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台运营交接摘要】')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆服务中台运营交接摘要】')); }); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆:88 / 1,033')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日活跃:96'));