diff --git a/vehicle-data-platform/apps/api/internal/platform/handler_test.go b/vehicle-data-platform/apps/api/internal/platform/handler_test.go index 7c455ca3..3be9c876 100644 --- a/vehicle-data-platform/apps/api/internal/platform/handler_test.go +++ b/vehicle-data-platform/apps/api/internal/platform/handler_test.go @@ -159,6 +159,9 @@ func TestHandlerVehicleCoverageTreatsMissingCanonicalSourceAsDegraded(t *testing if row.ServiceStatus == nil || row.ServiceStatus.Status != "degraded" { t.Fatalf("missing canonical source should degrade vehicle service, got row=%+v body=%s", row, rec.Body.String()) } + if row.ServiceStatus.Title != "来源不完整" { + t.Fatalf("missing canonical source should use explicit incomplete-source title, got row=%+v body=%s", row, rec.Body.String()) + } } } @@ -410,6 +413,9 @@ func TestHandlerVehicleServiceSummaryEndpoint(t *testing.T) { } noDataStatusFound := false for _, status := range body.Data.ServiceStatuses { + if status.Status == "degraded" && status.Title != "来源异常" { + t.Fatalf("summary degraded status should use broad source exception title, got %+v", status) + } if status.Status == "no_data" { noDataStatusFound = true if status.Title != "暂无数据来源" || status.Count != body.Data.NoDataVehicles { diff --git a/vehicle-data-platform/apps/api/internal/platform/mock_store.go b/vehicle-data-platform/apps/api/internal/platform/mock_store.go index 26374e4f..782a21a0 100644 --- a/vehicle-data-platform/apps/api/internal/platform/mock_store.go +++ b/vehicle-data-platform/apps/api/internal/platform/mock_store.go @@ -46,7 +46,7 @@ func (m *MockStore) DashboardSummary(context.Context) (DashboardSummary, error) }, ServiceStatuses: []ServiceStatusStat{ {Status: "healthy", Title: "服务正常", Count: 2}, - {Status: "degraded", Title: "部分来源离线", Count: 1}, + {Status: "degraded", Title: "来源异常", Count: 1}, {Status: "offline", Title: "车辆离线", Count: 1}, {Status: "identity_required", Title: "身份未绑定", Count: 0}, }, @@ -184,7 +184,7 @@ func (m *MockStore) VehicleServiceSummary(ctx context.Context) (VehicleServiceSu } statusCounts := map[string]*ServiceStatusStat{ "healthy": {Status: "healthy", Title: "服务正常"}, - "degraded": {Status: "degraded", Title: "部分来源离线"}, + "degraded": {Status: "degraded", Title: "来源异常"}, "offline": {Status: "offline", Title: "车辆离线"}, "no_data": {Status: "no_data", Title: "暂无数据来源"}, "identity_required": {Status: "identity_required", Title: "身份未绑定"}, diff --git a/vehicle-data-platform/apps/api/internal/platform/production_store.go b/vehicle-data-platform/apps/api/internal/platform/production_store.go index 331b25cb..8a024fcd 100644 --- a/vehicle-data-platform/apps/api/internal/platform/production_store.go +++ b/vehicle-data-platform/apps/api/internal/platform/production_store.go @@ -76,7 +76,7 @@ func (s *ProductionStore) DashboardSummary(ctx context.Context) (DashboardSummar func (s *ProductionStore) serviceStatusStats(ctx context.Context) ([]ServiceStatusStat, error) { definitions := []ServiceStatusStat{ {Status: "healthy", Title: "服务正常"}, - {Status: "degraded", Title: "部分来源离线"}, + {Status: "degraded", Title: "来源异常"}, {Status: "offline", Title: "车辆离线"}, {Status: "no_data", Title: "暂无数据来源"}, {Status: "identity_required", Title: "身份未绑定"}, @@ -149,7 +149,7 @@ GROUP BY v.vin } summary.ServiceStatuses = []ServiceStatusStat{ {Status: "healthy", Title: "服务正常", Count: healthy}, - {Status: "degraded", Title: "部分来源离线", Count: degraded}, + {Status: "degraded", Title: "来源异常", Count: degraded}, {Status: "offline", Title: "车辆离线", Count: offline}, {Status: "no_data", Title: "暂无数据来源", Count: summary.NoDataVehicles}, {Status: "identity_required", Title: "身份未绑定", Count: summary.IdentityRequiredVehicles}, diff --git a/vehicle-data-platform/apps/api/internal/platform/service.go b/vehicle-data-platform/apps/api/internal/platform/service.go index 93630b3f..90b192b3 100644 --- a/vehicle-data-platform/apps/api/internal/platform/service.go +++ b/vehicle-data-platform/apps/api/internal/platform/service.go @@ -970,6 +970,16 @@ func buildVehicleCoverageServiceStatus(row VehicleCoverageRow) *VehicleServiceSt OnlineSourceCount: row.OnlineSourceCount, } } + if len(row.MissingProtocols) > 0 { + return &VehicleServiceStatus{ + Status: "degraded", + Severity: "warning", + Title: "来源不完整", + Detail: sourceCoverageDetail(row.SourceCount, row.OnlineSourceCount, "车辆服务可用但缺少 "+strings.Join(row.MissingProtocols, "、")+" 来源。"), + SourceCount: row.SourceCount, + OnlineSourceCount: row.OnlineSourceCount, + } + } if row.OnlineSourceCount < row.SourceCount { return &VehicleServiceStatus{ Status: "degraded", @@ -980,16 +990,6 @@ func buildVehicleCoverageServiceStatus(row VehicleCoverageRow) *VehicleServiceSt OnlineSourceCount: row.OnlineSourceCount, } } - if len(row.MissingProtocols) > 0 { - return &VehicleServiceStatus{ - Status: "degraded", - Severity: "warning", - Title: "部分来源离线", - Detail: sourceCoverageDetail(row.SourceCount, row.OnlineSourceCount, "车辆服务可用但缺少 "+strings.Join(row.MissingProtocols, "、")+" 来源。"), - SourceCount: row.SourceCount, - OnlineSourceCount: row.OnlineSourceCount, - } - } return &VehicleServiceStatus{ Status: "healthy", Severity: "ok", diff --git a/vehicle-data-platform/apps/web/src/App.tsx b/vehicle-data-platform/apps/web/src/App.tsx index 2859ac83..c8db398e 100644 --- a/vehicle-data-platform/apps/web/src/App.tsx +++ b/vehicle-data-platform/apps/web/src/App.tsx @@ -268,7 +268,7 @@ function serviceStatusFromOverview(overview: VehicleServiceOverview): VehicleSer return { status: 'degraded', severity: 'warning', - title: '部分来源离线', + title: '来源异常', detail: `${onlineSourceCount}/${sourceCount} 个来源在线,车辆服务可用但需要关注离线来源。`, sourceCount, onlineSourceCount diff --git a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx index 9939e90e..4165959c 100644 --- a/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Dashboard.tsx @@ -23,7 +23,7 @@ const serviceStatusColor: Record = const serviceStatusTitle: Record = { healthy: '服务正常', - degraded: '部分来源离线', + degraded: '来源异常', offline: '车辆离线', no_data: '暂无数据来源', identity_required: '身份未绑定' @@ -54,7 +54,7 @@ function rowServiceStatus(row: { serviceStatus?: { title: string; severity: stri 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 }; } @@ -275,7 +275,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on 服务正常 - 部分来源离线 + 来源异常 车辆离线 暂无数据来源 身份未绑定 diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index 60cf757a..688b695b 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -22,7 +22,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 }; } @@ -78,7 +78,7 @@ export function Realtime({ onOpenVehicle }: { onOpenVehicle: (vin: string, proto 服务正常 - 部分来源离线 + 来源异常 车辆离线 身份未绑定 diff --git a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx index ca774597..f37213f0 100644 --- a/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Vehicles.tsx @@ -24,7 +24,7 @@ function vehicleServiceStatus(row: VehicleCoverageRow) { 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 }; } @@ -170,7 +170,7 @@ export function Vehicles({ 服务正常 - 部分来源离线 + 来源异常 车辆离线 暂无数据来源 身份未绑定 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 d2216a99..e729122f 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -817,13 +817,13 @@ test('filters dashboard coverage by service status', async () => { expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument(); fireEvent.click(screen.getByTestId('dashboard-service-status-filter')); - fireEvent.click(await screen.findByText('部分来源离线')); + fireEvent.click(await screen.findByText('来源异常')); fireEvent.click(screen.getByRole('button', { name: '筛选' })); await waitFor(() => { expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined); }); - expect(screen.getByText('当前筛选:部分来源离线')).toBeInTheDocument(); + expect(screen.getByText('当前筛选:来源异常')).toBeInTheDocument(); }); test('filters dashboard coverage by missing source evidence', async () => { @@ -1284,7 +1284,7 @@ test('shows vehicle context when opening detail from shareable hash', async () = render(); expect(await screen.findByText('粤AG18312 / VIN001')).toBeInTheDocument(); - expect(screen.getByText('当前车辆:部分来源离线')).toBeInTheDocument(); + expect(screen.getByText('当前车辆:来源异常')).toBeInTheDocument(); }); test('refreshes vehicle context when hash changes to another detail vehicle', async () => { @@ -1983,7 +1983,7 @@ test('filters vehicle list by service status', async () => { await screen.findByRole('heading', { name: '车辆服务' }); fireEvent.click(screen.getByTestId('service-status-filter')); - fireEvent.click(await screen.findByText('部分来源离线')); + fireEvent.click(await screen.findByText('来源异常')); fireEvent.click(screen.getByRole('button', { name: '查询' })); await waitFor(() => {