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{}, ) }