2025-11-28 23:30:49 +08:00
|
|
|
|
package database
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
2025-12-02 22:52:33 +08:00
|
|
|
|
"log"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"time"
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
|
|
|
|
|
"carrotskin/pkg/config"
|
2025-12-02 22:52:33 +08:00
|
|
|
|
|
2025-11-28 23:30:49 +08:00
|
|
|
|
"gorm.io/driver/postgres"
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
|
"gorm.io/gorm/logger"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// New 创建新的PostgreSQL数据库连接
|
|
|
|
|
|
func New(cfg config.DatabaseConfig) (*gorm.DB, error) {
|
|
|
|
|
|
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
|
|
|
|
|
|
cfg.Host,
|
|
|
|
|
|
cfg.Port,
|
|
|
|
|
|
cfg.Username,
|
|
|
|
|
|
cfg.Password,
|
|
|
|
|
|
cfg.Database,
|
|
|
|
|
|
cfg.SSLMode,
|
|
|
|
|
|
cfg.Timezone,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
// 配置慢查询监控
|
|
|
|
|
|
newLogger := logger.New(
|
|
|
|
|
|
log.New(os.Stdout, "\r\n", log.LstdFlags),
|
|
|
|
|
|
logger.Config{
|
|
|
|
|
|
SlowThreshold: 200 * time.Millisecond, // 慢查询阈值:200ms
|
|
|
|
|
|
LogLevel: logger.Warn, // 只记录警告和错误
|
|
|
|
|
|
IgnoreRecordNotFoundError: true, // 忽略记录未找到错误
|
|
|
|
|
|
Colorful: false, // 生产环境禁用彩色
|
|
|
|
|
|
},
|
|
|
|
|
|
)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 打开数据库连接
|
|
|
|
|
|
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
2025-12-02 22:52:33 +08:00
|
|
|
|
Logger: newLogger,
|
|
|
|
|
|
DisableForeignKeyConstraintWhenMigrating: true, // 禁用外键约束
|
|
|
|
|
|
PrepareStmt: true, // 启用预编译语句缓存
|
|
|
|
|
|
QueryFields: true, // 明确指定查询字段
|
2025-11-28 23:30:49 +08:00
|
|
|
|
})
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("连接PostgreSQL数据库失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取底层SQL数据库实例
|
|
|
|
|
|
sqlDB, err := db.DB()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("获取数据库实例失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-02 22:52:33 +08:00
|
|
|
|
// 优化连接池配置
|
|
|
|
|
|
maxIdleConns := cfg.MaxIdleConns
|
|
|
|
|
|
if maxIdleConns <= 0 {
|
|
|
|
|
|
maxIdleConns = 10
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
maxOpenConns := cfg.MaxOpenConns
|
|
|
|
|
|
if maxOpenConns <= 0 {
|
|
|
|
|
|
maxOpenConns = 100
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
connMaxLifetime := cfg.ConnMaxLifetime
|
|
|
|
|
|
if connMaxLifetime <= 0 {
|
|
|
|
|
|
connMaxLifetime = 1 * time.Hour
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sqlDB.SetMaxIdleConns(maxIdleConns)
|
|
|
|
|
|
sqlDB.SetMaxOpenConns(maxOpenConns)
|
|
|
|
|
|
sqlDB.SetConnMaxLifetime(connMaxLifetime)
|
|
|
|
|
|
sqlDB.SetConnMaxIdleTime(10 * time.Minute)
|
2025-11-28 23:30:49 +08:00
|
|
|
|
|
|
|
|
|
|
// 测试连接
|
|
|
|
|
|
if err := sqlDB.Ping(); err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("数据库连接测试失败: %w", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return db, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetDSN 获取数据源名称
|
|
|
|
|
|
func GetDSN(cfg config.DatabaseConfig) string {
|
|
|
|
|
|
return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s TimeZone=%s",
|
|
|
|
|
|
cfg.Host,
|
|
|
|
|
|
cfg.Port,
|
|
|
|
|
|
cfg.Username,
|
|
|
|
|
|
cfg.Password,
|
|
|
|
|
|
cfg.Database,
|
|
|
|
|
|
cfg.SSLMode,
|
|
|
|
|
|
cfg.Timezone,
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|