package main import ( "context" "fmt" "log" "os" "os/signal" "strings" "syscall" "time" _ "github.com/go-sql-driver/mysql" "lingniu/vehicle-data-platform/apps/api/internal/config" "lingniu/vehicle-data-platform/apps/api/internal/platform" ) func main() { if err := run(); err != nil { log.Fatal(err) } } func run() error { cfg := config.Load() if strings.TrimSpace(cfg.MySQLDSN) == "" { return fmt.Errorf("MYSQL_DSN is required") } gateways := map[string]platform.AlertNotificationGateway{} for _, candidate := range []struct { channel string endpoint string secret string }{ {channel: "sms", endpoint: cfg.AlertNotificationSMSURL, secret: cfg.AlertNotificationSMSSecret}, {channel: "email", endpoint: cfg.AlertNotificationEmailURL, secret: cfg.AlertNotificationEmailSecret}, {channel: "wecom", endpoint: cfg.AlertNotificationWeComURL, secret: cfg.AlertNotificationWeComSecret}, } { if strings.TrimSpace(candidate.endpoint) == "" && strings.TrimSpace(candidate.secret) == "" { continue } gateway, err := platform.NewHTTPAlertNotificationGateway(candidate.endpoint, candidate.secret, cfg.AlertNotificationTimeout) if err != nil { return fmt.Errorf("%s gateway: %w", candidate.channel, err) } gateways[candidate.channel] = gateway } if len(gateways) == 0 { return fmt.Errorf("at least one ALERT_NOTIFICATION_*_URL and matching signing secret are required") } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() db, err := platform.OpenSQL(ctx, "mysql", cfg.MySQLDSN) if err != nil { return err } defer db.Close() host, _ := os.Hostname() dispatcher := platform.NewAlertNotificationDispatcher( platform.NewProductionStore(db, nil, ""), gateways, cfg.AlertNotificationBatchSize, cfg.AlertNotificationLease, "notification-dispatcher-"+host, ) poll := cfg.AlertNotificationPollInterval if poll < 250*time.Millisecond { poll = time.Second } if poll > time.Minute { poll = time.Minute } ticker := time.NewTicker(poll) defer ticker.Stop() log.Printf("alert notification dispatcher started channels=%d batch=%d poll=%s lease=%s", len(gateways), cfg.AlertNotificationBatchSize, poll, cfg.AlertNotificationLease) for { started := time.Now() result, dispatchErr := dispatcher.RunOnce(ctx) if dispatchErr != nil { log.Printf("alert notification dispatch failed claimed=%d sent=%d failed=%d duration=%s error=%v", result.Claimed, result.Sent, result.Failed, time.Since(started), dispatchErr) } else if result.Claimed > 0 { log.Printf("alert notification dispatch completed claimed=%d sent=%d failed=%d duration=%s", result.Claimed, result.Sent, result.Failed, time.Since(started)) } select { case <-ctx.Done(): log.Printf("alert notification dispatcher stopped") return nil case <-ticker.C: } } }