diff --git a/cmd/service-radar/main.go b/cmd/service-radar/main.go index d2ca8ea..12dc16a 100644 --- a/cmd/service-radar/main.go +++ b/cmd/service-radar/main.go @@ -7,6 +7,7 @@ import ( "os/signal" "syscall" "weatherstation/internal/radar" + "weatherstation/internal/rain" "weatherstation/internal/server" ) @@ -21,6 +22,11 @@ func main() { log.Fatalf("service-radar start error: %v", err) } + // Also start CMPA hourly rain scheduler (StoreToDB=true; tiles/dir/url from env inside package) + if err := rain.Start(ctx, rain.Options{StoreToDB: true}); err != nil { + log.Fatalf("service-rain (embedded) start error: %v", err) + } + // Keep process alive until signal <-ctx.Done() log.Println("service-radar shutting down") diff --git a/internal/rain/scheduler.go b/internal/rain/scheduler.go index bad38d6..7026bd9 100644 --- a/internal/rain/scheduler.go +++ b/internal/rain/scheduler.go @@ -121,14 +121,23 @@ func runOnce(ctx context.Context, opts Options, loc *time.Location) error { func downloadAndStoreTile(ctx context.Context, local time.Time, dateStr, hh, mm string, z, y, x int, opts Options) error { url := fmt.Sprintf("https://image.data.cma.cn/tiles/China/CMPA_RT_China_0P01_HOR-PRE_GISJPG_Tiles/%s/%s/%s/%d/%d/%d.bin", dateStr, hh, mm, z, y, x) - // skip if exists in DB + // skip if exists in DB; dt source configurable (default: local slot time) + dtSource := strings.ToLower(getenvDefault("RAIN_DT_SOURCE", "local")) // local|url + var product string + var dtForKey time.Time if ref, err := ParseCMPATileURL(url); err == nil { - exists, err := databaseHas(ctx, ref.Product, ref.DT, z, y, x) + product = ref.Product + if dtSource == "url" { + dtForKey = ref.DT + } else { + dtForKey = local + } + exists, err := databaseHas(ctx, product, dtForKey, z, y, x) if err != nil { return err } if exists { - log.Printf("[rain] skip: already in DB z=%d y=%d x=%d dt(local)=%s url=%s", z, y, x, ref.DT.Format("2006-01-02 15:04"), url) + log.Printf("[rain] skip: already in DB z=%d y=%d x=%d dt(%s)=%s url=%s", z, y, x, dtSource, dtForKey.Format("2006-01-02 15:04"), url) return nil } } @@ -150,10 +159,30 @@ func downloadAndStoreTile(ctx context.Context, local time.Time, dateStr, hh, mm if rerr != nil { return fmt.Errorf("read saved tile: %w", rerr) } - if err := StoreTileBytes(ctx, url, b); err != nil { + // Determine product and dt according to dtSource + if product == "" { + if ref, e := ParseCMPATileURL(url); e == nil { + product = ref.Product + if dtSource == "url" { + dtForKey = ref.DT + } else { + dtForKey = local + } + } + } + if product == "" { + return fmt.Errorf("cannot parse product from url for DB store") + } + if dtForKey.IsZero() { + if dtSource == "url" { + return fmt.Errorf("dt source=url but failed to parse dt") + } + dtForKey = local + } + if err := database.UpsertRainTile(ctx, database.GetDB(), product, dtForKey, z, y, x, 256, 256, b); err != nil { return fmt.Errorf("store tile db: %w", err) } - log.Printf("[rain] stored to DB: %s", fname) + log.Printf("[rain] stored to DB: %s (dt=%s, source=%s)", fname, dtForKey.Format("2006-01-02 15:04:05"), dtSource) } return nil } diff --git a/internal/rain/store.go b/internal/rain/store.go index f5f59ad..0268c20 100644 --- a/internal/rain/store.go +++ b/internal/rain/store.go @@ -3,6 +3,8 @@ package rain import ( "context" "fmt" + "log" + "os" "path" "regexp" "strconv" @@ -65,6 +67,15 @@ func StoreTileBytes(ctx context.Context, urlOrPath string, data []byte) error { if err != nil { return err } + // 可选:强制 +8 小时(若上游传入的时间已为 UTC 且未转换,可用此开关修正) + if strings.EqualFold(strings.TrimSpace(strings.ToLower(os.Getenv("RAIN_FORCE_SHIFT8"))), "1") { + ref.DT = ref.DT.Add(8 * time.Hour) + } + // 可选调试:打印解析出的时间(CST)与 URL + if strings.EqualFold(strings.TrimSpace(strings.ToLower(os.Getenv("RAIN_DEBUG"))), "1") { + log.Printf("[rain] store tile: url=%s -> product=%s dt(local)=%s z=%d y=%d x=%d", + urlOrPath, ref.Product, ref.DT.Format("2006-01-02 15:04:05 -0700"), ref.Z, ref.Y, ref.X) + } db := database.GetDB() return database.UpsertRainTile(ctx, db, ref.Product, ref.DT, ref.Z, ref.Y, ref.X, 256, 256, data) }