package database import ( "fmt" "log" "os" "strings" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) // loadDotEnv 在当前工作目录查找 .env 文件并加载到进程环境变量中(已存在的变量不覆盖)。 // 仅用于本地开发场景:生产部署通过 docker-compose / 容器环境变量直接注入,没有 .env 文件时静默跳过。 func loadDotEnv() { data, err := os.ReadFile(".env") if err != nil { return } for _, line := range strings.Split(string(data), "\n") { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "#") { continue } key, value, ok := strings.Cut(line, "=") if !ok { continue } key = strings.TrimSpace(key) if key == "" { continue } if _, exists := os.LookupEnv(key); exists { continue } os.Setenv(key, strings.TrimSpace(value)) } } // DBConfig holds MySQL connection parameters. type DBConfig struct { Host string Port string User string Password string DBName string } func (c DBConfig) DSN() string { return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", c.User, c.Password, c.Host, c.Port, c.DBName) } // envConfig 从环境变量读取连接参数。Host/User/Password 没有内置默认值,必须由 // 部署环境(本地 shell、.env、docker-compose 等)显式提供;DBName 默认统一为 // "sproutgate",开发和生产环境共用同一个库名。 func envConfig() (DBConfig, error) { host := os.Getenv("DB_HOST") user := os.Getenv("DB_USER") password := os.Getenv("DB_PASSWORD") if host == "" || user == "" || password == "" { return DBConfig{}, fmt.Errorf("缺少数据库环境变量,请设置 DB_HOST/DB_USER/DB_PASSWORD(或直接设置 DB_DSN)") } port := os.Getenv("DB_PORT") if port == "" { port = "3306" } dbName := os.Getenv("DB_NAME") if dbName == "" { dbName = "sproutgate" } return DBConfig{Host: host, Port: port, User: user, Password: password, DBName: dbName}, nil } // Open 根据环境变量打开数据库连接。 // 优先级:DB_DSN(完整自定义连接串)> DB_HOST/DB_PORT/DB_USER/DB_PASSWORD/DB_NAME func Open() (*gorm.DB, error) { loadDotEnv() dsn := os.Getenv("DB_DSN") if dsn == "" { cfg, err := envConfig() if err != nil { return nil, err } dsn = cfg.DSN() log.Printf("[database] 使用数据库 %s:%s/%s", cfg.Host, cfg.Port, cfg.DBName) } else { log.Println("[database] 使用自定义 DB_DSN") } logLevel := logger.Info if os.Getenv("GIN_MODE") == "release" { logLevel = logger.Warn } db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logLevel), }) if err != nil { return nil, fmt.Errorf("连接数据库失败: %w", err) } sqlDB, err := db.DB() if err != nil { return nil, err } sqlDB.SetMaxOpenConns(25) sqlDB.SetMaxIdleConns(5) return db, nil }