Files
EchoChat/backend/go-service/pkg/db/postgres.go
2026-05-19 16:15:01 +08:00

101 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package db 提供数据库连接管理
package db
import (
"context"
"errors"
"fmt"
"time"
"github.com/echochat/backend/config"
"github.com/echochat/backend/pkg/logs"
"go.uber.org/zap"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
// GormDB 全局 GORM 数据库实例
var GormDB *gorm.DB
// zapGormLogger 将 GORM 日志适配到 zap
type zapGormLogger struct{}
func (l *zapGormLogger) LogMode(logger.LogLevel) logger.Interface { return l }
func (l *zapGormLogger) Info(ctx context.Context, msg string, data ...interface{}) {
logs.Info(ctx, "gorm", fmt.Sprintf(msg, data...))
}
func (l *zapGormLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
logs.Warn(ctx, "gorm", fmt.Sprintf(msg, data...))
}
func (l *zapGormLogger) Error(ctx context.Context, msg string, data ...interface{}) {
logs.Error(ctx, "gorm", fmt.Sprintf(msg, data...))
}
func (l *zapGormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
elapsed := time.Since(begin)
sql, rows := fc()
fields := []zap.Field{
zap.Duration("latency", elapsed),
zap.Int64("rows", rows),
zap.String("sql", sql),
}
if err != nil {
// gorm.ErrRecordNotFound 在业务侧大量被用作"是否存在"判定GetByXxx 返回 nil, nil
// 没必要在 SQL 层把它当 ERROR 刷出来,否则会误触告警;保持原 fields 在 Debug 层级即可排障
if errors.Is(err, gorm.ErrRecordNotFound) {
logs.Debug(ctx, "gorm.trace", "SQL未命中record not found", fields...)
return
}
fields = append(fields, zap.Error(err))
logs.Error(ctx, "gorm.trace", "SQL执行失败", fields...)
return
}
if elapsed > 200*time.Millisecond {
logs.Warn(ctx, "gorm.trace", "慢SQL告警", fields...)
return
}
logs.Debug(ctx, "gorm.trace", "SQL执行", fields...)
}
// NewPostgres 初始化 PostgreSQL 数据库连接
func NewPostgres(cfg *config.DatabaseConfig) (*gorm.DB, error) {
funcName := "db.NewPostgres"
ctx := context.Background()
logs.Info(ctx, funcName, "正在连接 PostgreSQL",
zap.String("host", cfg.Host),
zap.Int("port", cfg.Port),
zap.String("dbname", cfg.DBName),
)
db, err := gorm.Open(postgres.Open(cfg.DSN()), &gorm.Config{
Logger: &zapGormLogger{},
})
if err != nil {
logs.Error(ctx, funcName, "PostgreSQL 连接失败", zap.Error(err))
return nil, fmt.Errorf("连接 PostgreSQL 失败: %w", err)
}
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("获取底层 sql.DB 失败: %w", err)
}
sqlDB.SetMaxIdleConns(cfg.MaxIdleConns)
sqlDB.SetMaxOpenConns(cfg.MaxOpenConns)
sqlDB.SetConnMaxLifetime(time.Hour)
if err := sqlDB.Ping(); err != nil {
logs.Error(ctx, funcName, "PostgreSQL Ping 失败", zap.Error(err))
return nil, fmt.Errorf("ping PostgreSQL 失败: %w", err)
}
GormDB = db
logs.Info(ctx, funcName, "PostgreSQL 连接成功")
return db, nil
}