package config import ( "fmt" "os" "path/filepath" "sync" "gopkg.in/yaml.v2" ) // Config 应用配置 type Config struct { Database DBConfig `yaml:"database"` Auth Auth `yaml:"auth"` Service Service `yaml:"service"` } // DBConfig 数据库配置 type DBConfig struct { Type string `yaml:"type"` Host string `yaml:"host"` Port int `yaml:"port"` Username string `yaml:"username"` Password string `yaml:"password"` Database string `yaml:"database"` MaxOpenConns int `yaml:"max_open_conns"` MaxIdleConns int `yaml:"max_idle_conns"` ConnMaxLifetime int `yaml:"conn_max_lifetime"` // 单位:秒 } // Auth 认证配置 type Auth struct { Token string `yaml:"token"` } // Service 微服务配置 type Service struct { ReadTimeout int `yaml:"read_timeout"` WriteTimeout int `yaml:"write_timeout"` IdleTimeout int `yaml:"idle_timeout"` } // 导出接口,防止外部修改 type IConfig interface { GetDatabase() DBConfig GetAuth() Auth GetService() Service IsDatabaseConfigured() bool } // 实现接口的具体类型 type configWrapper struct { config *Config } func (cw *configWrapper) GetDatabase() DBConfig { return cw.config.Database } func (cw *configWrapper) GetAuth() Auth { return cw.config.Auth } func (cw *configWrapper) GetService() Service { return cw.config.Service } func (cw *configWrapper) IsDatabaseConfigured() bool { return cw.config.Database.Type != "" && cw.config.Database.Host != "" && cw.config.Database.Port > 0 && cw.config.Database.Username != "" && cw.config.Database.Database != "" } func (cw *configWrapper) IsAuthConfigured() bool { return cw.config.Auth.Token != "" } func (cw *configWrapper) IsServiceConfigured() bool { return cw.config.Service.ReadTimeout > 0 && cw.config.Service.WriteTimeout > 0 && cw.config.Service.IdleTimeout > 0 } var ( instance IConfig once sync.Once initErr error ) // GetConfig 获取配置单例实例 func GetConfig() IConfig { once.Do(func() { config, err := loadConfig() if err != nil { initErr = err // 创建一个空的配置实例,避免nil指针 instance = &configWrapper{config: &Config{}} return } instance = &configWrapper{config: config} }) return instance } // GetInitError 获取初始化错误(如果有) func GetInitError() error { return initErr } // loadConfig 加载配置文件 func loadConfig() (*Config, error) { configFile, err := findConfigFile() if err != nil { return nil, err } fmt.Printf("✅ Using config file: %s\n", configFile) // 读取配置文件 data, err := os.ReadFile(configFile) if err != nil { return nil, fmt.Errorf("failed to read config file %s: %v", configFile, err) } var config Config err = yaml.Unmarshal(data, &config) if err != nil { return nil, fmt.Errorf("failed to parse config file: %v", err) } return &config, nil } // findConfigFile 查找配置文件 func findConfigFile() (string, error) { // 1. 首先尝试可执行文件同目录 exePath, err := os.Executable() if err == nil { exeDir := filepath.Dir(exePath) configFile := filepath.Join(exeDir, "db.yaml") if _, err := os.Stat(configFile); err == nil { return configFile, nil } } // 2. 尝试环境变量指定的路径 envConfigPath := os.Getenv("DB_CONFIG_PATH") if envConfigPath != "" { if _, err := os.Stat(envConfigPath); err == nil { return envConfigPath, nil } return "", fmt.Errorf("DB_CONFIG_PATH file not found: %s", envConfigPath) } // 3. 如果都没有找到,返回错误 exeDir := "unknown" if exePath, err := os.Executable(); err == nil { exeDir = filepath.Dir(exePath) } return "", fmt.Errorf(`No configuration file found! Tried locations: 1. Executable directory: %s/db.yaml 2. Environment variable: DB_CONFIG_PATH Solutions: - Place db.yaml in the same directory as the executable - Or set DB_CONFIG_PATH environment variable to config file path Example: export DB_CONFIG_PATH=/path/to/your/db.yaml`, exeDir) }