diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index 8b8274c2..bdad8a99 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -19,8 +19,11 @@ function canOpenVehicle(vin?: string) { function vehicleServiceStatus(row: VehicleRealtimeRow) { if (row.serviceStatus) { + const title = row.serviceStatus.status === 'degraded' || row.serviceStatus.title === '来源不完整' + ? '数据通道不完整' + : row.serviceStatus.title; return { - label: row.serviceStatus.title, + label: title, color: row.serviceStatus.severity === 'ok' ? 'green' as const : row.serviceStatus.severity === 'error' ? 'red' as const : 'orange' as const }; } @@ -28,7 +31,7 @@ function vehicleServiceStatus(row: VehicleRealtimeRow) { return { label: '车辆离线', color: 'red' as const }; } if (row.onlineSourceCount < row.sourceCount) { - return { label: '来源不完整', color: 'orange' as const }; + return { label: '数据通道不完整', color: 'orange' as const }; } return { label: '服务正常', color: 'green' as const }; } @@ -55,7 +58,7 @@ function sourceIssueTags(row: VehicleRealtimeRow) { .filter((source) => !source.online || !source.hasRealtime) .map((source) => `${source.protocol} ${source.hasRealtime ? '离线' : '未接入'}`) ?? []; if (tags.length > 0) return tags; - if (row.onlineSourceCount < row.sourceCount) return ['来源缺失']; + if (row.onlineSourceCount < row.sourceCount) return ['数据通道缺失']; return []; } @@ -144,7 +147,7 @@ const onlineLabel: Record = { const serviceStatusLabel: Record = { healthy: '服务正常', - degraded: '来源不完整', + degraded: '数据通道不完整', offline: '车辆离线', identity_required: '身份未绑定' }; @@ -369,7 +372,7 @@ function realtimeDutyHandoffText({ `页面车辆:${rows.length.toLocaleString()} / 总计 ${total.toLocaleString()};版本:${runtimeRelease || '未标记'}`, `在线:${onlineCount.toLocaleString()};离线:${(rows.length - onlineCount).toLocaleString()};定位有效:${locatedCount.toLocaleString()};需要关注:${degradedCount.toLocaleString()};超时:${staleCount.toLocaleString()};数据通道:${sourceTypeCount.toLocaleString()}`, `地图:${amapConfigured ? '高德已配置' : '高德未配置'}`, - `车辆覆盖:${sourceLines.length > 0 ? sourceLines.join(';') : '当前页暂无来源明细'}`, + `车辆覆盖:${sourceLines.length > 0 ? sourceLines.join(';') : '当前页暂无数据通道明细'}`, `实时入口:${appURL(buildAppHash({ page: 'realtime', protocol: filters.protocol, filters }))}`, '' ]; @@ -385,12 +388,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 }))}`, - ` 原始记录:${protocol ? rawFrameAPIURL(row, protocol) : '-'}` + ` 历史查询:${protocol ? rawFrameAPIURL(row, protocol) : '-'}` ); }); return lines.join('\n'); @@ -431,7 +434,7 @@ function realtimeImpactReportText({ `定位影响:${locatedCount.toLocaleString()} 辆有有效坐标`, `服务影响:${degradedCount.toLocaleString()} 辆降级 / ${staleCount.toLocaleString()} 辆超时`, `地图能力:${amapConfigured ? '高德地图可用' : '高德地图未配置,使用坐标预览'}`, - `建议动作:${impactLevel === '实时稳定' ? '保持监控并关注告警队列' : '优先处理离线、超时和来源不完整车辆,并回到轨迹和原始记录复核'}`, + `建议动作:${impactLevel === '实时稳定' ? '保持监控并关注告警队列' : '优先处理离线、超时和数据通道不完整车辆,并回到轨迹和历史查询复核'}`, `实时监控:${appURL(buildAppHash({ page: 'realtime', protocol: filters.protocol, filters }))}` ].join('\n'); } @@ -517,7 +520,7 @@ function mapCustomerDecisionText({ `在线车辆:${appURL(buildAppHash({ page: 'map', protocol: filters.protocol, filters: { ...filters, online: 'online' } }))}`, selectedVIN ? `车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: selectedVIN, protocol: selectedProtocol }))}` : '', selectedVIN ? `轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: selectedVIN, protocol: selectedProtocol }))}` : '', - selectedVIN ? `数据导出:${appURL(buildAppHash({ page: 'history-query', keyword: selectedVIN, protocol: selectedProtocol, filters: { tab: 'raw', includeFields: 'true' } }))}` : '' + selectedVIN ? `历史查询导出:${appURL(buildAppHash({ page: 'history-query', keyword: selectedVIN, protocol: selectedProtocol, filters: { tab: 'raw', includeFields: 'true' } }))}` : '' ].filter(Boolean).join('\n'); } @@ -558,7 +561,7 @@ async function copyText(value: string, label: string) { export function Realtime({ mode = 'realtime', title = '实时监控', - description = '以车辆为主对象查看最新位置、在线来源、核心实时数据和地图作业状态', + description = '以车辆为主对象查看最新位置、在线状态、核心实时数据和地图作业状态', onOpenVehicle, onOpenHistory, onOpenQuality, @@ -834,7 +837,7 @@ export function Realtime({ { title: '车辆服务', value: selectedVehicleLabel, - detail: selectedMapRow ? '查看车辆画像、来源状态和最新实时字段。' : '先从地图或车辆列表选择一辆车。', + detail: selectedMapRow ? '查看车辆画像、数据通道状态和最新实时字段。' : '先从地图或车辆列表选择一辆车。', action: '车辆档案', color: selectedMapRow ? vehicleServiceStatus(selectedMapRow).color : 'grey' as const, disabled: !selectedMapRow || !canOpenVehicle(selectedMapRow.vin), @@ -918,10 +921,10 @@ export function Realtime({ { title: '查询导出', value: `${rows.length.toLocaleString()} 辆当前页`, - detail: '导出当前车辆清单,或进入选中车辆的历史记录。', + detail: '导出当前车辆清单,或进入选中车辆的历史查询。', color: 'blue' as const, primaryAction: '导出当前页', - secondaryAction: '历史记录', + secondaryAction: '历史查询', onPrimary: exportRealtime, onSecondary: openSelectedVehicleRaw, disabled: false, @@ -1235,7 +1238,7 @@ export function Realtime({ <> {vehicleServiceStatus(selectedMapRow).label} {dataFreshness(selectedMapRow).detail} - {selectedVehicleProtocol || '未知来源'} + {selectedVehicleProtocol || '未知通道'} ) : ( 先选车辆 @@ -1293,7 +1296,7 @@ export function Realtime({ {vehicleServiceStatus(selectedMapRow).label} {dataFreshness(selectedMapRow).detail} - {selectedMapRow.primaryProtocol || '未知来源'} + {selectedMapRow.primaryProtocol || '未知通道'} {selectedMapRow.plate || selectedMapRow.vin} @@ -1411,7 +1414,7 @@ export function Realtime({ 服务正常 - 来源不完整 + 数据通道不完整 车辆离线 身份未绑定 @@ -1724,7 +1727,7 @@ export function Realtime({ { label: '在线率', value: formatPercent(onlineRate), color: onlineRate >= 80 ? 'green' as const : 'orange' as const, detail: `${onlineCount.toLocaleString()} / ${rows.length.toLocaleString()} 辆在线。` }, { label: '定位有效率', value: formatPercent(locatedRate), color: locatedRate >= 80 ? 'green' as const : 'orange' as const, detail: `${locatedCount.toLocaleString()} / ${rows.length.toLocaleString()} 辆坐标有效。` }, { label: '新鲜数据', value: `${(rows.length - staleCount).toLocaleString()} 辆`, color: staleCount > 0 ? 'orange' as const : 'green' as const, detail: `${staleCount.toLocaleString()} 辆超过 5 分钟未更新。` }, - { label: '降级率', value: formatPercent(degradedRate), color: degradedCount > 0 ? 'orange' as const : 'green' as const, detail: `${degradedCount.toLocaleString()} 辆存在来源不完整或离线。` }, + { label: '降级率', value: formatPercent(degradedRate), color: degradedCount > 0 ? 'orange' as const : 'green' as const, detail: `${degradedCount.toLocaleString()} 辆存在数据通道不完整或离线。` }, { label: '分页覆盖率', value: formatPercent(pageCoverageRate), color: pageCoverageRate >= 100 ? 'green' as const : 'grey' as const, detail: '当前页车辆数 / 当前筛选总车辆数。' } ].map((item) => ( 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 2376d061..8cf0fd8b 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -8992,7 +8992,7 @@ test('shows canonical service status in realtime vehicles', async () => { render(); expect((await screen.findAllByText('VIN-RT-DEGRADED')).length).toBeGreaterThan(0); - expect(screen.getAllByText('来源不完整').length).toBeGreaterThan(0); + expect(screen.getAllByText('数据通道不完整').length).toBeGreaterThan(0); }); test('frames realtime page as one vehicle service with source evidence', async () => { @@ -9535,7 +9535,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('原始记录: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' })); @@ -10323,7 +10323,7 @@ test('shows and clears current realtime service filters', async () => { expect(screen.getByText('关键词:粤ART002')).toBeInTheDocument(); expect(screen.getAllByText('数据通道:JT808').length).toBeGreaterThan(0); expect(screen.getByText('在线:在线')).toBeInTheDocument(); - expect(screen.getByText('服务状态:来源不完整')).toBeInTheDocument(); + expect(screen.getByText('服务状态:数据通道不完整')).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '清空筛选' })); await waitFor(() => {