package config import ( "fmt" "os" "strings" "github.com/joho/godotenv" "github.com/spf13/viper" ) // Config 应用配置。 type Config struct { App AppConfig `mapstructure:"app"` Server ServerConfig `mapstructure:"server"` Redis RedisConfig `mapstructure:"redis"` AI AIConfig `mapstructure:"ai"` Storage StorageConfig `mapstructure:"storage"` Log LogConfig `mapstructure:"log"` } type AppConfig struct { Env string `mapstructure:"env"` Version string `mapstructure:"version"` } type ServerConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` ReadTimeout int `mapstructure:"read_timeout"` WriteTimeout int `mapstructure:"write_timeout"` } // Addr 返回 host:port 地址。 func (s ServerConfig) Addr() string { return fmt.Sprintf("%s:%d", s.Host, s.Port) } type RedisConfig struct { Addr string `mapstructure:"addr"` Password string `mapstructure:"password"` DB int `mapstructure:"db"` } type AIConfig struct { STT STTConfig `mapstructure:"stt"` LLM LLMConfig `mapstructure:"llm"` TTS TTSConfig `mapstructure:"tts"` } type STTConfig struct { Provider string `mapstructure:"provider"` APIKey string `mapstructure:"api_key"` Model string `mapstructure:"model"` Endpoint string `mapstructure:"endpoint"` } type LLMConfig struct { Provider string `mapstructure:"provider"` APIKey string `mapstructure:"api_key"` Model string `mapstructure:"model"` Endpoint string `mapstructure:"endpoint"` Timeout int `mapstructure:"timeout"` } type TTSConfig struct { Provider string `mapstructure:"provider"` APIKey string `mapstructure:"api_key"` Model string `mapstructure:"model"` Voice string `mapstructure:"voice"` Speed float64 `mapstructure:"speed"` Endpoint string `mapstructure:"endpoint"` Timeout int `mapstructure:"timeout"` } type StorageConfig struct { Driver string `mapstructure:"driver"` DSN string `mapstructure:"dsn"` } type LogConfig struct { Level string `mapstructure:"level"` Format string `mapstructure:"format"` } // Load 加载配置。优先级:环境变量 > config.{env}.yaml > config.yaml。 func Load() (*Config, error) { v := viper.New() v.SetConfigName("config") v.SetConfigType("yaml") v.AddConfigPath(".") v.AddConfigPath("./config") v.AddConfigPath("./backend") // 默认值 v.SetDefault("app.env", "dev") v.SetDefault("server.host", "0.0.0.0") v.SetDefault("server.port", 8080) v.SetDefault("server.read_timeout", 30) v.SetDefault("server.write_timeout", 30) v.SetDefault("redis.addr", "localhost:6379") v.SetDefault("redis.db", 0) v.SetDefault("ai.stt.provider", "deepgram") v.SetDefault("ai.stt.model", "nova-2") v.SetDefault("ai.stt.endpoint", "wss://api.deepgram.com/v1/listen") v.SetDefault("ai.llm.provider", "openai") v.SetDefault("ai.llm.model", "gpt-4o") v.SetDefault("ai.llm.endpoint", "https://api.openai.com/v1") v.SetDefault("ai.llm.timeout", 10) v.SetDefault("ai.tts.provider", "openai") v.SetDefault("ai.tts.model", "tts-1") v.SetDefault("ai.tts.voice", "mimo_default") v.SetDefault("ai.tts.speed", 1.0) v.SetDefault("ai.tts.endpoint", "https://api.openai.com/v1") v.SetDefault("ai.tts.timeout", 5) v.SetDefault("storage.driver", "memory") v.SetDefault("log.level", "info") v.SetDefault("log.format", "console") // 读取基础配置文件 _ = v.ReadInConfig() // 文件不存在不报错 // 根据 APP_ENV 覆盖 env := os.Getenv("APP_ENV") if env == "" { env = v.GetString("app.env") } if env != "" { v.SetConfigName("config." + env) _ = v.MergeInConfig() } // 加载 .env 文件(不覆盖已有环境变量) // 按优先级尝试:当前目录、上级目录(兼容从 backend/ 或项目根目录启动) _ = godotenv.Load() _ = godotenv.Load("../.env") // 环境变量覆盖 v.SetEnvPrefix("CAMTALK") v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.AutomaticEnv() var cfg Config if err := v.Unmarshal(&cfg); err != nil { return nil, fmt.Errorf("config unmarshal: %w", err) } // 填充默认值 if cfg.Server.Host == "" { cfg.Server.Host = "0.0.0.0" } if cfg.Server.Port == 0 { cfg.Server.Port = 8080 } if cfg.App.Env == "" { cfg.App.Env = "dev" } return &cfg, nil }