Skip to content

Commit a750f69

Browse files
committed
refactor(database): 重构数据库初始化逻辑,分离连接字符串构建和日志配置
- 将PostgreSQL和MySQL连接字符串构建逻辑提取为独立函数 - 添加数据库日志配置函数以简化初始化过程 - 增强数据库连接池配置,优化连接管理 - 更新配置热加载失败时的错误处理,记录日志而不中断服务 - 更新.gitignore文件以按需启用RSA密钥文件的忽略规则
1 parent 97fe120 commit a750f69

File tree

3 files changed

+127
-79
lines changed

3 files changed

+127
-79
lines changed

.gitignore

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ temp/
152152
*.crt
153153
*.csr
154154

155-
# RSA key files
156-
rsa_private_key.pem
157-
rsa_public_key.pem
155+
# RSA key files (可按需启用忽略规则)
156+
# rsa_private_key.pem
157+
# rsa_public_key.pem
158158

159159
# Upload files
160160
uploads/
@@ -279,4 +279,4 @@ Network Trash Folder
279279
Temporary Items
280280
.apdisk
281281
public/*
282-
gotribe-admin
282+
gotribe-admin

config/config.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package config
77

88
import (
99
"fmt"
10+
"log"
1011
"os"
1112

1213
"github.com/fsnotify/fsnotify"
@@ -47,8 +48,11 @@ func InitConfig() {
4748
viper.OnConfigChange(func(e fsnotify.Event) {
4849
// 将读取的配置信息保存至全局变量Conf
4950
if err := viper.Unmarshal(Conf); err != nil {
50-
panic(fmt.Errorf("初始化配置文件失败:%s \n", err))
51+
// 配置热更新失败时记录错误日志,但不中断服务
52+
log.Printf("配置热更新失败: %v,保持使用旧配置", err)
53+
return
5154
}
55+
log.Printf("配置文件已重新加载: %s", e.Name)
5256
})
5357

5458
if err != nil {

internal/pkg/common/database.go

Lines changed: 118 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package common
77

88
import (
9+
"database/sql"
910
"fmt"
1011
"gotribe-admin/config"
1112
"gotribe-admin/internal/pkg/model/migrate"
@@ -23,95 +24,138 @@ import (
2324
// 全局数据库变量
2425
var DB *gorm.DB
2526

26-
// 初始化数据库
27-
func InitDatabase() {
28-
var dsn string
29-
var showDsn string
27+
// getGormConfig 获取 GORM 通用配置
28+
func getGormConfig() *gorm.Config {
29+
return &gorm.Config{
30+
// 禁用外键(指定外键时不会在数据库创建真实的外键约束)
31+
DisableForeignKeyConstraintWhenMigrating: true,
32+
// 使用单数表名
33+
NamingStrategy: schema.NamingStrategy{
34+
SingularTable: true,
35+
},
36+
}
37+
}
3038

31-
if config.Conf.Database.Type == "postgres" {
32-
dsn = fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s %s",
33-
config.Conf.Database.Host,
34-
config.Conf.Database.Username,
35-
config.Conf.Database.Password,
36-
config.Conf.Database.Database,
37-
config.Conf.Database.Port,
38-
config.Conf.Database.SSLMode,
39-
config.Conf.Database.Query,
40-
)
41-
showDsn = fmt.Sprintf("host=%s user=%s password=****** dbname=%s port=%d sslmode=%s %s",
42-
config.Conf.Database.Host,
43-
config.Conf.Database.Username,
44-
config.Conf.Database.Database,
45-
config.Conf.Database.Port,
46-
config.Conf.Database.SSLMode,
47-
config.Conf.Database.Query,
48-
)
49-
} else {
50-
// MySQL 连接字符串
51-
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&collation=%s&%s",
52-
config.Conf.Database.Username,
53-
config.Conf.Database.Password,
54-
config.Conf.Database.Host,
55-
config.Conf.Database.Port,
56-
config.Conf.Database.Database,
57-
config.Conf.Database.Charset,
58-
config.Conf.Database.Collation,
59-
config.Conf.Database.Query,
60-
)
61-
showDsn = fmt.Sprintf(
62-
"%s:******@tcp(%s:%d)/%s?charset=%s&collation=%s&%s",
63-
config.Conf.Database.Username,
64-
config.Conf.Database.Host,
65-
config.Conf.Database.Port,
66-
config.Conf.Database.Database,
67-
config.Conf.Database.Charset,
68-
config.Conf.Database.Collation,
69-
config.Conf.Database.Query,
70-
)
39+
// buildPostgresDSN 构建 PostgreSQL 连接字符串
40+
func buildPostgresDSN(maskPassword bool) string {
41+
db := config.Conf.Database
42+
43+
// 处理 SSLMode 默认值
44+
sslMode := db.SSLMode
45+
if sslMode == "" {
46+
sslMode = "disable"
47+
}
48+
49+
// 处理 Query 参数,如果为空则不添加
50+
queryPart := ""
51+
if db.Query != "" {
52+
queryPart = " " + db.Query
53+
}
54+
55+
password := db.Password
56+
if maskPassword {
57+
password = "******"
58+
}
59+
60+
return fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s%s",
61+
db.Host, db.Username, password, db.Database, db.Port, sslMode, queryPart)
62+
}
63+
64+
// buildMySQLDSN 构建 MySQL 连接字符串
65+
func buildMySQLDSN(maskPassword bool) string {
66+
db := config.Conf.Database
67+
68+
// 处理 Query 参数,如果为空则不添加 & 前缀
69+
queryPart := ""
70+
if db.Query != "" {
71+
queryPart = "&" + db.Query
72+
}
73+
74+
password := db.Password
75+
if maskPassword {
76+
password = "******"
7177
}
7278

73-
var db *gorm.DB
74-
var err error
79+
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&collation=%s%s",
80+
db.Username, password, db.Host, db.Port, db.Database,
81+
db.Charset, db.Collation, queryPart)
82+
}
83+
84+
// setupDatabaseLogger 配置数据库日志
85+
func setupDatabaseLogger(db *gorm.DB) {
86+
if !config.Conf.Database.LogMode {
87+
return
88+
}
89+
90+
newLogger := logger.New(
91+
log.New(os.Stdout, "\r\n", log.LstdFlags),
92+
logger.Config{
93+
SlowThreshold: time.Second,
94+
LogLevel: logger.Info,
95+
Colorful: true,
96+
},
97+
)
98+
db.Debug()
99+
db.Logger = newLogger
100+
}
101+
102+
// openDatabase 打开数据库连接
103+
func openDatabase(dsn string) (*gorm.DB, error) {
104+
dbType := config.Conf.Database.Type
105+
gormConfig := getGormConfig()
106+
107+
if dbType == "postgres" {
108+
return gorm.Open(postgres.Open(dsn), gormConfig)
109+
}
110+
// 默认使用 MySQL
111+
return gorm.Open(mysql.Open(dsn), gormConfig)
112+
}
75113

114+
// 初始化数据库
115+
func InitDatabase() {
116+
var dsn, showDsn string
117+
118+
// 根据数据库类型构建连接字符串
76119
if config.Conf.Database.Type == "postgres" {
77-
db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
78-
// 禁用外键(指定外键时不会在数据库创建真实的外键约束)
79-
DisableForeignKeyConstraintWhenMigrating: true,
80-
// 使用单数表名
81-
NamingStrategy: schema.NamingStrategy{
82-
SingularTable: true,
83-
},
84-
})
120+
dsn = buildPostgresDSN(false)
121+
showDsn = buildPostgresDSN(true)
85122
} else {
86-
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
87-
// 禁用外键(指定外键时不会在数据库创建真实的外键约束)
88-
DisableForeignKeyConstraintWhenMigrating: true,
89-
// 使用单数表名
90-
NamingStrategy: schema.NamingStrategy{
91-
SingularTable: true,
92-
},
93-
})
123+
dsn = buildMySQLDSN(false)
124+
showDsn = buildMySQLDSN(true)
94125
}
126+
127+
// 打开数据库连接
128+
db, err := openDatabase(dsn)
95129
if err != nil {
96130
Log.Panicf("初始化数据库异常: %v", err)
97131
panic(fmt.Errorf("初始化数据库异常: %v", err))
98132
}
99133

100-
// 开启数据库日志
101-
if config.Conf.Database.LogMode {
102-
newLogger := logger.New(
103-
log.New(os.Stdout, "\r\n", log.LstdFlags),
104-
logger.Config{
105-
SlowThreshold: time.Second,
106-
LogLevel: logger.Info,
107-
Colorful: true,
108-
},
109-
)
110-
db.Debug()
111-
db.Logger = newLogger
134+
// 配置数据库日志
135+
setupDatabaseLogger(db)
136+
137+
// 配置连接池参数,避免连接频繁关闭和重连导致的日志输出
138+
var sqlDB *sql.DB
139+
sqlDB, err = db.DB()
140+
if err != nil {
141+
Log.Panicf("获取数据库连接失败: %v", err)
142+
panic(fmt.Errorf("获取数据库连接失败: %v", err))
112143
}
144+
145+
// 设置连接池参数
146+
// SetMaxOpenConns: 设置打开数据库连接的最大数量
147+
sqlDB.SetMaxOpenConns(100)
148+
// SetMaxIdleConns: 设置空闲连接池中连接的最大数量
149+
sqlDB.SetMaxIdleConns(10)
150+
// SetConnMaxLifetime: 设置连接可复用的最大时间(应该小于 MySQL 的 wait_timeout)
151+
// MySQL 默认 wait_timeout 是 8 小时,这里设置为 7 小时,避免连接被 MySQL 主动关闭
152+
sqlDB.SetConnMaxLifetime(7 * time.Hour)
153+
// SetConnMaxIdleTime: 设置连接的最大空闲时间,超过此时间的空闲连接会被关闭
154+
sqlDB.SetConnMaxIdleTime(1 * time.Hour)
155+
113156
// 全局DB赋值
114157
DB = db
158+
115159
// 自动迁移表结构
116160
if config.Conf.System.EnableMigrate {
117161
migrate.DBAutoMigrate(DB)

0 commit comments

Comments
 (0)