fix(platform): clarify vehicle service status labels
This commit is contained in:
@@ -159,6 +159,9 @@ func TestHandlerVehicleCoverageTreatsMissingCanonicalSourceAsDegraded(t *testing
|
|||||||
if row.ServiceStatus == nil || row.ServiceStatus.Status != "degraded" {
|
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())
|
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
|
noDataStatusFound := false
|
||||||
for _, status := range body.Data.ServiceStatuses {
|
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" {
|
if status.Status == "no_data" {
|
||||||
noDataStatusFound = true
|
noDataStatusFound = true
|
||||||
if status.Title != "暂无数据来源" || status.Count != body.Data.NoDataVehicles {
|
if status.Title != "暂无数据来源" || status.Count != body.Data.NoDataVehicles {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func (m *MockStore) DashboardSummary(context.Context) (DashboardSummary, error)
|
|||||||
},
|
},
|
||||||
ServiceStatuses: []ServiceStatusStat{
|
ServiceStatuses: []ServiceStatusStat{
|
||||||
{Status: "healthy", Title: "服务正常", Count: 2},
|
{Status: "healthy", Title: "服务正常", Count: 2},
|
||||||
{Status: "degraded", Title: "部分来源离线", Count: 1},
|
{Status: "degraded", Title: "来源异常", Count: 1},
|
||||||
{Status: "offline", Title: "车辆离线", Count: 1},
|
{Status: "offline", Title: "车辆离线", Count: 1},
|
||||||
{Status: "identity_required", Title: "身份未绑定", Count: 0},
|
{Status: "identity_required", Title: "身份未绑定", Count: 0},
|
||||||
},
|
},
|
||||||
@@ -184,7 +184,7 @@ func (m *MockStore) VehicleServiceSummary(ctx context.Context) (VehicleServiceSu
|
|||||||
}
|
}
|
||||||
statusCounts := map[string]*ServiceStatusStat{
|
statusCounts := map[string]*ServiceStatusStat{
|
||||||
"healthy": {Status: "healthy", Title: "服务正常"},
|
"healthy": {Status: "healthy", Title: "服务正常"},
|
||||||
"degraded": {Status: "degraded", Title: "部分来源离线"},
|
"degraded": {Status: "degraded", Title: "来源异常"},
|
||||||
"offline": {Status: "offline", Title: "车辆离线"},
|
"offline": {Status: "offline", Title: "车辆离线"},
|
||||||
"no_data": {Status: "no_data", Title: "暂无数据来源"},
|
"no_data": {Status: "no_data", Title: "暂无数据来源"},
|
||||||
"identity_required": {Status: "identity_required", Title: "身份未绑定"},
|
"identity_required": {Status: "identity_required", Title: "身份未绑定"},
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (s *ProductionStore) DashboardSummary(ctx context.Context) (DashboardSummar
|
|||||||
func (s *ProductionStore) serviceStatusStats(ctx context.Context) ([]ServiceStatusStat, error) {
|
func (s *ProductionStore) serviceStatusStats(ctx context.Context) ([]ServiceStatusStat, error) {
|
||||||
definitions := []ServiceStatusStat{
|
definitions := []ServiceStatusStat{
|
||||||
{Status: "healthy", Title: "服务正常"},
|
{Status: "healthy", Title: "服务正常"},
|
||||||
{Status: "degraded", Title: "部分来源离线"},
|
{Status: "degraded", Title: "来源异常"},
|
||||||
{Status: "offline", Title: "车辆离线"},
|
{Status: "offline", Title: "车辆离线"},
|
||||||
{Status: "no_data", Title: "暂无数据来源"},
|
{Status: "no_data", Title: "暂无数据来源"},
|
||||||
{Status: "identity_required", Title: "身份未绑定"},
|
{Status: "identity_required", Title: "身份未绑定"},
|
||||||
@@ -149,7 +149,7 @@ GROUP BY v.vin
|
|||||||
}
|
}
|
||||||
summary.ServiceStatuses = []ServiceStatusStat{
|
summary.ServiceStatuses = []ServiceStatusStat{
|
||||||
{Status: "healthy", Title: "服务正常", Count: healthy},
|
{Status: "healthy", Title: "服务正常", Count: healthy},
|
||||||
{Status: "degraded", Title: "部分来源离线", Count: degraded},
|
{Status: "degraded", Title: "来源异常", Count: degraded},
|
||||||
{Status: "offline", Title: "车辆离线", Count: offline},
|
{Status: "offline", Title: "车辆离线", Count: offline},
|
||||||
{Status: "no_data", Title: "暂无数据来源", Count: summary.NoDataVehicles},
|
{Status: "no_data", Title: "暂无数据来源", Count: summary.NoDataVehicles},
|
||||||
{Status: "identity_required", Title: "身份未绑定", Count: summary.IdentityRequiredVehicles},
|
{Status: "identity_required", Title: "身份未绑定", Count: summary.IdentityRequiredVehicles},
|
||||||
|
|||||||
@@ -970,6 +970,16 @@ func buildVehicleCoverageServiceStatus(row VehicleCoverageRow) *VehicleServiceSt
|
|||||||
OnlineSourceCount: row.OnlineSourceCount,
|
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 {
|
if row.OnlineSourceCount < row.SourceCount {
|
||||||
return &VehicleServiceStatus{
|
return &VehicleServiceStatus{
|
||||||
Status: "degraded",
|
Status: "degraded",
|
||||||
@@ -980,16 +990,6 @@ func buildVehicleCoverageServiceStatus(row VehicleCoverageRow) *VehicleServiceSt
|
|||||||
OnlineSourceCount: row.OnlineSourceCount,
|
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{
|
return &VehicleServiceStatus{
|
||||||
Status: "healthy",
|
Status: "healthy",
|
||||||
Severity: "ok",
|
Severity: "ok",
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ function serviceStatusFromOverview(overview: VehicleServiceOverview): VehicleSer
|
|||||||
return {
|
return {
|
||||||
status: 'degraded',
|
status: 'degraded',
|
||||||
severity: 'warning',
|
severity: 'warning',
|
||||||
title: '部分来源离线',
|
title: '来源异常',
|
||||||
detail: `${onlineSourceCount}/${sourceCount} 个来源在线,车辆服务可用但需要关注离线来源。`,
|
detail: `${onlineSourceCount}/${sourceCount} 个来源在线,车辆服务可用但需要关注离线来源。`,
|
||||||
sourceCount,
|
sourceCount,
|
||||||
onlineSourceCount
|
onlineSourceCount
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const serviceStatusColor: Record<string, 'green' | 'orange' | 'red' | 'grey'> =
|
|||||||
|
|
||||||
const serviceStatusTitle: Record<string, string> = {
|
const serviceStatusTitle: Record<string, string> = {
|
||||||
healthy: '服务正常',
|
healthy: '服务正常',
|
||||||
degraded: '部分来源离线',
|
degraded: '来源异常',
|
||||||
offline: '车辆离线',
|
offline: '车辆离线',
|
||||||
no_data: '暂无数据来源',
|
no_data: '暂无数据来源',
|
||||||
identity_required: '身份未绑定'
|
identity_required: '身份未绑定'
|
||||||
@@ -54,7 +54,7 @@ function rowServiceStatus(row: { serviceStatus?: { title: string; severity: stri
|
|||||||
return { label: '车辆离线', color: 'red' as const };
|
return { label: '车辆离线', color: 'red' as const };
|
||||||
}
|
}
|
||||||
if (row.onlineSourceCount < row.sourceCount) {
|
if (row.onlineSourceCount < row.sourceCount) {
|
||||||
return { label: '部分来源离线', color: 'orange' as const };
|
return { label: '来源异常', color: 'orange' as const };
|
||||||
}
|
}
|
||||||
return { label: '服务正常', color: 'green' as const };
|
return { label: '服务正常', color: 'green' as const };
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on
|
|||||||
</Form.Select>
|
</Form.Select>
|
||||||
<Form.Select field="serviceStatus" label="服务状态" placeholder="全部" style={{ width: 150 }} data-testid="dashboard-service-status-filter">
|
<Form.Select field="serviceStatus" label="服务状态" placeholder="全部" style={{ width: 150 }} data-testid="dashboard-service-status-filter">
|
||||||
<Select.Option value="healthy">服务正常</Select.Option>
|
<Select.Option value="healthy">服务正常</Select.Option>
|
||||||
<Select.Option value="degraded">部分来源离线</Select.Option>
|
<Select.Option value="degraded">来源异常</Select.Option>
|
||||||
<Select.Option value="offline">车辆离线</Select.Option>
|
<Select.Option value="offline">车辆离线</Select.Option>
|
||||||
<Select.Option value="no_data">暂无数据来源</Select.Option>
|
<Select.Option value="no_data">暂无数据来源</Select.Option>
|
||||||
<Select.Option value="identity_required">身份未绑定</Select.Option>
|
<Select.Option value="identity_required">身份未绑定</Select.Option>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function vehicleServiceStatus(row: VehicleRealtimeRow) {
|
|||||||
return { label: '车辆离线', color: 'red' as const };
|
return { label: '车辆离线', color: 'red' as const };
|
||||||
}
|
}
|
||||||
if (row.onlineSourceCount < row.sourceCount) {
|
if (row.onlineSourceCount < row.sourceCount) {
|
||||||
return { label: '部分来源离线', color: 'orange' as const };
|
return { label: '来源异常', color: 'orange' as const };
|
||||||
}
|
}
|
||||||
return { label: '服务正常', color: 'green' as const };
|
return { label: '服务正常', color: 'green' as const };
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ export function Realtime({ onOpenVehicle }: { onOpenVehicle: (vin: string, proto
|
|||||||
</Form.Select>
|
</Form.Select>
|
||||||
<Form.Select field="serviceStatus" label="服务状态" placeholder="全部" style={{ width: 150 }}>
|
<Form.Select field="serviceStatus" label="服务状态" placeholder="全部" style={{ width: 150 }}>
|
||||||
<Select.Option value="healthy">服务正常</Select.Option>
|
<Select.Option value="healthy">服务正常</Select.Option>
|
||||||
<Select.Option value="degraded">部分来源离线</Select.Option>
|
<Select.Option value="degraded">来源异常</Select.Option>
|
||||||
<Select.Option value="offline">车辆离线</Select.Option>
|
<Select.Option value="offline">车辆离线</Select.Option>
|
||||||
<Select.Option value="identity_required">身份未绑定</Select.Option>
|
<Select.Option value="identity_required">身份未绑定</Select.Option>
|
||||||
</Form.Select>
|
</Form.Select>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ function vehicleServiceStatus(row: VehicleCoverageRow) {
|
|||||||
return { label: '车辆离线', color: 'red' as const };
|
return { label: '车辆离线', color: 'red' as const };
|
||||||
}
|
}
|
||||||
if (row.onlineSourceCount < row.sourceCount) {
|
if (row.onlineSourceCount < row.sourceCount) {
|
||||||
return { label: '部分来源离线', color: 'orange' as const };
|
return { label: '来源异常', color: 'orange' as const };
|
||||||
}
|
}
|
||||||
return { label: '服务正常', color: 'green' as const };
|
return { label: '服务正常', color: 'green' as const };
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ export function Vehicles({
|
|||||||
</Form.Select>
|
</Form.Select>
|
||||||
<Form.Select field="serviceStatus" label="服务状态" placeholder="全部" style={{ width: 150 }} data-testid="service-status-filter">
|
<Form.Select field="serviceStatus" label="服务状态" placeholder="全部" style={{ width: 150 }} data-testid="service-status-filter">
|
||||||
<Select.Option value="healthy">服务正常</Select.Option>
|
<Select.Option value="healthy">服务正常</Select.Option>
|
||||||
<Select.Option value="degraded">部分来源离线</Select.Option>
|
<Select.Option value="degraded">来源异常</Select.Option>
|
||||||
<Select.Option value="offline">车辆离线</Select.Option>
|
<Select.Option value="offline">车辆离线</Select.Option>
|
||||||
<Select.Option value="no_data">暂无数据来源</Select.Option>
|
<Select.Option value="no_data">暂无数据来源</Select.Option>
|
||||||
<Select.Option value="identity_required">身份未绑定</Select.Option>
|
<Select.Option value="identity_required">身份未绑定</Select.Option>
|
||||||
|
|||||||
@@ -817,13 +817,13 @@ test('filters dashboard coverage by service status', async () => {
|
|||||||
|
|
||||||
expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
|
expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
|
||||||
fireEvent.click(screen.getByTestId('dashboard-service-status-filter'));
|
fireEvent.click(screen.getByTestId('dashboard-service-status-filter'));
|
||||||
fireEvent.click(await screen.findByText('部分来源离线'));
|
fireEvent.click(await screen.findByText('来源异常'));
|
||||||
fireEvent.click(screen.getByRole('button', { name: '筛选' }));
|
fireEvent.click(screen.getByRole('button', { name: '筛选' }));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined);
|
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 () => {
|
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(<App />);
|
render(<App />);
|
||||||
|
|
||||||
expect(await screen.findByText('粤AG18312 / VIN001')).toBeInTheDocument();
|
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 () => {
|
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: '车辆服务' });
|
await screen.findByRole('heading', { name: '车辆服务' });
|
||||||
fireEvent.click(screen.getByTestId('service-status-filter'));
|
fireEvent.click(screen.getByTestId('service-status-filter'));
|
||||||
fireEvent.click(await screen.findByText('部分来源离线'));
|
fireEvent.click(await screen.findByText('来源异常'));
|
||||||
fireEvent.click(screen.getByRole('button', { name: '查询' }));
|
fireEvent.click(screen.getByRole('button', { name: '查询' }));
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user