| 
									
										
										
										
											2025-06-30 19:21:56 +08:00
										 |  |  |  | package database | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | import ( | 
					
						
							|  |  |  |  | 	"context" | 
					
						
							|  |  |  |  | 	"fmt" | 
					
						
							|  |  |  |  | 	"time" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	"gorm.io/driver/postgres" | 
					
						
							|  |  |  |  | 	"gorm.io/gorm" | 
					
						
							|  |  |  |  | 	"gorm.io/gorm/logger" | 
					
						
							|  |  |  |  | 	"gorm.io/gorm/schema" | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // Config 数据库配置 | 
					
						
							|  |  |  |  | type Config struct { | 
					
						
							|  |  |  |  | 	Host            string | 
					
						
							|  |  |  |  | 	Port            string | 
					
						
							|  |  |  |  | 	User            string | 
					
						
							|  |  |  |  | 	Password        string | 
					
						
							|  |  |  |  | 	Name            string | 
					
						
							|  |  |  |  | 	SSLMode         string | 
					
						
							|  |  |  |  | 	Timezone        string | 
					
						
							|  |  |  |  | 	MaxOpenConns    int | 
					
						
							|  |  |  |  | 	MaxIdleConns    int | 
					
						
							|  |  |  |  | 	ConnMaxLifetime time.Duration | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // DB 数据库包装器 | 
					
						
							|  |  |  |  | type DB struct { | 
					
						
							|  |  |  |  | 	*gorm.DB | 
					
						
							|  |  |  |  | 	config Config | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // NewConnection 创建新的数据库连接 | 
					
						
							|  |  |  |  | func NewConnection(config Config) (*DB, error) { | 
					
						
							|  |  |  |  | 	// 构建DSN | 
					
						
							|  |  |  |  | 	dsn := buildDSN(config) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 配置GORM | 
					
						
							|  |  |  |  | 	gormConfig := &gorm.Config{ | 
					
						
							|  |  |  |  | 		Logger: logger.Default.LogMode(logger.Info), | 
					
						
							|  |  |  |  | 		NamingStrategy: schema.NamingStrategy{ | 
					
						
							|  |  |  |  | 			SingularTable: true, // 使用单数表名 | 
					
						
							|  |  |  |  | 		}, | 
					
						
							|  |  |  |  | 		DisableForeignKeyConstraintWhenMigrating: true, | 
					
						
							| 
									
										
										
										
											2025-07-11 21:05:58 +08:00
										 |  |  |  | 		NowFunc: func() time.Time { | 
					
						
							|  |  |  |  | 			return time.Now().In(time.FixedZone("CST", 8*3600)) // 强制使用北京时间 | 
					
						
							|  |  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2025-08-19 01:49:19 +08:00
										 |  |  |  | 		PrepareStmt: true, | 
					
						
							|  |  |  |  | 		DisableAutomaticPing: false, | 
					
						
							| 
									
										
										
										
											2025-06-30 19:21:56 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 连接数据库 | 
					
						
							|  |  |  |  | 	db, err := gorm.Open(postgres.Open(dsn), gormConfig) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, fmt.Errorf("连接数据库失败: %w", err) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 获取底层sql.DB | 
					
						
							|  |  |  |  | 	sqlDB, err := db.DB() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, fmt.Errorf("获取数据库实例失败: %w", err) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 配置连接池 | 
					
						
							|  |  |  |  | 	sqlDB.SetMaxOpenConns(config.MaxOpenConns) | 
					
						
							|  |  |  |  | 	sqlDB.SetMaxIdleConns(config.MaxIdleConns) | 
					
						
							|  |  |  |  | 	sqlDB.SetConnMaxLifetime(config.ConnMaxLifetime) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	// 测试连接 | 
					
						
							|  |  |  |  | 	if err := sqlDB.Ping(); err != nil { | 
					
						
							|  |  |  |  | 		return nil, fmt.Errorf("数据库连接测试失败: %w", err) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return &DB{ | 
					
						
							|  |  |  |  | 		DB:     db, | 
					
						
							|  |  |  |  | 		config: config, | 
					
						
							|  |  |  |  | 	}, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // buildDSN 构建数据库连接字符串 | 
					
						
							|  |  |  |  | func buildDSN(config Config) string { | 
					
						
							|  |  |  |  | 	return fmt.Sprintf( | 
					
						
							| 
									
										
										
										
											2025-07-11 21:05:58 +08:00
										 |  |  |  | 		"host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=%s options='-c timezone=%s'", | 
					
						
							| 
									
										
										
										
											2025-06-30 19:21:56 +08:00
										 |  |  |  | 		config.Host, | 
					
						
							|  |  |  |  | 		config.User, | 
					
						
							|  |  |  |  | 		config.Password, | 
					
						
							|  |  |  |  | 		config.Name, | 
					
						
							|  |  |  |  | 		config.Port, | 
					
						
							|  |  |  |  | 		config.SSLMode, | 
					
						
							|  |  |  |  | 		config.Timezone, | 
					
						
							| 
									
										
										
										
											2025-07-11 21:05:58 +08:00
										 |  |  |  | 		config.Timezone, | 
					
						
							| 
									
										
										
										
											2025-06-30 19:21:56 +08:00
										 |  |  |  | 	) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // Close 关闭数据库连接 | 
					
						
							|  |  |  |  | func (db *DB) Close() error { | 
					
						
							|  |  |  |  | 	sqlDB, err := db.DB.DB() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return sqlDB.Close() | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // Ping 检查数据库连接 | 
					
						
							|  |  |  |  | func (db *DB) Ping() error { | 
					
						
							|  |  |  |  | 	sqlDB, err := db.DB.DB() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return sqlDB.Ping() | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // GetStats 获取连接池统计信息 | 
					
						
							|  |  |  |  | func (db *DB) GetStats() (map[string]interface{}, error) { | 
					
						
							|  |  |  |  | 	sqlDB, err := db.DB.DB() | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	stats := sqlDB.Stats() | 
					
						
							|  |  |  |  | 	return map[string]interface{}{ | 
					
						
							|  |  |  |  | 		"max_open_connections": stats.MaxOpenConnections, | 
					
						
							|  |  |  |  | 		"open_connections":     stats.OpenConnections, | 
					
						
							|  |  |  |  | 		"in_use":               stats.InUse, | 
					
						
							|  |  |  |  | 		"idle":                 stats.Idle, | 
					
						
							|  |  |  |  | 		"wait_count":           stats.WaitCount, | 
					
						
							|  |  |  |  | 		"wait_duration":        stats.WaitDuration, | 
					
						
							|  |  |  |  | 		"max_idle_closed":      stats.MaxIdleClosed, | 
					
						
							|  |  |  |  | 		"max_idle_time_closed": stats.MaxIdleTimeClosed, | 
					
						
							|  |  |  |  | 		"max_lifetime_closed":  stats.MaxLifetimeClosed, | 
					
						
							|  |  |  |  | 	}, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | // BeginTx 开始事务(已废弃,请使用shared/database.TransactionManager) | 
					
						
							|  |  |  |  | // @deprecated 请使用 shared/database.TransactionManager | 
					
						
							| 
									
										
										
										
											2025-06-30 19:21:56 +08:00
										 |  |  |  | func (db *DB) BeginTx() *gorm.DB { | 
					
						
							|  |  |  |  | 	return db.DB.Begin() | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // Migrate 执行数据库迁移 | 
					
						
							|  |  |  |  | func (db *DB) Migrate(models ...interface{}) error { | 
					
						
							|  |  |  |  | 	return db.DB.AutoMigrate(models...) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // IsHealthy 检查数据库健康状态 | 
					
						
							|  |  |  |  | func (db *DB) IsHealthy() bool { | 
					
						
							|  |  |  |  | 	return db.Ping() == nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // WithContext 返回带上下文的数据库实例 | 
					
						
							|  |  |  |  | func (db *DB) WithContext(ctx interface{}) *gorm.DB { | 
					
						
							|  |  |  |  | 	if c, ok := ctx.(context.Context); ok { | 
					
						
							|  |  |  |  | 		return db.DB.WithContext(c) | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return db.DB | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | // 注意:事务相关功能已迁移到 shared/database.TransactionManager | 
					
						
							|  |  |  |  | // 请使用 TransactionManager 进行事务管理 |