fix: tighten source-aware mileage projection

This commit is contained in:
lingniu
2026-07-08 15:41:59 +08:00
parent 800ca1a8b1
commit e0ae57828e
7 changed files with 500 additions and 17 deletions

View File

@@ -2,6 +2,7 @@ package stats
import (
"context"
"database/sql"
"strings"
"time"
@@ -127,6 +128,17 @@ func ProjectDailyMileage(ctx context.Context, exec Execer, vin string, statDate
statDate,
string(protocol),
)
if err != nil {
return err
}
_, err = exec.ExecContext(ctx, cleanupProjectedDailyMileageSQL,
vin,
statDate,
string(protocol),
vin,
statDate,
string(protocol),
)
return err
}
@@ -257,3 +269,70 @@ JOIN (
SET s.is_selected = 1
WHERE s.vin = ? AND s.stat_date = ? AND s.protocol = ?
`
const cleanupProjectedDailyMileageSQL = `
DELETE FROM vehicle_daily_mileage
WHERE vin = ? AND stat_date = ? AND protocol = ?
AND NOT EXISTS (
SELECT 1
FROM vehicle_daily_mileage_source
WHERE vin = ? AND stat_date = ? AND protocol = ? AND is_selected = 1
)
`
type sourceBaseline struct {
LatestTotalKM float64
LatestEventTime time.Time
}
func lookupPreviousSourceBaseline(ctx context.Context, query Queryer, vin string, statDate string, protocol envelope.Protocol, sourceKey string) (sourceBaseline, bool, error) {
if query == nil || strings.TrimSpace(vin) == "" || strings.TrimSpace(statDate) == "" || strings.TrimSpace(sourceKey) == "" {
return sourceBaseline{}, false, nil
}
previousDate, ok := previousStatDate(statDate)
if !ok {
return sourceBaseline{}, false, nil
}
rows, err := query.QueryContext(ctx, previousSourceBaselineSQL, vin, previousDate, string(protocol), sourceKey)
if err != nil {
return sourceBaseline{}, false, err
}
defer rows.Close()
if !rows.Next() {
if err := rows.Err(); err != nil {
return sourceBaseline{}, false, err
}
return sourceBaseline{}, false, nil
}
var latestTotal sql.NullFloat64
var latestEvent sql.NullTime
if err := rows.Scan(&latestTotal, &latestEvent); err != nil {
return sourceBaseline{}, false, err
}
if err := rows.Err(); err != nil {
return sourceBaseline{}, false, err
}
return sourceBaseline{
LatestTotalKM: latestTotal.Float64,
LatestEventTime: latestEvent.Time,
}, latestTotal.Valid, nil
}
func previousStatDate(statDate string) (string, bool) {
day, err := time.Parse("2006-01-02", strings.TrimSpace(statDate))
if err != nil {
return "", false
}
return day.AddDate(0, 0, -1).Format("2006-01-02"), true
}
const previousSourceBaselineSQL = `
SELECT latest_total_mileage_km, latest_event_time
FROM vehicle_daily_mileage_source
WHERE vin = ?
AND stat_date = ?
AND protocol = ?
AND source_key = ?
ORDER BY latest_event_time DESC
LIMIT 1
`