// Filename: internal/app/app.go package app import ( "context" "fmt" "gemini-balancer/internal/config" "gemini-balancer/internal/crypto" "gemini-balancer/internal/db/migrations" "gemini-balancer/internal/db/seeder" "gemini-balancer/internal/scheduler" "gemini-balancer/internal/service" "gemini-balancer/internal/settings" "net/http" "os" "os/signal" "syscall" "time" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "gorm.io/gorm" ) // App type App struct { Config *config.Config Router *gin.Engine DB *gorm.DB Logger *logrus.Logger CryptoService *crypto.Service // 拥有独立生命周期的后台服务 ResourceService *service.ResourceService APIKeyService *service.APIKeyService DBLogWriter *service.DBLogWriterService AnalyticsService *service.AnalyticsService HealthCheckService *service.HealthCheckService SettingsManager *settings.SettingsManager GroupManager *service.GroupManager TokenManager *service.TokenManager Scheduler *scheduler.Scheduler } // NewApp func NewApp( cfg *config.Config, router *gin.Engine, db *gorm.DB, logger *logrus.Logger, cryptoService *crypto.Service, resourceService *service.ResourceService, apiKeyService *service.APIKeyService, dbLogWriter *service.DBLogWriterService, analyticsService *service.AnalyticsService, healthCheckService *service.HealthCheckService, settingsManager *settings.SettingsManager, groupManager *service.GroupManager, tokenManager *service.TokenManager, scheduler *scheduler.Scheduler, ) *App { return &App{ Config: cfg, Router: router, DB: db, Logger: logger, CryptoService: cryptoService, ResourceService: resourceService, APIKeyService: apiKeyService, DBLogWriter: dbLogWriter, AnalyticsService: analyticsService, HealthCheckService: healthCheckService, SettingsManager: settingsManager, GroupManager: groupManager, TokenManager: tokenManager, Scheduler: scheduler, } } // Run 启动流程现在由App主动编排,而非被动接受 func (a *App) Run() error { // --- 阶段一: (数据库设置) --- a.Logger.Info("* [SYSTEM] * Preparing: Database Setup ...") // 步骤 1: (运行基础迁移,确保表存在) if err := migrations.RunMigrations(a.DB, a.Logger); err != nil { return fmt.Errorf("initial database migration failed: %w", err) } // 步骤 2: (运行所有版本化的数据迁移) if err := migrations.RunVersionedMigrations(a.DB, a.Config, a.Logger); err != nil { return fmt.Errorf("failed to run versioned migrations: %w", err) } // 步骤 3: (数据播种) seeder.RunSeeder(a.DB, a.CryptoService, a.Logger) a.Logger.Info("* [SYSTEM] * All Uitls READY. ---") // --- 阶段二: (启动后台服务) --- a.Logger.Info("* [SYSTEM] * Starting main: Background Services ") a.APIKeyService.Start() a.DBLogWriter.Start() a.AnalyticsService.Start() a.HealthCheckService.Start() a.Scheduler.Start() a.Logger.Info("* [SYSTEM] * All Background Services are RUNNING. ") // --- 阶段三: (优雅关机) --- defer a.Scheduler.Stop() defer a.HealthCheckService.Stop() defer a.AnalyticsService.Stop() defer a.APIKeyService.Stop() defer a.DBLogWriter.Stop() defer a.SettingsManager.Stop() defer a.TokenManager.Stop() // --- 阶段四: (HTTP服务器) --- serverAddr := fmt.Sprintf("0.0.0.0:%s", a.Config.Server.Port) srv := &http.Server{ Addr: serverAddr, Handler: a.Router, } go func() { a.Logger.Infof("* [SYSTEM] * HTTP Server Now Listening on %s ", serverAddr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { a.Logger.Fatalf("HTTP server listen error: %s\n", err) } }() // --- 阶段五: (处理关机信号) --- quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit a.Logger.Info("* [SYSTEM] * Shutdown signal received. Executing strategic retreat...") ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() if err := srv.Shutdown(ctx); err != nil { a.Logger.Fatal("Server forced to shutdown:", err) } a.Logger.Info("* [SYSTEM] * All units have ceased operations. Mission complete. ") return nil }