fix: ignore invalid zero mileage metrics

This commit is contained in:
lingniu
2026-07-01 23:06:06 +08:00
parent c803a19bf5
commit 19008e840c
2 changed files with 56 additions and 5 deletions

View File

@@ -78,6 +78,9 @@ func SamplesFromEnvelope(env envelope.FrameEnvelope, loc *time.Location) ([]Metr
if !ok { if !ok {
return nil, nil return nil, nil
} }
if totalMileage <= 0 {
return nil, nil
}
if loc == nil { if loc == nil {
loc = time.FixedZone("Asia/Shanghai", 8*3600) 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) first_total_mileage_km, latest_total_mileage_km, sample_count, calculation_method)
VALUES (?, ?, ?, ?, ?, 'km', ?, ?, 1, 'TOTAL_MILEAGE_DIFF') VALUES (?, ?, ?, ?, ?, 'km', ?, ?, 1, 'TOTAL_MILEAGE_DIFF')
ON DUPLICATE KEY UPDATE ON DUPLICATE KEY UPDATE
first_total_mileage_km = LEAST(first_total_mileage_km, VALUES(first_total_mileage_km)), first_total_mileage_km = CASE
latest_total_mileage_km = GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km)), 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 metric_value = CASE
WHEN metric_key = 'daily_mileage_km' WHEN metric_key = 'daily_mileage_km'
THEN GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km)) THEN GREATEST(
- LEAST(first_total_mileage_km, VALUES(first_total_mileage_km)) 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' 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) ELSE VALUES(metric_value)
END, END,
sample_count = sample_count + 1, sample_count = sample_count + 1,

View File

@@ -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) { func TestWriterEnsuresSchemaAndUpsertsTwoMetrics(t *testing.T) {
exec := &recordingExec{} exec := &recordingExec{}
writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600)) 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") { if !strings.Contains(exec.calls[1].query, "ON DUPLICATE KEY UPDATE") {
t.Fatalf("unexpected upsert sql: %s", exec.calls[1].query) 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 { type execCall struct {