From cc89e0d537084478d2941ece83f9dc62421b66bc Mon Sep 17 00:00:00 2001 From: lingniu Date: Thu, 2 Jul 2026 00:24:53 +0800 Subject: [PATCH] fix: filter raw frames by vehicle key --- go/vehicle-gateway/internal/history/query.go | 42 ++++++++------- .../internal/history/query_test.go | 53 ++++++++++++++++--- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/go/vehicle-gateway/internal/history/query.go b/go/vehicle-gateway/internal/history/query.go index 0b659e0c..16062cda 100644 --- a/go/vehicle-gateway/internal/history/query.go +++ b/go/vehicle-gateway/internal/history/query.go @@ -17,15 +17,16 @@ type Queryer interface { } type RawFrameQuery struct { - Protocol string - VIN string - Phone string - DeviceID string - MessageID string - DateFrom string - DateTo string - Limit int - Offset int + Protocol string + VehicleKey string + VIN string + Phone string + DeviceID string + MessageID string + DateFrom string + DateTo string + Limit int + Offset int } type RawFrameRow struct { @@ -123,6 +124,7 @@ func (r *RawFrameRepository) tableName() string { func normalizeRawFrameQuery(query RawFrameQuery) RawFrameQuery { query.Protocol = strings.ToUpper(strings.TrimSpace(query.Protocol)) + query.VehicleKey = strings.TrimSpace(query.VehicleKey) query.VIN = strings.TrimSpace(query.VIN) query.Phone = strings.TrimSpace(query.Phone) query.DeviceID = strings.TrimSpace(query.DeviceID) @@ -143,6 +145,9 @@ func buildRawFrameSQL(table string, query RawFrameQuery) (string, []any) { if query.Protocol != "" { add("protocol = '" + quote(query.Protocol) + "'") } + if query.VehicleKey != "" { + add("vehicle_key = '" + quote(query.VehicleKey) + "'") + } if query.VIN != "" { add("vin = '" + quote(query.VIN) + "'") } @@ -221,15 +226,16 @@ func parseRawFrameQuery(r *http.Request) (RawFrameQuery, error) { return RawFrameQuery{}, err } query := RawFrameQuery{ - Protocol: values.Get("protocol"), - VIN: values.Get("vin"), - Phone: values.Get("phone"), - DeviceID: values.Get("deviceId"), - MessageID: values.Get("messageId"), - DateFrom: values.Get("dateFrom"), - DateTo: values.Get("dateTo"), - Limit: limit, - Offset: offset, + Protocol: values.Get("protocol"), + VehicleKey: values.Get("vehicleKey"), + VIN: values.Get("vin"), + Phone: values.Get("phone"), + DeviceID: values.Get("deviceId"), + MessageID: values.Get("messageId"), + DateFrom: values.Get("dateFrom"), + DateTo: values.Get("dateTo"), + Limit: limit, + Offset: offset, } if !validDateTime(query.DateFrom) || !validDateTime(query.DateTo) { return RawFrameQuery{}, errors.New("dateFrom/dateTo must use YYYY-MM-DD or YYYY-MM-DD HH:mm:ss") diff --git a/go/vehicle-gateway/internal/history/query_test.go b/go/vehicle-gateway/internal/history/query_test.go index 0a4c990c..dd6a9c1f 100644 --- a/go/vehicle-gateway/internal/history/query_test.go +++ b/go/vehicle-gateway/internal/history/query_test.go @@ -94,6 +94,43 @@ func TestRawFrameHandlerReturnsRawFrames(t *testing.T) { } } +func TestRawFrameHandlerFiltersByVehicleKey(t *testing.T) { + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("sqlmock.New() error = %v", err) + } + defer db.Close() + mock.ExpectQuery("vehicle_key = 'JT808:013307811350'"). + WillReturnRows(sqlmock.NewRows([]string{ + "ts", "frame_id", "event_id", "message_id", "event_time", "received_at", "raw_size_bytes", + "raw_hex", "raw_text", "parsed_json", "fields_json", "parse_status", "parse_error", "source_endpoint", + "protocol", "vehicle_key", "vin", "phone", "device_id", + }).AddRow( + "2026-07-02 00:18:22", "go_frame", "event-3", 0x0200, "2026-07-02 00:18:22", "2026-07-02 00:22:43", + 63, "7E0200", "", `{"header":{"message_id":"0x0200"}}`, `{"total_mileage_km":8792.8}`, + "OK", "", "115.231.168.135:22170", "JT808", "JT808:013307811350", "", "013307811350", "", + )) + + handler := NewRawFrameHandler(NewRawFrameRepository(db, "lingniu_vehicle_ts")) + request := httptest.NewRequest(http.MethodGet, "/api/history/raw-frames?vehicleKey=JT808:013307811350&protocol=JT808&limit=1", nil) + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusOK { + t.Fatalf("status = %d body=%s", response.Code, response.Body.String()) + } + body := response.Body.String() + for _, want := range []string{`"vehicle_key":"JT808:013307811350"`, `"phone":"013307811350"`, `"total":1`} { + if !strings.Contains(body, want) { + t.Fatalf("response missing %s: %s", want, body) + } + } + if err := mock.ExpectationsWereMet(); err != nil { + t.Fatalf("sql expectations: %v", err) + } +} + func TestRawFrameHandlerRejectsInvalidLimit(t *testing.T) { handler := NewRawFrameHandler(NewRawFrameRepository(&sql.DB{}, "")) request := httptest.NewRequest(http.MethodGet, "/api/history/raw-frames?limit=501", nil) @@ -121,19 +158,21 @@ func TestParseMessageIDSupportsDecimalAndHex(t *testing.T) { func TestBuildRawFrameSQLUsesLiteralsForTDengine(t *testing.T) { sqlText, args := buildRawFrameSQL("lingniu_vehicle_ts.raw_frames", RawFrameQuery{ - Protocol: "JT808", - VIN: "VIN'1", - MessageID: "0x0200", - DateFrom: "2026-07-01 00:00:00", - DateTo: "2026-07-01 23:59:59", - Limit: 20, - Offset: 5, + Protocol: "JT808", + VehicleKey: "JT808:013307811350", + VIN: "VIN'1", + MessageID: "0x0200", + DateFrom: "2026-07-01 00:00:00", + DateTo: "2026-07-01 23:59:59", + Limit: 20, + Offset: 5, }) if len(args) != 0 { t.Fatalf("expected no query args for TDengine, got %#v", args) } for _, want := range []string{ "protocol = 'JT808'", + "vehicle_key = 'JT808:013307811350'", "vin = 'VIN''1'", "message_id = 512", "LIMIT 20 OFFSET 5",