feat: 推广 V3.5 实时氢耗并完善导出

This commit is contained in:
lingniu
2026-09-04 15:42:02 +08:00
parent a26179c8fe
commit e402eb5e60
53 changed files with 2855 additions and 320 deletions
@@ -26,6 +26,7 @@ func main() {
noise := flag.Float64("hydrogen-noise-kg", envFloat("OPEN_STAT_HYDROGEN_NOISE_KG", 0.05), "ignored mass jitter")
maxDrop := flag.Float64("hydrogen-max-drop-kg", envFloat("OPEN_STAT_HYDROGEN_MAX_DROP_KG", 20), "maximum accepted drop between samples")
vin := flag.String("vin", "", "optional 17-character VIN for a scoped rebuild")
brand := flag.String("brand", "", "optional exact vehicle brand for an atomic scoped rebuild")
vinWorkers := flag.Int("vin-workers", envInt("OPEN_STAT_VIN_WORKERS", 4), "parallel per-VIN queries for an all-vehicle day")
allDates := flag.Bool("all-dates", false, "rebuild every available completed event date; optionally scoped by -vin")
dryRun := flag.Bool("dry-run", false, "calculate and print results without changing MySQL")
@@ -62,9 +63,16 @@ func main() {
log.Fatal("-vin-workers must be between 1 and 16")
}
normalizedVIN := strings.ToUpper(strings.TrimSpace(*vin))
normalizedBrand := strings.TrimSpace(*brand)
if normalizedVIN != "" && normalizedBrand != "" {
log.Fatal("-vin and -brand cannot be combined")
}
if *seedStream && normalizedVIN != "" {
log.Fatal("-seed-stream-state requires an all-vehicle rebuild")
}
if *seedStream && normalizedBrand != "" {
log.Fatal("-seed-stream-state cannot be combined with -brand")
}
if *seedStream && *dryRun {
log.Fatal("-seed-stream-state cannot be combined with -dry-run")
}
@@ -84,6 +92,29 @@ func main() {
log.Fatalf("no active hydrogen tank capacity for VIN %s", normalizedVIN)
}
}
var brandVINs []string
if normalizedBrand != "" {
brandVINs, err = openplatform.LoadHydrogenBrandVINs(ctx, mysqlDB, normalizedBrand)
if err != nil {
log.Fatalf("load VINs for brand %q: %v", normalizedBrand, err)
}
if len(brandVINs) == 0 {
log.Fatalf("no vehicles found for brand %q", normalizedBrand)
}
brandSet := make(map[string]struct{}, len(brandVINs))
for _, brandVIN := range brandVINs {
brandSet[brandVIN] = struct{}{}
}
for capacityVIN := range capacities {
if _, ok := brandSet[capacityVIN]; !ok {
delete(capacities, capacityVIN)
}
}
if len(capacities) == 0 {
log.Fatalf("no active hydrogen tank capacities found for brand %q", normalizedBrand)
}
fmt.Printf("brand=%s scoped_vins=%d capacity_vins=%d\n", normalizedBrand, len(brandVINs), len(capacities))
}
startDate := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 0, 0, 0, 0, location).AddDate(0, 0, -(*lookback - 1))
if *allDates {
var first, last time.Time
@@ -112,6 +143,8 @@ func main() {
scope := "all-vehicles"
if normalizedVIN != "" {
scope = normalizedVIN
} else if normalizedBrand != "" {
scope = "brand:" + normalizedBrand
}
fmt.Printf("scope=%s date_from=%s date_to=%s mode=all-dates\n", scope, startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
}
@@ -141,6 +174,8 @@ func main() {
if !*dryRun {
if *seedStream {
err = openplatform.ReplaceHydrogenDailyStatsAndSeedStream(ctx, mysqlDB, date, stats)
} else if normalizedBrand != "" {
err = openplatform.ReplaceHydrogenDailyStatsForVINs(ctx, mysqlDB, date, brandVINs, stats)
} else if normalizedVIN == "" {
err = openplatform.ReplaceHydrogenDailyStats(ctx, mysqlDB, date, stats)
} else {