package config import ( "errors" "fmt" "sync/atomic" "time" ) // globalConfig 保存当前配置,支持原子访问 var globalConfig atomic.Pointer[Config] // Config 应用配置 type Config struct { Server ServerConfig `mapstructure:"server"` Redis RedisConfig `mapstructure:"redis"` Database DatabaseConfig `mapstructure:"database"` Queue QueueConfig `mapstructure:"queue"` Logging LoggingConfig `mapstructure:"logging"` Middleware MiddlewareConfig `mapstructure:"middleware"` SMS SMSConfig `mapstructure:"sms"` JWT JWTConfig `mapstructure:"jwt"` DefaultAdmin DefaultAdminConfig `mapstructure:"default_admin"` } // ServerConfig HTTP 服务器配置 type ServerConfig struct { Address string `mapstructure:"address"` // 例如 ":3000" ReadTimeout time.Duration `mapstructure:"read_timeout"` // 例如 "10s" WriteTimeout time.Duration `mapstructure:"write_timeout"` // 例如 "10s" ShutdownTimeout time.Duration `mapstructure:"shutdown_timeout"` // 例如 "30s" Prefork bool `mapstructure:"prefork"` // 多进程模式 } // RedisConfig Redis 连接配置 type RedisConfig struct { Address string `mapstructure:"address"` // 例如 "localhost" Port int `mapstructure:"port"` // 例如 "6379" Password string `mapstructure:"password"` // 无认证时留空 DB int `mapstructure:"db"` // 数据库编号(0-15) PoolSize int `mapstructure:"pool_size"` // 最大连接数 MinIdleConns int `mapstructure:"min_idle_conns"` // 保活连接数 DialTimeout time.Duration `mapstructure:"dial_timeout"` // 例如 "5s" ReadTimeout time.Duration `mapstructure:"read_timeout"` // 例如 "3s" WriteTimeout time.Duration `mapstructure:"write_timeout"` // 例如 "3s" } // DatabaseConfig 数据库连接配置 type DatabaseConfig struct { Host string `mapstructure:"host"` // 数据库主机地址 Port int `mapstructure:"port"` // 数据库端口 User string `mapstructure:"user"` // 数据库用户名 Password string `mapstructure:"password"` // 数据库密码(明文存储) DBName string `mapstructure:"dbname"` // 数据库名称 SSLMode string `mapstructure:"sslmode"` // SSL 模式:disable, require, verify-ca, verify-full MaxOpenConns int `mapstructure:"max_open_conns"` // 最大打开连接数(默认:25) MaxIdleConns int `mapstructure:"max_idle_conns"` // 最大空闲连接数(默认:10) ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"` // 连接最大生命周期(默认:5m) } // QueueConfig 任务队列配置 type QueueConfig struct { Concurrency int `mapstructure:"concurrency"` // Worker 并发数(默认:10) Queues map[string]int `mapstructure:"queues"` // 队列优先级配置(队列名 -> 权重) RetryMax int `mapstructure:"retry_max"` // 最大重试次数(默认:5) Timeout time.Duration `mapstructure:"timeout"` // 任务超时时间(默认:10m) } // LoggingConfig 日志配置 type LoggingConfig struct { Level string `mapstructure:"level"` // debug, info, warn, error Development bool `mapstructure:"development"` // 启用开发模式(美化输出) AppLog LogRotationConfig `mapstructure:"app_log"` // 应用日志配置 AccessLog LogRotationConfig `mapstructure:"access_log"` // HTTP 访问日志配置 } // LogRotationConfig Lumberjack 日志轮转配置 type LogRotationConfig struct { Filename string `mapstructure:"filename"` // 日志文件路径 MaxSize int `mapstructure:"max_size"` // 轮转前的最大大小(MB) MaxBackups int `mapstructure:"max_backups"` // 保留的旧文件最大数量 MaxAge int `mapstructure:"max_age"` // 保留旧文件的最大天数 Compress bool `mapstructure:"compress"` // 压缩轮转的文件 } // MiddlewareConfig 中间件配置 type MiddlewareConfig struct { EnableAuth bool `mapstructure:"enable_auth"` // 启用 keyauth 中间件 EnableRateLimiter bool `mapstructure:"enable_rate_limiter"` // 启用限流器(默认:false) RateLimiter RateLimiterConfig `mapstructure:"rate_limiter"` // 限流器配置 } // RateLimiterConfig 限流器配置 type RateLimiterConfig struct { Max int `mapstructure:"max"` // 时间窗口内的最大请求数 Expiration time.Duration `mapstructure:"expiration"` // 时间窗口(例如 "1m") Storage string `mapstructure:"storage"` // "memory" 或 "redis" } // SMSConfig 短信服务配置 type SMSConfig struct { GatewayURL string `mapstructure:"gateway_url"` // 短信网关地址 Username string `mapstructure:"username"` // 账号用户名 Password string `mapstructure:"password"` // 账号密码 Signature string `mapstructure:"signature"` // 短信签名(例如:【签名】) Timeout time.Duration `mapstructure:"timeout"` // 请求超时时间 } // JWTConfig JWT 认证配置 type JWTConfig struct { SecretKey string `mapstructure:"secret_key"` // JWT 签名密钥 TokenDuration time.Duration `mapstructure:"token_duration"` // Token 有效期(C 端 JWT) AccessTokenTTL time.Duration `mapstructure:"access_token_ttl"` // 访问令牌有效期(B 端 Redis Token) RefreshTokenTTL time.Duration `mapstructure:"refresh_token_ttl"` // 刷新令牌有效期(B 端 Redis Token) } // DefaultAdminConfig 默认超级管理员配置 type DefaultAdminConfig struct { Username string `mapstructure:"username"` Password string `mapstructure:"password"` Phone string `mapstructure:"phone"` } // Validate 验证配置值 func (c *Config) Validate() error { // 服务器验证 if c.Server.Address == "" { return fmt.Errorf("invalid configuration: server.address: must be non-empty (current value: empty)") } if c.Server.ReadTimeout < 5*time.Second || c.Server.ReadTimeout > 300*time.Second { return fmt.Errorf("invalid configuration: server.read_timeout: duration out of range (current value: %s, expected: 5s-300s)", c.Server.ReadTimeout) } if c.Server.WriteTimeout < 5*time.Second || c.Server.WriteTimeout > 300*time.Second { return fmt.Errorf("invalid configuration: server.write_timeout: duration out of range (current value: %s, expected: 5s-300s)", c.Server.WriteTimeout) } if c.Server.ShutdownTimeout < 10*time.Second || c.Server.ShutdownTimeout > 120*time.Second { return fmt.Errorf("invalid configuration: server.shutdown_timeout: duration out of range (current value: %s, expected: 10s-120s)", c.Server.ShutdownTimeout) } // Redis 验证 if c.Redis.Address == "" { return fmt.Errorf("invalid configuration: redis.address: must be non-empty (current value: empty)") } // Port 验证(独立字段) if c.Redis.Port <= 0 || c.Redis.Port > 65535 { return fmt.Errorf("invalid configuration: redis.port: port number out of range (current value: %d, expected: 1-65535)", c.Redis.Port) } if c.Redis.DB < 0 || c.Redis.DB > 15 { return fmt.Errorf("invalid configuration: redis.db: database number out of range (current value: %d, expected: 0-15)", c.Redis.DB) } if c.Redis.PoolSize <= 0 || c.Redis.PoolSize > 1000 { return fmt.Errorf("invalid configuration: redis.pool_size: pool size out of range (current value: %d, expected: 1-1000)", c.Redis.PoolSize) } if c.Redis.MinIdleConns < 0 || c.Redis.MinIdleConns > c.Redis.PoolSize { return fmt.Errorf("invalid configuration: redis.min_idle_conns: must be 0 to pool_size (current value: %d, pool_size: %d)", c.Redis.MinIdleConns, c.Redis.PoolSize) } // 日志验证 validLevels := map[string]bool{"debug": true, "info": true, "warn": true, "error": true} if !validLevels[c.Logging.Level] { return fmt.Errorf("invalid configuration: logging.level: invalid log level (current value: %s, expected: debug, info, warn, error)", c.Logging.Level) } if c.Logging.AppLog.Filename == "" { return fmt.Errorf("invalid configuration: logging.app_log.filename: must be non-empty valid file path") } if c.Logging.AccessLog.Filename == "" { return fmt.Errorf("invalid configuration: logging.access_log.filename: must be non-empty valid file path") } if c.Logging.AppLog.MaxSize < 1 || c.Logging.AppLog.MaxSize > 1000 { return fmt.Errorf("invalid configuration: logging.app_log.max_size: size out of range (current value: %d, expected: 1-1000 MB)", c.Logging.AppLog.MaxSize) } if c.Logging.AccessLog.MaxSize < 1 || c.Logging.AccessLog.MaxSize > 1000 { return fmt.Errorf("invalid configuration: logging.access_log.max_size: size out of range (current value: %d, expected: 1-1000 MB)", c.Logging.AccessLog.MaxSize) } // 中间件验证 if c.Middleware.RateLimiter.Max <= 0 { c.Middleware.RateLimiter.Max = 100 // 默认值 } if c.Middleware.RateLimiter.Max > 10000 { return fmt.Errorf("invalid configuration: middleware.rate_limiter.max: request limit out of range (current value: %d, expected: 1-10000)", c.Middleware.RateLimiter.Max) } validStorage := map[string]bool{"memory": true, "redis": true} if c.Middleware.RateLimiter.Storage != "" && !validStorage[c.Middleware.RateLimiter.Storage] { return fmt.Errorf("invalid configuration: middleware.rate_limiter.storage: invalid storage type (current value: %s, expected: memory or redis)", c.Middleware.RateLimiter.Storage) } // 短信服务验证 if c.SMS.GatewayURL == "" { return fmt.Errorf("invalid configuration: sms.gateway_url: must be non-empty (current value: empty)") } if c.SMS.Username == "" { return fmt.Errorf("invalid configuration: sms.username: must be non-empty (current value: empty)") } if c.SMS.Password == "" { return fmt.Errorf("invalid configuration: sms.password: must be non-empty (current value: empty)") } if c.SMS.Signature == "" { return fmt.Errorf("invalid configuration: sms.signature: must be non-empty (current value: empty)") } if c.SMS.Timeout < 5*time.Second || c.SMS.Timeout > 60*time.Second { return fmt.Errorf("invalid configuration: sms.timeout: duration out of range (current value: %s, expected: 5s-60s)", c.SMS.Timeout) } // JWT 验证 if c.JWT.SecretKey == "" { return fmt.Errorf("invalid configuration: jwt.secret_key: must be non-empty (current value: empty)") } if len(c.JWT.SecretKey) < 32 { return fmt.Errorf("invalid configuration: jwt.secret_key: secret key too short (current length: %d, expected: >= 32)", len(c.JWT.SecretKey)) } if c.JWT.TokenDuration < 1*time.Hour || c.JWT.TokenDuration > 720*time.Hour { return fmt.Errorf("invalid configuration: jwt.token_duration: duration out of range (current value: %s, expected: 1h-720h)", c.JWT.TokenDuration) } if c.JWT.AccessTokenTTL < 1*time.Hour || c.JWT.AccessTokenTTL > 168*time.Hour { return fmt.Errorf("invalid configuration: jwt.access_token_ttl: duration out of range (current value: %s, expected: 1h-168h)", c.JWT.AccessTokenTTL) } if c.JWT.RefreshTokenTTL < 24*time.Hour || c.JWT.RefreshTokenTTL > 720*time.Hour { return fmt.Errorf("invalid configuration: jwt.refresh_token_ttl: duration out of range (current value: %s, expected: 24h-720h)", c.JWT.RefreshTokenTTL) } return nil } // Get 返回当前配置 func Get() *Config { return globalConfig.Load() } // Set 原子地更新全局配置 func Set(cfg *Config) error { if cfg == nil { return errors.New("config cannot be nil") } if err := cfg.Validate(); err != nil { return err } globalConfig.Store(cfg) return nil }