diff --git a/scripts/generate_daily_report.go b/scripts/generate_daily_report.go index fe7659a..6a0e6de 100644 --- a/scripts/generate_daily_report.go +++ b/scripts/generate_daily_report.go @@ -85,7 +85,10 @@ func run() error { } defer db.Close() - date := time.Now().Format("2006-01-02") + date, err := resolveReportDate(time.Now(), os.Args[1:], os.Getenv("REPORT_DATE")) + if err != nil { + return err + } // 1. 获取报告数据(使用新schema) report, err := generateReportDataV3(db, date) @@ -133,6 +136,35 @@ func run() error { return nil } +func resolveReportDate(now time.Time, args []string, envDate string) (string, error) { + date := strings.TrimSpace(envDate) + + for i := 0; i < len(args); i++ { + switch { + case args[i] == "-date" || args[i] == "--date": + if i+1 >= len(args) { + return "", fmt.Errorf("缺少 -date 参数值,期望格式 YYYY-MM-DD") + } + date = strings.TrimSpace(args[i+1]) + i++ + case strings.HasPrefix(args[i], "-date="): + date = strings.TrimSpace(strings.TrimPrefix(args[i], "-date=")) + case strings.HasPrefix(args[i], "--date="): + date = strings.TrimSpace(strings.TrimPrefix(args[i], "--date=")) + } + } + + if date == "" { + return now.Format("2006-01-02"), nil + } + + parsed, err := time.Parse("2006-01-02", date) + if err != nil { + return "", fmt.Errorf("无效报告日期 %q,期望格式 YYYY-MM-DD", date) + } + return parsed.Format("2006-01-02"), nil +} + // ============ 数据模型 ============ const ( diff --git a/scripts/generate_daily_report_test.go b/scripts/generate_daily_report_test.go index f61fd4c..7154fc6 100644 --- a/scripts/generate_daily_report_test.go +++ b/scripts/generate_daily_report_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "testing" + "time" ) func sampleReportForV1() *ReportV3 { @@ -180,6 +181,52 @@ func TestBuildFreeSourceBreakdown(t *testing.T) { } } +func TestResolveReportDateDefaultsToToday(t *testing.T) { + got, err := resolveReportDate(time.Date(2026, 5, 14, 9, 0, 0, 0, time.FixedZone("CST", 8*3600)), nil, "") + if err != nil { + t.Fatalf("resolveReportDate returned error: %v", err) + } + if got != "2026-05-14" { + t.Fatalf("date = %q, want %q", got, "2026-05-14") + } +} + +func TestResolveReportDateUsesEnvDate(t *testing.T) { + got, err := resolveReportDate(time.Now(), nil, "2026-05-09") + if err != nil { + t.Fatalf("resolveReportDate returned error: %v", err) + } + if got != "2026-05-09" { + t.Fatalf("date = %q, want %q", got, "2026-05-09") + } +} + +func TestResolveReportDateCLIOverridesEnv(t *testing.T) { + got, err := resolveReportDate(time.Now(), []string{"-date", "2024-06-05"}, "2026-05-09") + if err != nil { + t.Fatalf("resolveReportDate returned error: %v", err) + } + if got != "2024-06-05" { + t.Fatalf("date = %q, want %q", got, "2024-06-05") + } +} + +func TestResolveReportDateSupportsEqualsSyntax(t *testing.T) { + got, err := resolveReportDate(time.Now(), []string{"--date=2024-10-25"}, "") + if err != nil { + t.Fatalf("resolveReportDate returned error: %v", err) + } + if got != "2024-10-25" { + t.Fatalf("date = %q, want %q", got, "2024-10-25") + } +} + +func TestResolveReportDateRejectsInvalidDate(t *testing.T) { + if _, err := resolveReportDate(time.Now(), []string{"-date", "2026/05/09"}, ""); err == nil { + t.Fatalf("expected invalid date error") + } +} + func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) { report := sampleReportForV1() report.ModelEvents = []ModelEvent{ diff --git a/scripts/rebuild_historical_report.sh b/scripts/rebuild_historical_report.sh new file mode 100755 index 0000000..222d370 --- /dev/null +++ b/scripts/rebuild_historical_report.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +if [[ $# -lt 1 ]]; then + echo "用法: scripts/rebuild_historical_report.sh YYYY-MM-DD" >&2 + exit 1 +fi + +REPORT_DATE="$1" +shift || true + +if [[ -f ".env.local" ]]; then + # shellcheck disable=SC1091 + source ".env.local" +fi +if [[ -f ".env" ]]; then + # shellcheck disable=SC1091 + source ".env" +fi + +REPORT_DATE="$REPORT_DATE" go run -tags llm_script ./scripts/generate_daily_report.go "$@"