refactor(platform): centralize vehicle lookup rules
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, test } from 'vitest';
|
||||||
|
import type { QualityIssueRow } from '../api/types';
|
||||||
|
import { qualityIssueVehicleLookup } from './vehicleLookup';
|
||||||
|
|
||||||
|
function issue(row: Partial<QualityIssueRow>): QualityIssueRow {
|
||||||
|
return {
|
||||||
|
vin: '',
|
||||||
|
plate: '',
|
||||||
|
phone: '',
|
||||||
|
sourceEndpoint: '',
|
||||||
|
protocol: 'JT808',
|
||||||
|
issueType: 'VIN_MISSING',
|
||||||
|
severity: 'warning',
|
||||||
|
lastSeen: '',
|
||||||
|
detail: '',
|
||||||
|
...row
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('qualityIssueVehicleLookup', () => {
|
||||||
|
test('prefers a valid VIN', () => {
|
||||||
|
expect(qualityIssueVehicleLookup(issue({ vin: 'VIN001', plate: '粤A12345', phone: '13300000001' }))).toEqual({
|
||||||
|
key: 'VIN001',
|
||||||
|
label: '车辆服务'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('uses plate when VIN is missing', () => {
|
||||||
|
expect(qualityIssueVehicleLookup(issue({ vin: 'unknown', plate: '粤A12345', phone: '13300000001' }))).toEqual({
|
||||||
|
key: '粤A12345',
|
||||||
|
label: '按车牌查车'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('uses phone when VIN and plate are missing', () => {
|
||||||
|
expect(qualityIssueVehicleLookup(issue({ vin: 'unknown', phone: '13300000001' }))).toEqual({
|
||||||
|
key: '13300000001',
|
||||||
|
label: '按手机号查车'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
22
vehicle-data-platform/apps/web/src/domain/vehicleLookup.ts
Normal file
22
vehicle-data-platform/apps/web/src/domain/vehicleLookup.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import type { QualityIssueRow } from '../api/types';
|
||||||
|
|
||||||
|
export type VehicleLookup = {
|
||||||
|
key: string;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function qualityIssueVehicleLookup(row: QualityIssueRow): VehicleLookup {
|
||||||
|
const vin = row.vin?.trim();
|
||||||
|
if (vin && vin !== 'unknown') {
|
||||||
|
return { key: vin, label: '车辆服务' };
|
||||||
|
}
|
||||||
|
const plate = row.plate?.trim();
|
||||||
|
if (plate) {
|
||||||
|
return { key: plate, label: '按车牌查车' };
|
||||||
|
}
|
||||||
|
const phone = row.phone?.trim();
|
||||||
|
if (phone) {
|
||||||
|
return { key: phone, label: '按手机号查车' };
|
||||||
|
}
|
||||||
|
return { key: '', label: '车辆服务' };
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { api } from '../api/client';
|
|||||||
import type { DashboardSummary, LinkHealth, ProtocolStat, QualityIssueRow, VehicleCoverageRow, VehicleRealtimeRow } from '../api/types';
|
import type { DashboardSummary, LinkHealth, ProtocolStat, QualityIssueRow, VehicleCoverageRow, VehicleRealtimeRow } from '../api/types';
|
||||||
import { PageHeader } from '../components/PageHeader';
|
import { PageHeader } from '../components/PageHeader';
|
||||||
import { StatusTag } from '../components/StatusTag';
|
import { StatusTag } from '../components/StatusTag';
|
||||||
|
import { qualityIssueVehicleLookup } from '../domain/vehicleLookup';
|
||||||
|
|
||||||
const statusColor: Record<string, 'green' | 'orange' | 'red' | 'grey'> = {
|
const statusColor: Record<string, 'green' | 'orange' | 'red' | 'grey'> = {
|
||||||
ok: 'green',
|
ok: 'green',
|
||||||
@@ -15,22 +16,6 @@ function formatLag(value?: number | null) {
|
|||||||
return value == null ? '未接入' : value.toLocaleString();
|
return value == null ? '未接入' : value.toLocaleString();
|
||||||
}
|
}
|
||||||
|
|
||||||
function qualityVehicleLookup(row: QualityIssueRow) {
|
|
||||||
const vin = row.vin?.trim();
|
|
||||||
if (vin && vin !== 'unknown') {
|
|
||||||
return { key: vin, label: '车辆服务' };
|
|
||||||
}
|
|
||||||
const plate = row.plate?.trim();
|
|
||||||
if (plate) {
|
|
||||||
return { key: plate, label: '按车牌查车' };
|
|
||||||
}
|
|
||||||
const phone = row.phone?.trim();
|
|
||||||
if (phone) {
|
|
||||||
return { key: phone, label: '按手机号查车' };
|
|
||||||
}
|
|
||||||
return { key: '', label: '车辆服务' };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Dashboard({ onOpenVehicle, onOpenQuality }: { onOpenVehicle: (vin: string) => void; onOpenQuality: () => void }) {
|
export function Dashboard({ onOpenVehicle, onOpenQuality }: { onOpenVehicle: (vin: string) => void; onOpenQuality: () => void }) {
|
||||||
const [summary, setSummary] = useState<DashboardSummary | null>(null);
|
const [summary, setSummary] = useState<DashboardSummary | null>(null);
|
||||||
const [coverage, setCoverage] = useState<VehicleCoverageRow[]>([]);
|
const [coverage, setCoverage] = useState<VehicleCoverageRow[]>([]);
|
||||||
@@ -148,7 +133,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality }: { onOpenVehicle: (vi
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
width: 130,
|
width: 130,
|
||||||
render: (_: unknown, row: QualityIssueRow) => {
|
render: (_: unknown, row: QualityIssueRow) => {
|
||||||
const lookup = qualityVehicleLookup(row);
|
const lookup = qualityIssueVehicleLookup(row);
|
||||||
return <Button disabled={!lookup.key} onClick={() => onOpenVehicle(lookup.key)}>{lookup.label}</Button>;
|
return <Button disabled={!lookup.key} onClick={() => onOpenVehicle(lookup.key)}>{lookup.label}</Button>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { api } from '../api/client';
|
import { api } from '../api/client';
|
||||||
import type { OpsHealth, QualitySummary, QualityIssueRow } from '../api/types';
|
import type { OpsHealth, QualitySummary, QualityIssueRow } from '../api/types';
|
||||||
import { PageHeader } from '../components/PageHeader';
|
import { PageHeader } from '../components/PageHeader';
|
||||||
|
import { qualityIssueVehicleLookup } from '../domain/vehicleLookup';
|
||||||
|
|
||||||
const statusColor: Record<string, 'green' | 'orange' | 'red' | 'grey'> = {
|
const statusColor: Record<string, 'green' | 'orange' | 'red' | 'grey'> = {
|
||||||
ok: 'green',
|
ok: 'green',
|
||||||
@@ -49,22 +50,6 @@ async function copyText(value: string, label: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function vehicleLookup(row: QualityIssueRow) {
|
|
||||||
const vin = row.vin?.trim();
|
|
||||||
if (vin && vin !== 'unknown') {
|
|
||||||
return { key: vin, label: '车辆服务' };
|
|
||||||
}
|
|
||||||
const plate = row.plate?.trim();
|
|
||||||
if (plate) {
|
|
||||||
return { key: plate, label: '按车牌查车' };
|
|
||||||
}
|
|
||||||
const phone = row.phone?.trim();
|
|
||||||
if (phone) {
|
|
||||||
return { key: phone, label: '按手机号查车' };
|
|
||||||
}
|
|
||||||
return { key: '', label: '车辆服务' };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Quality({
|
export function Quality({
|
||||||
onOpenVehicle,
|
onOpenVehicle,
|
||||||
onHealthLoaded
|
onHealthLoaded
|
||||||
@@ -194,7 +179,7 @@ export function Quality({
|
|||||||
title: '操作',
|
title: '操作',
|
||||||
width: 250,
|
width: 250,
|
||||||
render: (_: unknown, row: QualityIssueRow) => {
|
render: (_: unknown, row: QualityIssueRow) => {
|
||||||
const lookup = vehicleLookup(row);
|
const lookup = qualityIssueVehicleLookup(row);
|
||||||
return (
|
return (
|
||||||
<Space spacing={4}>
|
<Space spacing={4}>
|
||||||
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.phone, '手机号')}>手机号</Button>
|
<Button size="small" icon={<IconCopy />} onClick={() => copyText(row.phone, '手机号')}>手机号</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user