diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index fd435b20..3026023b 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -34,7 +34,7 @@ function vehicleServiceStatus(row: VehicleRealtimeRow) { } function sourceEvidenceText(row: VehicleRealtimeRow) { - return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`; + return `${row.onlineSourceCount}/${row.sourceCount} 通道在线`; } function formatPercent(value: number) { @@ -154,11 +154,11 @@ const realtimeExportColumns: CsvColumn[] = [ { title: '车牌', value: (row) => row.plate }, { title: '手机号', value: (row) => row.phone }, { title: 'OEM', value: (row) => row.oem }, - { title: '主来源', value: (row) => row.primaryProtocol }, - { title: '来源列表', value: (row) => row.protocols?.join('|') }, + { title: '主通道', value: (row) => row.primaryProtocol }, + { title: '数据通道', value: (row) => row.protocols?.join('|') }, { title: '在线', value: (row) => row.online ? '在线' : '离线' }, { title: '车辆服务状态', value: (row) => vehicleServiceStatus(row).label }, - { title: '来源在线', value: (row) => sourceEvidenceText(row) }, + { title: '通道在线', value: (row) => sourceEvidenceText(row) }, { title: '经度', value: (row) => row.longitude }, { title: '纬度', value: (row) => row.latitude }, { title: '速度km/h', value: (row) => row.speedKmh }, @@ -187,7 +187,7 @@ function realtimeExportFileName(filters: Record) { function realtimeFilterSummary(filters: Record) { return [ filters.keyword ? `关键词:${filters.keyword}` : '', - filters.protocol ? `数据来源:${filters.protocol}` : '', + filters.protocol ? `数据通道:${filters.protocol}` : '', filters.online ? `在线:${onlineLabel[filters.online] ?? filters.online}` : '', filters.serviceStatus ? `服务状态:${serviceStatusLabel[filters.serviceStatus] ?? filters.serviceStatus}` : '' ].filter(Boolean); @@ -265,13 +265,13 @@ function realtimeIssueLabels(row: VehicleRealtimeRow) { function realtimeIssueAction(row: VehicleRealtimeRow) { const issues = realtimeIssueLabels(row).join(';'); if (!isValidCoordinate(row)) { - return `核对${row.primaryProtocol || '实时来源'}定位字段解析和平台转发`; + return `核对${row.primaryProtocol || '实时数据'}定位字段解析和平台转发`; } if (dataFreshness(row).stale) { - return `确认${row.primaryProtocol || '实时来源'}是否持续上报,必要时检查接入链路`; + return `确认${row.primaryProtocol || '实时数据'}是否持续上报,必要时查看告警事件`; } if (hasSourceIssue(row)) { - return '补齐离线来源,确认多来源实时状态一致'; + return '处理离线数据通道,确认车辆实时状态一致'; } return issues === '暂无异常' ? '持续观察' : '结合车辆服务详情继续排查'; } @@ -385,12 +385,12 @@ function realtimeDutyHandoffText({ lines.push( `${index + 1}. ${row.plate || '-'} / ${row.vin} / ${protocol || '-'} / ${status.label}`, ` 最后上报:${row.lastSeen || '-'};新鲜度:${freshness.label};位置:${isValidCoordinate(row) ? `${row.longitude},${row.latitude}` : '无有效坐标'};速度:${row.speedKmh ?? '-'} km/h;SOC:${row.socPercent ?? '-'}%`, - ` 来源:${sourceEvidenceText(row)};问题:${realtimeIssueLabels(row).join(';')}`, + ` 数据覆盖:${sourceEvidenceText(row)};问题:${realtimeIssueLabels(row).join(';')}`, ` 车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: row.vin, protocol }))}`, ` 实时监控:${appURL(buildAppHash({ page: 'realtime', keyword: row.vin, protocol }))}`, ` 轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: row.vin, protocol }))}`, ` 里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: row.vin, protocol }))}`, - ` 历史RAW:${protocol ? rawFrameAPIURL(row, protocol) : '-'}` + ` 原始记录:${protocol ? rawFrameAPIURL(row, protocol) : '-'}` ); }); return lines.join('\n'); @@ -431,7 +431,7 @@ function realtimeImpactReportText({ `定位影响:${locatedCount.toLocaleString()} 辆有有效坐标`, `服务影响:${degradedCount.toLocaleString()} 辆降级 / ${staleCount.toLocaleString()} 辆超时`, `地图能力:${amapConfigured ? '高德地图可用' : '高德地图未配置,使用坐标预览'}`, - `建议动作:${impactLevel === '实时稳定' ? '保持监控并关注告警队列' : '优先处理离线、超时和来源不完整车辆,并回到轨迹/RAW证据复核'}`, + `建议动作:${impactLevel === '实时稳定' ? '保持监控并关注告警队列' : '优先处理离线、超时和来源不完整车辆,并回到轨迹和原始记录复核'}`, `实时监控:${appURL(buildAppHash({ page: 'realtime', protocol: filters.protocol, filters }))}` ].join('\n'); } @@ -728,7 +728,7 @@ export function Realtime({ applyFilters(nextFilters); }} style={{ marginBottom: 12 }}> - + GB32960 JT808 YUTONG_MQTT @@ -815,7 +815,7 @@ export function Realtime({ {staleCount > 0 ? 核对超时车辆 {staleCount.toLocaleString()} : 实时新鲜} {sourceIssueRows.length > 0 && onOpenQuality ? ( ) : null} @@ -825,7 +825,7 @@ export function Realtime({
车辆覆盖概览
{sourceCoverageRows.length === 0 ? ( - 当前页暂无车辆数据通道。 + 当前页暂无车辆实时数据。 ) : sourceCoverageRows.map((item) => (
) : null}
-
地图能力状态
+
地图展示状态
{mapIntegrationRows.map((item) => (
@@ -927,9 +927,9 @@ export function Realtime({ ))}
-
车辆数据一致性检查
+
需关注车辆
{sourceIssueRows.length === 0 ? ( - 当前页车辆来源一致 + 当前页车辆状态稳定 ) : ( sourceIssueRows.map((row) => { const status = vehicleServiceStatus(row); @@ -946,16 +946,16 @@ export function Realtime({ {sourceEvidenceText(row)} {dataFreshness(row).label} {sourceIssueTags(row).map((item) => ( - 一致性:{item} + 问题:{item} ))} - 一致性诊断:{row.serviceStatus?.detail || sourceEvidenceText(row)} + 处置说明:{row.serviceStatus?.detail || sourceEvidenceText(row)}
{row.lastSeen || '-'} - +
@@ -965,7 +965,7 @@ export function Realtime({ )}
-
地图车辆服务队列
+
地图车辆队列
{mapServiceRows.length === 0 ? ( 暂无可定位车辆 ) : ( @@ -1006,8 +1006,8 @@ export function Realtime({ - - + +
@@ -1117,13 +1117,13 @@ export function Realtime({ ) }, { - title: '来源证据', + title: '实时通道', width: 260, render: (_: unknown, row: VehicleRealtimeRow) => ( ) }, - { title: '证据覆盖', width: 120, render: (_: unknown, row: VehicleRealtimeRow) => sourceEvidenceText(row) }, + { title: '数据覆盖', width: 120, render: (_: unknown, row: VehicleRealtimeRow) => sourceEvidenceText(row) }, { title: '在线', width: 90, render: (_: unknown, row: VehicleRealtimeRow) => }, { title: '最后时间', dataIndex: 'lastSeen', width: 170 }, { @@ -1133,7 +1133,7 @@ export function Realtime({ - + ) } 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 01eac874..c28098fb 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -192,7 +192,7 @@ test('exposes AMap operations shortcuts when map key is configured', async () => fireEvent.click(screen.getByRole('button', { name: '顶部地图态势' })); expect(window.location.hash).toBe('#/map'); expect(await screen.findByRole('heading', { name: '实时地图' })).toBeInTheDocument(); - expect(screen.getByText('地图车辆服务队列')).toBeInTheDocument(); + expect(screen.getByText('地图车辆队列')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '顶部实时监控' })); expect(window.location.hash).toBe('#/realtime'); fireEvent.click(screen.getByRole('button', { name: '顶部轨迹回放' })); @@ -8791,11 +8791,11 @@ test('frames realtime page as one vehicle service with source evidence', async ( expect(screen.getByText('高德地图待配置')).toBeInTheDocument(); expect(screen.getAllByText('定位有效').length).toBeGreaterThan(0); expect(screen.getByText('车辆核心数据')).toBeInTheDocument(); - expect(screen.getByText('来源证据')).toBeInTheDocument(); + expect(screen.getByText('实时通道')).toBeInTheDocument(); expect(screen.getByText('GB32960 在线')).toBeInTheDocument(); expect(screen.getByText('JT808 在线')).toBeInTheDocument(); expect(screen.getByText('YUTONG_MQTT 未接入')).toBeInTheDocument(); - expect(screen.getAllByText('2/2 来源在线').length).toBeGreaterThan(0); + expect(screen.getAllByText('2/2 通道在线').length).toBeGreaterThan(0); expect(screen.getByText('实时运营影响')).toBeInTheDocument(); expect(screen.getByRole('button', { name: /复制影响报告/ })).toBeInTheDocument(); expect(screen.getByText('需要处置')).toBeInTheDocument(); @@ -9177,7 +9177,7 @@ test('copies realtime operations summary from realtime page', async () => { fireEvent.click(screen.getByRole('button', { name: '复制实时摘要' })); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【实时监控摘要】')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选:数据来源:JT808;在线:在线')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选:数据通道:JT808;在线:在线')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆总数:3,当前页:3')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆:2,定位有效:2')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('需要关注:2,数据通道:3')); @@ -9187,7 +9187,7 @@ test('copies realtime operations summary from realtime page', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时页面:http://localhost:3000/#/realtime?protocol=JT808&online=online')); fireEvent.click(screen.getByRole('button', { name: '复制实时异常处置清单' })); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【实时异常处置清单】')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选:数据来源:JT808;在线:在线')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选:数据通道:JT808;在线:在线')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常车辆:3 / 当前页 3 / 总计 3')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('粤A摘要3 / VIN-RT-SUMMARY-003 / JT808')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('状态:车辆离线;在线:离线;问题:')); @@ -9205,7 +9205,7 @@ test('copies realtime operations summary from realtime page', async () => { expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时入口:http://localhost:3000/#/realtime?protocol=JT808&online=online')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务:http://localhost:3000/#/detail?keyword=VIN-RT-SUMMARY-003&protocol=JT808')); expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程统计:http://localhost:3000/#/mileage?keyword=VIN-RT-SUMMARY-003&protocol=JT808')); - expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史RAW:http://localhost:3000/api/history/raw-frames?protocol=JT808&vin=VIN-RT-SUMMARY-003&limit=20&includeFields=true')); + expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录:http://localhost:3000/api/history/raw-frames?protocol=JT808&vin=VIN-RT-SUMMARY-003&limit=20&includeFields=true')); fireEvent.click(screen.getByRole('button', { name: '实时筛选 需要关注 2' })); expect(window.location.hash).toBe('#/realtime?protocol=JT808&serviceStatus=degraded&online=online'); fireEvent.click(screen.getByRole('button', { name: '筛选数据通道 GB32960' })); @@ -9313,7 +9313,7 @@ test('shows production AMap integration status on realtime page', async () => { render(); - expect(await screen.findByText('地图能力状态')).toBeInTheDocument(); + expect(await screen.findByText('地图展示状态')).toBeInTheDocument(); expect(screen.getByText('Web JS Key')).toBeInTheDocument(); expect(screen.getByText('服务端 API Key')).toBeInTheDocument(); expect(screen.getAllByText('已配置').length).toBeGreaterThan(0); @@ -9508,10 +9508,10 @@ test('opens vehicle service from realtime map service queue', async () => { render(); - expect(await screen.findByText('地图车辆服务队列')).toBeInTheDocument(); + expect(await screen.findByText('地图车辆队列')).toBeInTheDocument(); expect(screen.getAllByText('粤AMAP01').length).toBeGreaterThan(0); expect(screen.getByText('JT808 离线,GB32960 仍可支撑车辆服务')).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: '地图车辆服务' })); + fireEvent.click(screen.getByRole('button', { name: '车辆详情' })); await waitFor(() => { expect(window.location.hash).toBe('#/detail?keyword=VIN-MAP-001&protocol=GB32960'); @@ -9579,7 +9579,7 @@ test('opens amap coordinate and trajectory playback from realtime map service qu render(); - expect(await screen.findByText('地图车辆服务队列')).toBeInTheDocument(); + expect(await screen.findByText('地图车辆队列')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '高德坐标' })); expect(openSpy).toHaveBeenCalledWith( expect.stringContaining('https://uri.amap.com/marker?position=113.24567,23.12345'), @@ -9682,11 +9682,11 @@ test('surfaces multi-source realtime consistency issues with quality drilldown', render(); - expect(await screen.findByText('车辆数据一致性检查')).toBeInTheDocument(); + expect(await screen.findByText('需关注车辆')).toBeInTheDocument(); expect(screen.getAllByText('粤A一致监控').length).toBeGreaterThan(0); - expect(screen.getByText('一致性诊断:YUTONG_MQTT 离线,GB32960/JT808 仍可支撑车辆服务')).toBeInTheDocument(); - expect(screen.getByText('一致性:YUTONG_MQTT 离线')).toBeInTheDocument(); - fireEvent.click(screen.getByRole('button', { name: '检查质量' })); + expect(screen.getByText('处置说明:YUTONG_MQTT 离线,GB32960/JT808 仍可支撑车辆服务')).toBeInTheDocument(); + expect(screen.getByText('问题:YUTONG_MQTT 离线')).toBeInTheDocument(); + fireEvent.click(screen.getByRole('button', { name: '查看告警' })); await waitFor(() => { expect(window.location.hash).toBe('#/alert-events?keyword=VIN-RT-CONSISTENCY&protocol=GB32960'); @@ -9816,7 +9816,7 @@ test('opens quality issues from realtime vehicle row with source evidence', asyn render(); expect((await screen.findAllByText('VIN-RT-QUALITY')).length).toBeGreaterThan(0); - fireEvent.click(screen.getAllByRole('button', { name: '质量问题' })[0]); + fireEvent.click(screen.getAllByRole('button', { name: '告警事件' })[0]); await waitFor(() => { expect(window.location.hash).toBe('#/alert-events?keyword=VIN-RT-QUALITY&protocol=JT808'); @@ -9991,7 +9991,7 @@ test('shows and clears current realtime service filters', async () => { expect(await screen.findByText('当前实时筛选')).toBeInTheDocument(); expect(screen.getByText('关键词:粤ART002')).toBeInTheDocument(); - expect(screen.getByText('数据来源:JT808')).toBeInTheDocument(); + expect(screen.getByText('数据通道:JT808')).toBeInTheDocument(); expect(screen.getByText('在线:在线')).toBeInTheDocument(); expect(screen.getByText('服务状态:来源不完整')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '清空筛选' })); @@ -10030,7 +10030,7 @@ test('updates realtime hash when source filters are submitted', async () => { await screen.findByRole('heading', { name: '实时监控' }); fireEvent.change(screen.getByPlaceholderText('VIN / 车牌 / 手机号 / OEM'), { target: { value: '粤ART002' } }); - fireEvent.click(screen.getByText('全部来源')); + fireEvent.click(screen.getByText('全部数据通道')); fireEvent.click(await screen.findByText('JT808')); fireEvent.click(screen.getByRole('button', { name: '查询' }));