140 lines
3.1 KiB
Go
140 lines
3.1 KiB
Go
package model
|
|
|
|
import (
|
|
"database/sql"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
var DB *gorm.DB
|
|
|
|
// DBPoolConfig 数据库连接池配置
|
|
type DBPoolConfig struct {
|
|
MaxOpenConns int
|
|
MaxIdleConns int
|
|
ConnMaxLifetime time.Duration
|
|
ConnMaxIdleTime time.Duration
|
|
}
|
|
|
|
// DefaultDBPoolConfig 返回连接池配置,优先读取环境变量,缺省提供生产合理的默认值
|
|
func DefaultDBPoolConfig() DBPoolConfig {
|
|
maxOpen := getEnvInt("DB_MAX_OPEN_CONNS", 50)
|
|
maxIdle := getEnvInt("DB_MAX_IDLE_CONNS", 10)
|
|
lifetimeMin := getEnvInt("DB_CONN_MAX_LIFETIME_MINUTES", 60)
|
|
idleTimeMin := getEnvInt("DB_CONN_MAX_IDLE_MINUTES", 15)
|
|
|
|
if maxOpen <= 0 {
|
|
maxOpen = 50
|
|
}
|
|
if maxIdle <= 0 {
|
|
maxIdle = 10
|
|
}
|
|
if maxIdle > maxOpen {
|
|
maxIdle = maxOpen
|
|
}
|
|
|
|
return DBPoolConfig{
|
|
MaxOpenConns: maxOpen,
|
|
MaxIdleConns: maxIdle,
|
|
ConnMaxLifetime: time.Duration(lifetimeMin) * time.Minute,
|
|
ConnMaxIdleTime: time.Duration(idleTimeMin) * time.Minute,
|
|
}
|
|
}
|
|
|
|
// ApplyPoolConfig 将连接池参数应用至底层 *sql.DB
|
|
func ApplyPoolConfig(sqlDB *sql.DB, cfg DBPoolConfig) {
|
|
if sqlDB == nil {
|
|
return
|
|
}
|
|
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
|
|
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
|
|
sqlDB.SetConnMaxLifetime(cfg.ConnMaxLifetime)
|
|
sqlDB.SetConnMaxIdleTime(cfg.ConnMaxIdleTime)
|
|
}
|
|
|
|
// BuildGormLogger 根据 GIN_MODE 区分日志策略:生产模式仅记录慢查询与错误,避免全量 SQL 撑满磁盘
|
|
func BuildGormLogger() logger.Interface {
|
|
mode := os.Getenv("GIN_MODE")
|
|
if mode == "release" {
|
|
return logger.New(
|
|
log.New(os.Stdout, "\r\n", log.LstdFlags),
|
|
logger.Config{
|
|
SlowThreshold: 200 * time.Millisecond,
|
|
LogLevel: logger.Warn,
|
|
IgnoreRecordNotFoundError: true,
|
|
Colorful: false,
|
|
},
|
|
)
|
|
}
|
|
return logger.Default.LogMode(logger.Info)
|
|
}
|
|
|
|
func getEnvInt(key string, fallback int) int {
|
|
if v := os.Getenv(key); v != "" {
|
|
if n, err := strconv.Atoi(v); err == nil {
|
|
return n
|
|
}
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func InitDB(dsn string) {
|
|
var err error
|
|
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
|
|
Logger: BuildGormLogger(),
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("数据库连接失败: %v", err)
|
|
}
|
|
|
|
sqlDB, err := DB.DB()
|
|
if err != nil {
|
|
log.Fatalf("获取底层数据库连接失败: %v", err)
|
|
}
|
|
|
|
poolCfg := DefaultDBPoolConfig()
|
|
ApplyPoolConfig(sqlDB, poolCfg)
|
|
|
|
err = Migrate(DB)
|
|
if err != nil {
|
|
log.Fatalf("数据库迁移失败: %v", err)
|
|
}
|
|
|
|
log.Printf("数据库连接成功并完成迁移 [pool: max_open=%d, max_idle=%d, lifetime=%v, idletime=%v]",
|
|
poolCfg.MaxOpenConns, poolCfg.MaxIdleConns, poolCfg.ConnMaxLifetime, poolCfg.ConnMaxIdleTime)
|
|
}
|
|
|
|
func Migrate(db *gorm.DB) error {
|
|
return db.AutoMigrate(
|
|
&Tenant{},
|
|
&User{},
|
|
&Channel{},
|
|
&Customer{},
|
|
&CustomerTag{},
|
|
&CustomerContact{},
|
|
&BlacklistEntry{},
|
|
&Session{},
|
|
&VisitorPageView{},
|
|
&Message{},
|
|
&SessionEvent{},
|
|
&Category{},
|
|
&KnowledgeEntry{},
|
|
&QuickReply{},
|
|
&QuickReplyUserUsage{},
|
|
&Plan{},
|
|
&OperationLog{},
|
|
&Announcement{},
|
|
&TenantSetting{},
|
|
&Role{},
|
|
&Permission{},
|
|
&RolePermission{},
|
|
&RoleDataScope{},
|
|
)
|
|
}
|