package config import ( "fmt" "log" "sync" "sync/atomic" "github.com/spf13/viper" ) // Config 应用配置结构 type Config struct { Server ServerConfig `mapstructure:"server"` Fanqie FanqieConfig `mapstructure:"fanqie"` Cache CacheConfig `mapstructure:"cache"` DeviceKeyPool DeviceKeyPoolConfig `mapstructure:"device_key_pool"` } // ServerConfig 服务器配置 type ServerConfig struct { Port string `mapstructure:"port"` Host string `mapstructure:"host"` AccessKey string `mapstructure:"access_key"` ProtectAll bool `mapstructure:"protect_all"` // 业务接口也要求访问密钥(公网部署建议开启) } // FanqieConfig 番茄小说配置 type FanqieConfig struct { Algorithm string `mapstructure:"algorithm"` Signature string `mapstructure:"signature"` // 签名方法: sign2 / sign4 Content ContentConfig `mapstructure:"content"` Manga MangaConfig `mapstructure:"manga"` } // ContentConfig 内容处理配置 type ContentConfig struct { RemoveImages bool `mapstructure:"remove_images"` RemoveImageDesc bool `mapstructure:"remove_image_desc"` RemoveBlankLines bool `mapstructure:"remove_blank_lines"` RemoveHTMLTags bool `mapstructure:"remove_html_tags"` RemoveChapterTitle bool `mapstructure:"remove_chapter_title"` AddParagraphIndent bool `mapstructure:"add_paragraph_indent"` DoubleNewline bool `mapstructure:"double_newline"` // 段落间使用双换行 EnableReview bool `mapstructure:"enable_review"` // 是否启用段评功能 } // MangaConfig 漫画处理配置 type MangaConfig struct { OutputMode string `mapstructure:"output_mode"` // 输出模式: data_uri / base64 / url } // CacheConfig 缓存配置 type CacheConfig struct { Enabled bool `mapstructure:"enabled"` Backend string `mapstructure:"backend"` // 存储后端: sqlite | postgres SQLitePath string `mapstructure:"sqlite_path"` Postgres PostgresConfig `mapstructure:"postgres"` Redis RedisConfig `mapstructure:"redis"` CleanupEnabled bool `mapstructure:"cleanup_enabled"` // 是否启用缓存清理 CleanupSeconds int `mapstructure:"cleanup_seconds"` // 缓存清理时间(秒) } // PostgresConfig PostgreSQL 连接配置 type PostgresConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` User string `mapstructure:"user"` Password string `mapstructure:"password"` Database string `mapstructure:"database"` SSLMode string `mapstructure:"sslmode"` // disable | prefer | require } // RedisConfig Redis 缓存配置(启用后正文缓存与音频代理缓存走 Redis,统计数据与设备池仍在 SQL 库) type RedisConfig struct { Enabled bool `mapstructure:"enabled"` Host string `mapstructure:"host"` Port int `mapstructure:"port"` Password string `mapstructure:"password"` DB int `mapstructure:"db"` } // DeviceKeyPoolConfig 设备密钥池配置 type DeviceKeyPoolConfig struct { Enabled bool `mapstructure:"enabled"` // 是否启用设备密钥池 InitialSize int `mapstructure:"initial_size"` // 初始化设备数量 MinSize int `mapstructure:"min_size"` // 最小设备数量 MaxSize int `mapstructure:"max_size"` // 最大设备数量 } // ConfigManager 配置管理器 type ConfigManager struct { mu sync.RWMutex configPath string callbacks []func(*Config) } var ( // appConfig 全局配置实例(原子指针,热更新替换时读取侧无数据竞争) appConfig atomic.Pointer[Config] manager *ConfigManager ) // GetAppConfig 获取当前配置,未加载时返回 nil func GetAppConfig() *Config { return appConfig.Load() } // NewConfigManager 创建配置管理器 func NewConfigManager() *ConfigManager { return &ConfigManager{ callbacks: make([]func(*Config), 0), } } // LoadConfig 加载配置文件 func LoadConfig(configPath string) error { // 初始化管理器 if manager == nil { manager = NewConfigManager() manager.configPath = configPath } // 加载 .env(若存在),敏感配置可由环境变量覆盖 loadDotenv("./.env") viper.SetConfigFile(configPath) viper.SetConfigType("yaml") // 设置默认值 setDefaults() if err := viper.ReadInConfig(); err != nil { return fmt.Errorf("读取配置文件失败: %w", err) } newConfig := &Config{} if err := viper.Unmarshal(newConfig); err != nil { return fmt.Errorf("解析配置文件失败: %w", err) } applyEnvOverrides(newConfig) // 更新配置 appConfig.Store(newConfig) return nil } // setDefaults 设置默认值 func setDefaults() { // 服务器配置 viper.SetDefault("server.port", "40180") viper.SetDefault("server.host", "0.0.0.0") // 番茄小说配置 viper.SetDefault("fanqie.algorithm", "gorgon") viper.SetDefault("fanqie.signature", "sign2") // 内容处理配置 viper.SetDefault("fanqie.content.remove_images", false) viper.SetDefault("fanqie.content.remove_image_desc", false) viper.SetDefault("fanqie.content.remove_blank_lines", true) viper.SetDefault("fanqie.content.remove_html_tags", false) viper.SetDefault("fanqie.content.remove_chapter_title", false) viper.SetDefault("fanqie.content.add_paragraph_indent", false) viper.SetDefault("fanqie.content.double_newline", true) viper.SetDefault("fanqie.content.enable_review", true) // 漫画处理配置 viper.SetDefault("fanqie.manga.output_mode", "url") // 缓存配置 viper.SetDefault("cache.enabled", true) viper.SetDefault("cache.backend", "sqlite") viper.SetDefault("cache.sqlite_path", "./cache.db") viper.SetDefault("cache.cleanup_enabled", false) viper.SetDefault("cache.cleanup_seconds", 86400) // PostgreSQL 配置 viper.SetDefault("cache.postgres.host", "127.0.0.1") viper.SetDefault("cache.postgres.port", 5432) viper.SetDefault("cache.postgres.user", "postgres") viper.SetDefault("cache.postgres.password", "") viper.SetDefault("cache.postgres.database", "fanqie") viper.SetDefault("cache.postgres.sslmode", "disable") // Redis 配置 viper.SetDefault("cache.redis.enabled", false) viper.SetDefault("cache.redis.host", "127.0.0.1") viper.SetDefault("cache.redis.port", 6379) viper.SetDefault("cache.redis.password", "") viper.SetDefault("cache.redis.db", 0) // 设备密钥池配置 viper.SetDefault("device_key_pool.enabled", true) viper.SetDefault("device_key_pool.initial_size", 3) viper.SetDefault("device_key_pool.min_size", 2) viper.SetDefault("device_key_pool.max_size", 10) } // GetConfig 获取配置实例 func GetConfig() *Config { if cfg := appConfig.Load(); cfg != nil { return cfg } log.Fatal("配置未初始化,请先调用 LoadConfig") return nil } // ReloadConfig 重新加载配置文件 func ReloadConfig() error { if manager == nil { return fmt.Errorf("配置管理器未初始化") } manager.mu.RLock() configPath := manager.configPath manager.mu.RUnlock() if configPath == "" { return fmt.Errorf("配置文件路径为空") } // 重新读取配置文件 viper.SetConfigFile(configPath) if err := viper.ReadInConfig(); err != nil { return fmt.Errorf("重新读取配置文件失败: %w", err) } newConfig := &Config{} if err := viper.Unmarshal(newConfig); err != nil { return fmt.Errorf("重新解析配置文件失败: %w", err) } applyEnvOverrides(newConfig) // 更新配置 manager.mu.Lock() var oldAlgorithm string if old := appConfig.Load(); old != nil { oldAlgorithm = old.Fanqie.Algorithm } appConfig.Store(newConfig) callbacks := make([]func(*Config), len(manager.callbacks)) copy(callbacks, manager.callbacks) manager.mu.Unlock() // 通知配置更新 for _, callback := range callbacks { callback(newConfig) } log.Printf("配置重新加载成功: %s", configPath) if oldAlgorithm != newConfig.Fanqie.Algorithm { log.Printf("算法已切换: %s -> %s", oldAlgorithm, newConfig.Fanqie.Algorithm) } return nil } // RegisterConfigCallback 注册配置更新回调 func RegisterConfigCallback(callback func(*Config)) { if manager == nil { log.Printf("警告: 配置管理器未初始化,无法注册回调") return } manager.mu.Lock() defer manager.mu.Unlock() manager.callbacks = append(manager.callbacks, callback) } // GetConfigManager 获取配置管理器实例 func GetConfigManager() *ConfigManager { return manager }