package main import ( "context" "os" "os/signal" "strconv" "syscall" "github.com/bytedance/sonic" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/google/uuid" "github.com/redis/go-redis/v9" "go.uber.org/zap" "gorm.io/gorm" "github.com/break/junhong_cmp_fiber/internal/handler" internalMiddleware "github.com/break/junhong_cmp_fiber/internal/middleware" "github.com/break/junhong_cmp_fiber/internal/routes" accountSvc "github.com/break/junhong_cmp_fiber/internal/service/account" permissionSvc "github.com/break/junhong_cmp_fiber/internal/service/permission" roleSvc "github.com/break/junhong_cmp_fiber/internal/service/role" "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/config" "github.com/break/junhong_cmp_fiber/pkg/database" "github.com/break/junhong_cmp_fiber/pkg/logger" "github.com/break/junhong_cmp_fiber/pkg/queue" ) func main() { // 1. 初始化配置 cfg := initConfig() // 2. 初始化日志 appLogger := initLogger(cfg) defer func() { _ = logger.Sync() }() // 3. 初始化数据库 db := initDatabase(cfg, appLogger) defer closeDatabase(db, appLogger) // 4. 初始化 Redis redisClient := initRedis(cfg, appLogger) defer closeRedis(redisClient, appLogger) // 5. 初始化队列客户端 queueClient := initQueue(redisClient, appLogger) defer closeQueue(queueClient, appLogger) // 6. 初始化 Services services := initServices(db, redisClient, appLogger) // 7. 启动配置监听器 watchCtx, cancelWatch := context.WithCancel(context.Background()) defer cancelWatch() go config.Watch(watchCtx, appLogger) // 8. 创建 Fiber 应用 app := createFiberApp(cfg, appLogger) // 9. 注册中间件 initMiddleware(app, cfg, appLogger) // 10. 注册路由 initRoutes(app, cfg, services, queueClient, db, redisClient, appLogger) // 11. 启动服务器 startServer(app, cfg, appLogger, cancelWatch) } // initConfig 加载配置 func initConfig() *config.Config { cfg, err := config.Load() if err != nil { panic("加载配置失败: " + err.Error()) } return cfg } // initLogger 初始化日志 func initLogger(cfg *config.Config) *zap.Logger { if err := logger.InitLoggers( cfg.Logging.Level, cfg.Logging.Development, logger.LogRotationConfig{ Filename: cfg.Logging.AppLog.Filename, MaxSize: cfg.Logging.AppLog.MaxSize, MaxBackups: cfg.Logging.AppLog.MaxBackups, MaxAge: cfg.Logging.AppLog.MaxAge, Compress: cfg.Logging.AppLog.Compress, }, logger.LogRotationConfig{ Filename: cfg.Logging.AccessLog.Filename, MaxSize: cfg.Logging.AccessLog.MaxSize, MaxBackups: cfg.Logging.AccessLog.MaxBackups, MaxAge: cfg.Logging.AccessLog.MaxAge, Compress: cfg.Logging.AccessLog.Compress, }, ); err != nil { panic("初始化日志失败: " + err.Error()) } appLogger := logger.GetAppLogger() appLogger.Info("应用程序启动中...", zap.String("address", cfg.Server.Address)) return appLogger } // initDatabase 初始化数据库连接 func initDatabase(cfg *config.Config, appLogger *zap.Logger) *gorm.DB { db, err := database.InitPostgreSQL(&cfg.Database, appLogger) if err != nil { appLogger.Fatal("初始化 PostgreSQL 失败", zap.Error(err)) } return db } // closeDatabase 关闭数据库连接 func closeDatabase(db *gorm.DB, appLogger *zap.Logger) { sqlDB, _ := db.DB() if sqlDB != nil { if err := sqlDB.Close(); err != nil { appLogger.Error("关闭 PostgreSQL 连接失败", zap.Error(err)) } } } // initRedis 初始化 Redis 连接 func initRedis(cfg *config.Config, appLogger *zap.Logger) *redis.Client { redisAddr := cfg.Redis.Address + ":" + strconv.Itoa(cfg.Redis.Port) redisClient := redis.NewClient(&redis.Options{ Addr: redisAddr, Password: cfg.Redis.Password, DB: cfg.Redis.DB, PoolSize: cfg.Redis.PoolSize, MinIdleConns: cfg.Redis.MinIdleConns, DialTimeout: cfg.Redis.DialTimeout, ReadTimeout: cfg.Redis.ReadTimeout, WriteTimeout: cfg.Redis.WriteTimeout, }) // 测试连接 ctx := context.Background() if err := redisClient.Ping(ctx).Err(); err != nil { appLogger.Fatal("连接 Redis 失败", zap.Error(err)) } appLogger.Info("Redis 已连接", zap.String("address", redisAddr)) return redisClient } // closeRedis 关闭 Redis 连接 func closeRedis(redisClient *redis.Client, appLogger *zap.Logger) { if err := redisClient.Close(); err != nil { appLogger.Error("关闭 Redis 客户端失败", zap.Error(err)) } } // initQueue 初始化队列客户端 func initQueue(redisClient *redis.Client, appLogger *zap.Logger) *queue.Client { return queue.NewClient(redisClient, appLogger) } // closeQueue 关闭队列客户端 func closeQueue(queueClient *queue.Client, appLogger *zap.Logger) { if err := queueClient.Close(); err != nil { appLogger.Error("关闭 Asynq 客户端失败", zap.Error(err)) } } // initServices 初始化所有 Services func initServices(db *gorm.DB, redisClient *redis.Client, appLogger *zap.Logger) *routes.Services { // 初始化 RBAC Store 层 accountStore := postgres.NewAccountStore(db, redisClient) roleStore := postgres.NewRoleStore(db) permissionStore := postgres.NewPermissionStore(db) accountRoleStore := postgres.NewAccountRoleStore(db) rolePermissionStore := postgres.NewRolePermissionStore(db) // 初始化 RBAC Service 层 accountService := accountSvc.New(accountStore, roleStore, accountRoleStore) roleService := roleSvc.New(roleStore, permissionStore, rolePermissionStore) permissionService := permissionSvc.New(permissionStore) // 初始化 Handler 层 accountHandler := handler.NewAccountHandler(accountService) roleHandler := handler.NewRoleHandler(roleService) permissionHandler := handler.NewPermissionHandler(permissionService) return &routes.Services{ AccountHandler: accountHandler, RoleHandler: roleHandler, PermissionHandler: permissionHandler, } } // createFiberApp 创建 Fiber 应用 func createFiberApp(cfg *config.Config, appLogger *zap.Logger) *fiber.App { return fiber.New(fiber.Config{ AppName: "君鸿卡管系统 v1.0.0", StrictRouting: true, CaseSensitive: true, JSONEncoder: sonic.Marshal, JSONDecoder: sonic.Unmarshal, Prefork: cfg.Server.Prefork, ReadTimeout: cfg.Server.ReadTimeout, WriteTimeout: cfg.Server.WriteTimeout, ErrorHandler: internalMiddleware.ErrorHandler(appLogger), }) } // initMiddleware 注册中间件 func initMiddleware(app *fiber.App, cfg *config.Config, appLogger *zap.Logger) { // 1. Recover - 必须第一个,捕获所有 panic app.Use(internalMiddleware.Recover(appLogger)) // 2. RequestID - 为每个请求生成唯一 ID app.Use(requestid.New(requestid.Config{ Generator: func() string { return uuid.NewString() }, })) // 3. Logger - 记录所有请求 app.Use(logger.Middleware()) // 4. Compress - 响应压缩 app.Use(compress.New(compress.Config{ Level: compress.LevelDefault, })) } // initRoutes 注册路由 func initRoutes(app *fiber.App, cfg *config.Config, services *routes.Services, queueClient *queue.Client, db *gorm.DB, redisClient *redis.Client, appLogger *zap.Logger) { // 注册模块化路由 routes.RegisterRoutes(app, services) // API v1 路由组(用于受保护的端点) v1 := app.Group("/api/v1") // 可选:启用认证中间件 if cfg.Middleware.EnableAuth { // TODO: 配置 TokenValidator appLogger.Info("认证中间件已启用") } // 可选:启用限流器 if cfg.Middleware.EnableRateLimiter { initRateLimiter(v1, cfg, appLogger) } } // initRateLimiter 初始化限流器 func initRateLimiter(router fiber.Router, cfg *config.Config, appLogger *zap.Logger) { var rateLimitStorage fiber.Storage if cfg.Middleware.RateLimiter.Storage == "redis" { rateLimitStorage = internalMiddleware.NewRedisStorage( cfg.Redis.Address, cfg.Redis.Password, cfg.Redis.DB, cfg.Redis.Port, ) appLogger.Info("限流器使用 Redis 存储", zap.String("redis_address", cfg.Redis.Address)) } else { rateLimitStorage = nil appLogger.Info("限流器使用内存存储") } router.Use(internalMiddleware.RateLimiter( cfg.Middleware.RateLimiter.Max, cfg.Middleware.RateLimiter.Expiration, rateLimitStorage, )) } // startServer 启动服务器 func startServer(app *fiber.App, cfg *config.Config, appLogger *zap.Logger, cancelWatch context.CancelFunc) { // 优雅关闭 quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt, syscall.SIGTERM) go func() { if err := app.Listen(cfg.Server.Address); err != nil { appLogger.Fatal("服务器启动失败", zap.Error(err)) } }() appLogger.Info("服务器已启动", zap.String("address", cfg.Server.Address)) // 等待关闭信号 <-quit appLogger.Info("正在关闭服务器...") // 取消配置监听器 cancelWatch() // 关闭 HTTP 服务器 if err := app.ShutdownWithTimeout(cfg.Server.ShutdownTimeout); err != nil { appLogger.Error("强制关闭服务器", zap.Error(err)) } appLogger.Info("服务器已停止") }