golang-dy-back/internal/db/db.go
2025-10-28 16:59:41 +08:00

73 lines
1.5 KiB
Go

package db
import (
"database/sql"
"fmt"
"sync"
"time"
"go-dy/internal/config"
_ "github.com/go-sql-driver/mysql"
)
var (
mu sync.Mutex
conn *sql.DB
)
func getDSN(cfg config.Config) string {
if cfg.DBDsn != "" {
return cfg.DBDsn
}
// sensible default for local dev
return "root:password@tcp(127.0.0.1:3306)/go-dy?parseTime=true&charset=utf8mb4&loc=Local"
}
// Get returns a shared DB connection. It retries initialization if previous attempts failed.
func Get(cfg config.Config) (*sql.DB, error) {
mu.Lock()
defer mu.Unlock()
if conn != nil {
return conn, nil
}
dsn := getDSN(cfg)
db, err := sql.Open("mysql", dsn)
if err != nil {
return nil, err
}
db.SetMaxOpenConns(20)
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(30 * time.Minute)
// verify connection first
if e := db.Ping(); e != nil {
_ = db.Close()
return nil, e
}
// init schema after successful ping
if e := initSchema(db); e != nil {
_ = db.Close()
return nil, e
}
conn = db
return conn, nil
}
func initSchema(db *sql.DB) error {
// minimal users table
_, err := db.Exec(`
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`)
if err != nil {
return fmt.Errorf("init schema: %w", err)
}
return nil
}