From 19008e840c7cd1141115875653ec19af537ba65d Mon Sep 17 00:00:00 2001 From: lingniu Date: Wed, 1 Jul 2026 23:06:06 +0800 Subject: [PATCH] fix: ignore invalid zero mileage metrics --- .../internal/stats/daily_metric.go | 39 ++++++++++++++++--- .../internal/stats/daily_metric_test.go | 22 +++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/go/vehicle-gateway/internal/stats/daily_metric.go b/go/vehicle-gateway/internal/stats/daily_metric.go index 468f5daa..89c1224f 100644 --- a/go/vehicle-gateway/internal/stats/daily_metric.go +++ b/go/vehicle-gateway/internal/stats/daily_metric.go @@ -78,6 +78,9 @@ func SamplesFromEnvelope(env envelope.FrameEnvelope, loc *time.Location) ([]Metr if !ok { return nil, nil } + if totalMileage <= 0 { + return nil, nil + } if loc == nil { loc = time.FixedZone("Asia/Shanghai", 8*3600) } @@ -115,14 +118,40 @@ INSERT INTO vehicle_daily_metric first_total_mileage_km, latest_total_mileage_km, sample_count, calculation_method) VALUES (?, ?, ?, ?, ?, 'km', ?, ?, 1, 'TOTAL_MILEAGE_DIFF') ON DUPLICATE KEY UPDATE - first_total_mileage_km = LEAST(first_total_mileage_km, VALUES(first_total_mileage_km)), - latest_total_mileage_km = GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km)), + first_total_mileage_km = CASE + WHEN first_total_mileage_km IS NULL OR first_total_mileage_km <= 0 + THEN VALUES(first_total_mileage_km) + ELSE LEAST(first_total_mileage_km, VALUES(first_total_mileage_km)) + END, + latest_total_mileage_km = CASE + WHEN latest_total_mileage_km IS NULL OR latest_total_mileage_km <= 0 + THEN VALUES(latest_total_mileage_km) + ELSE GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km)) + END, metric_value = CASE WHEN metric_key = 'daily_mileage_km' - THEN GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km)) - - LEAST(first_total_mileage_km, VALUES(first_total_mileage_km)) + THEN GREATEST( + CASE + WHEN latest_total_mileage_km IS NULL OR latest_total_mileage_km <= 0 + THEN VALUES(latest_total_mileage_km) + ELSE latest_total_mileage_km + END, + VALUES(latest_total_mileage_km) + ) + - CASE + WHEN first_total_mileage_km IS NULL OR first_total_mileage_km <= 0 + THEN VALUES(first_total_mileage_km) + ELSE LEAST(first_total_mileage_km, VALUES(first_total_mileage_km)) + END WHEN metric_key = 'daily_total_mileage_km' - THEN GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km)) + THEN GREATEST( + CASE + WHEN latest_total_mileage_km IS NULL OR latest_total_mileage_km <= 0 + THEN VALUES(latest_total_mileage_km) + ELSE latest_total_mileage_km + END, + VALUES(latest_total_mileage_km) + ) ELSE VALUES(metric_value) END, sample_count = sample_count + 1, diff --git a/go/vehicle-gateway/internal/stats/daily_metric_test.go b/go/vehicle-gateway/internal/stats/daily_metric_test.go index 3ec011d7..2d250be8 100644 --- a/go/vehicle-gateway/internal/stats/daily_metric_test.go +++ b/go/vehicle-gateway/internal/stats/daily_metric_test.go @@ -64,6 +64,25 @@ func TestSamplesFromEnvelopeSkipsMissingVINOrMileage(t *testing.T) { } } +func TestSamplesFromEnvelopeSkipsNonPositiveMileage(t *testing.T) { + for _, value := range []any{0, 0.0, -1.0, "0"} { + samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{ + Protocol: envelope.ProtocolJT808, + VIN: "LNBVIN00000000001", + EventTimeMS: time.Date(2026, 7, 1, 9, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(), + Fields: map[string]any{ + envelope.FieldTotalMileageKM: value, + }, + }, nil) + if err != nil { + t.Fatalf("SamplesFromEnvelope(%#v) error = %v", value, err) + } + if len(samples) != 0 { + t.Fatalf("expected no samples for non-positive mileage %#v, got %#v", value, samples) + } + } +} + func TestWriterEnsuresSchemaAndUpsertsTwoMetrics(t *testing.T) { exec := &recordingExec{} writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600)) @@ -90,6 +109,9 @@ func TestWriterEnsuresSchemaAndUpsertsTwoMetrics(t *testing.T) { if !strings.Contains(exec.calls[1].query, "ON DUPLICATE KEY UPDATE") { t.Fatalf("unexpected upsert sql: %s", exec.calls[1].query) } + if !strings.Contains(exec.calls[1].query, "first_total_mileage_km <= 0") { + t.Fatalf("upsert should ignore legacy zero first mileage: %s", exec.calls[1].query) + } } type execCall struct {