fix: ignore invalid zero mileage metrics
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user