From a4e9a8afb8cdac159fde0ecb8bb7e788a793a05e Mon Sep 17 00:00:00 2001 From: lingniu Date: Fri, 3 Jul 2026 23:17:23 +0800 Subject: [PATCH] feat(platform): include plates in raw history --- .../api/internal/platform/handler_test.go | 2 +- .../apps/api/internal/platform/mock_store.go | 2 +- .../apps/api/internal/platform/model.go | 1 + .../api/internal/platform/production_store.go | 76 +++++++++++++------ .../apps/web/src/api/types.ts | 1 + .../apps/web/src/pages/History.tsx | 3 +- 6 files changed, 60 insertions(+), 25 deletions(-) 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 b2d71c2a..c1b1e66d 100644 --- a/vehicle-data-platform/apps/api/internal/platform/handler_test.go +++ b/vehicle-data-platform/apps/api/internal/platform/handler_test.go @@ -110,7 +110,7 @@ func TestHandlerHistoryMileageQualityOps(t *testing.T) { want string }{ {"/api/history/locations?limit=10", "totalMileageKm"}, - {"/api/history/raw-frames?protocol=GB32960&vin=LB9A32A24R0LS1426&limit=1&includeFields=true", "rawSizeBytes"}, + {"/api/history/raw-frames?protocol=GB32960&vin=LB9A32A24R0LS1426&limit=1&includeFields=true", "plate"}, {"/api/mileage/daily?limit=10", "dailyMileageKm"}, {"/api/quality/issues?limit=10", "issueType"}, {"/api/ops/health", "linkHealth"}, 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 a98b2c92..5849dc08 100644 --- a/vehicle-data-platform/apps/api/internal/platform/mock_store.go +++ b/vehicle-data-platform/apps/api/internal/platform/mock_store.go @@ -221,7 +221,7 @@ func (m *MockStore) RawFrames(_ context.Context, query RawFrameQuery) (Page[RawF protocol := defaultString(query.Protocol, "GB32960") vin := defaultString(query.VIN, "LB9A32A24R0LS1426") rows := []RawFrameRow{ - {ID: "raw-20260703-001", VIN: vin, Protocol: protocol, FrameType: "realtime", DeviceTime: "2026-07-03 20:12:06", ServerTime: "2026-07-03 20:12:06", RawSizeBytes: 430, ParsedFields: fields}, + {ID: "raw-20260703-001", VIN: vin, Plate: "粤AG18312", Protocol: protocol, FrameType: "realtime", DeviceTime: "2026-07-03 20:12:06", ServerTime: "2026-07-03 20:12:06", RawSizeBytes: 430, ParsedFields: fields}, } limit := query.Limit if limit <= 0 { diff --git a/vehicle-data-platform/apps/api/internal/platform/model.go b/vehicle-data-platform/apps/api/internal/platform/model.go index 910f50dc..8986a57b 100644 --- a/vehicle-data-platform/apps/api/internal/platform/model.go +++ b/vehicle-data-platform/apps/api/internal/platform/model.go @@ -122,6 +122,7 @@ type HistoryLocationRow struct { type RawFrameRow struct { ID string `json:"id"` VIN string `json:"vin"` + Plate string `json:"plate"` Protocol string `json:"protocol"` FrameType string `json:"frameType"` DeviceTime string `json:"deviceTime"` 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 9444fe9a..9f992f12 100644 --- a/vehicle-data-platform/apps/api/internal/platform/production_store.go +++ b/vehicle-data-platform/apps/api/internal/platform/production_store.go @@ -305,31 +305,10 @@ func (s *ProductionStore) enrichHistoryLocationPlates(ctx context.Context, items seen[vin] = struct{}{} vins = append(vins, vin) } - if len(vins) == 0 { - return nil - } - placeholders := make([]string, len(vins)) - args := make([]any, len(vins)) - for index, vin := range vins { - placeholders[index] = "?" - args[index] = vin - } - rows, err := s.db.QueryContext(ctx, `SELECT vin, COALESCE(plate, '') FROM vehicle_identity_binding WHERE vin IN (`+strings.Join(placeholders, ",")+`)`, args...) + plates, err := s.platesByVIN(ctx, vins) if err != nil { return err } - defer rows.Close() - plates := map[string]string{} - for rows.Next() { - var vin, plate string - if err := rows.Scan(&vin, &plate); err != nil { - return err - } - plates[vin] = plate - } - if err := rows.Err(); err != nil { - return err - } for index := range items { if strings.TrimSpace(items[index].Plate) != "" { continue @@ -339,6 +318,56 @@ func (s *ProductionStore) enrichHistoryLocationPlates(ctx context.Context, items return nil } +func (s *ProductionStore) enrichRawFramePlates(ctx context.Context, items []RawFrameRow) error { + vins := make([]string, 0) + seen := map[string]struct{}{} + for _, item := range items { + vin := strings.TrimSpace(item.VIN) + if vin == "" { + continue + } + if _, ok := seen[vin]; ok { + continue + } + seen[vin] = struct{}{} + vins = append(vins, vin) + } + plates, err := s.platesByVIN(ctx, vins) + if err != nil { + return err + } + for index := range items { + items[index].Plate = plates[items[index].VIN] + } + return nil +} + +func (s *ProductionStore) platesByVIN(ctx context.Context, vins []string) (map[string]string, error) { + if len(vins) == 0 { + return map[string]string{}, nil + } + placeholders := make([]string, len(vins)) + args := make([]any, len(vins)) + for index, vin := range vins { + placeholders[index] = "?" + args[index] = vin + } + rows, err := s.db.QueryContext(ctx, `SELECT vin, COALESCE(plate, '') FROM vehicle_identity_binding WHERE vin IN (`+strings.Join(placeholders, ",")+`)`, args...) + if err != nil { + return nil, err + } + defer rows.Close() + plates := map[string]string{} + for rows.Next() { + var vin, plate string + if err := rows.Scan(&vin, &plate); err != nil { + return nil, err + } + plates[vin] = plate + } + return plates, rows.Err() +} + func (s *ProductionStore) RawFrames(ctx context.Context, query RawFrameQuery) (Page[RawFrameRow], error) { if s.tdengine == nil { return Page[RawFrameRow]{Items: []RawFrameRow{}, Total: 0, Limit: query.Limit, Offset: query.Offset}, nil @@ -382,6 +411,9 @@ func (s *ProductionStore) RawFrames(ctx context.Context, query RawFrameQuery) (P if built.CountText == "" { total = len(items) } + if err := s.enrichRawFramePlates(ctx, items); err != nil { + return Page[RawFrameRow]{}, err + } return Page[RawFrameRow]{Items: items, Total: total, Limit: query.Limit, Offset: query.Offset}, nil } diff --git a/vehicle-data-platform/apps/web/src/api/types.ts b/vehicle-data-platform/apps/web/src/api/types.ts index 2afeefd6..82f6f3d6 100644 --- a/vehicle-data-platform/apps/web/src/api/types.ts +++ b/vehicle-data-platform/apps/web/src/api/types.ts @@ -112,6 +112,7 @@ export interface HistoryLocationRow extends RealtimeLocationRow { export interface RawFrameRow { id: string; vin: string; + plate: string; protocol: string; frameType: string; deviceTime: string; diff --git a/vehicle-data-platform/apps/web/src/pages/History.tsx b/vehicle-data-platform/apps/web/src/pages/History.tsx index 4670397f..4c901f8e 100644 --- a/vehicle-data-platform/apps/web/src/pages/History.tsx +++ b/vehicle-data-platform/apps/web/src/pages/History.tsx @@ -163,6 +163,7 @@ export function History() { columns={[ { title: 'ID', dataIndex: 'id', width: 260 }, { title: 'VIN', dataIndex: 'vin', width: 190 }, + { title: '车牌', dataIndex: 'plate', width: 120 }, { title: '协议', dataIndex: 'protocol', width: 120 }, { title: '帧类型', dataIndex: 'frameType', width: 190 }, { title: '大小 B', dataIndex: 'rawSizeBytes', width: 100 }, @@ -177,7 +178,7 @@ export function History() { setSelectedRaw(null)} width={720}> - {selectedRaw?.protocol ?? '-'} / {selectedRaw?.vin ?? '-'} / {rawFieldCount} 个字段 + {selectedRaw?.protocol ?? '-'} / {selectedRaw?.plate || selectedRaw?.vin || '-'} / {rawFieldCount} 个字段 {rawFieldCount === 0 ? ( 当前查询未返回解析字段,请勾选“返回解析字段”或配置字段裁剪后重新查询。