From c523f53724d7c66f64c50f9d6f886d25cd57f216 Mon Sep 17 00:00:00 2001 From: hhs <386998068@qq.com> Date: Sat, 13 Jun 2026 15:17:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E7=AE=A1=E7=90=86=EF=BC=88Viper=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 internal/config/config.go,定义完整配置结构体 - 支持 YAML 文件 + 环境变量覆盖(CAMTALK_ 前缀) - 新增 config.yaml 默认配置文件 --- backend/config.yaml | 37 ++++++++ backend/internal/config/config.go | 150 ++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 backend/config.yaml create mode 100644 backend/internal/config/config.go diff --git a/backend/config.yaml b/backend/config.yaml new file mode 100644 index 0000000..ab4607f --- /dev/null +++ b/backend/config.yaml @@ -0,0 +1,37 @@ +# config.yaml — 默认配置 +app: + env: dev + +server: + host: "0.0.0.0" + port: 8080 + read_timeout: 30 + write_timeout: 30 + +redis: + addr: "localhost:6379" + password: "" + db: 0 + +ai: + stt: + provider: deepgram + endpoint: "wss://api.deepgram.com/v1/listen" + llm: + provider: openai + model: gpt-4o + endpoint: "https://api.openai.com/v1" + timeout: 10 + tts: + provider: openai + voice: alloy + speed: 1.0 + endpoint: "https://api.openai.com/v1" + timeout: 5 + +storage: + driver: memory + +log: + level: info + format: console diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go new file mode 100644 index 0000000..d8c252c --- /dev/null +++ b/backend/internal/config/config.go @@ -0,0 +1,150 @@ +package config + +import ( + "fmt" + "os" + "strings" + + "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"` + 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"` + 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.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.voice", "alloy") + 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() + } + + // 环境变量覆盖 + 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 +}