Bläddra i källkod

支持多数据库

qdy 2 månader sedan
förälder
incheckning
aed61b5bd2
3 ändrade filer med 344 tillägg och 69 borttagningar
  1. 15
    5
      bootstraps/bootstrap.go
  2. 107
    49
      config/config.go
  3. 222
    15
      config/subconfigs/database_config.go

+ 15
- 5
bootstraps/bootstrap.go Visa fil

@@ -14,6 +14,11 @@ import (
14 14
 	"git.x2erp.com/qdy/go-base/logger"
15 15
 )
16 16
 
17
+// 定义接口
18
+type ShutdownHandler interface {
19
+	OnShutdown()
20
+}
21
+
17 22
 // Bootstrapper 服务启动器
18 23
 type Bootstrapper struct {
19 24
 	serviceName    string
@@ -81,7 +86,7 @@ func (b *Bootstrapper) StartService() *Bootstrapper {
81 86
 }
82 87
 
83 88
 // Run 运行服务
84
-func (b *Bootstrapper) Run() {
89
+func (b *Bootstrapper) Run(handler ShutdownHandler) {
85 90
 	log.Printf("服务 %s 开始运行...", b.serviceName)
86 91
 
87 92
 	// 设置信号监听 - 在这里设置,而不是在New中
@@ -96,11 +101,11 @@ func (b *Bootstrapper) Run() {
96 101
 	}()
97 102
 
98 103
 	// 等待中断信号
99
-	b.waitForShutdown()
104
+	b.waitForShutdown(handler)
100 105
 }
101 106
 
102 107
 // RunTLS 运行HTTPS服务
103
-func (b *Bootstrapper) RunTLS(certFile, keyFile string) {
108
+func (b *Bootstrapper) RunTLS(certFile, keyFile string, handler ShutdownHandler) {
104 109
 	log.Printf("服务 %s 开始运行(HTTPS)...", b.serviceName)
105 110
 
106 111
 	// 设置信号监听
@@ -115,11 +120,11 @@ func (b *Bootstrapper) RunTLS(certFile, keyFile string) {
115 120
 	}()
116 121
 
117 122
 	// 等待中断信号
118
-	b.waitForShutdown()
123
+	b.waitForShutdown(handler)
119 124
 }
120 125
 
121 126
 // waitForShutdown 等待关闭信号
122
-func (b *Bootstrapper) waitForShutdown() {
127
+func (b *Bootstrapper) waitForShutdown(handler ShutdownHandler) {
123 128
 	log.Println("按 Ctrl+C 停止服务")
124 129
 
125 130
 	// 等待信号
@@ -137,6 +142,11 @@ func (b *Bootstrapper) waitForShutdown() {
137 142
 		log.Println("HTTP服务器已关闭")
138 143
 	}
139 144
 
145
+	// 执行关闭处理
146
+	if handler != nil {
147
+		handler.OnShutdown()
148
+	}
149
+
140 150
 	// 停止日志写入
141 151
 	logger.StopESWriter()
142 152
 	log.Println("服务优雅关闭完成")

+ 107
- 49
config/config.go Visa fil

@@ -4,11 +4,17 @@ import (
4 4
 	"sync"
5 5
 
6 6
 	"git.x2erp.com/qdy/go-base/config/subconfigs"
7
+	"git.x2erp.com/qdy/go-base/logger"
7 8
 )
8 9
 
9
-// IConfig 主配置接口
10
+// IConfig 主配置接口(更新支持多数据库)
10 11
 type IConfig interface {
11
-	GetDatabase() *subconfigs.DatabaseConfig
12
+	// 多数据库支持
13
+	GetDatabase() *subconfigs.DatabaseConfig                  // 获取默认数据库(向后兼容)
14
+	GetDatabases() *subconfigs.DatabasesConfig                // 获取所有数据库配置
15
+	GetDatabaseConfig(name string) *subconfigs.DatabaseConfig // 按名称获取数据库配置
16
+
17
+	// 其他配置(保持不变)
12 18
 	GetRedis() *subconfigs.RedisConfig
13 19
 	GetDoris() *subconfigs.DorisConfig
14 20
 	GetRabbitMQ() *subconfigs.RabbitMQConfig
@@ -18,48 +24,74 @@ type IConfig interface {
18 24
 	GetMicro() *subconfigs.MicroConfig
19 25
 	GetLog() *subconfigs.LogConfig
20 26
 	GetInitError() error
27
+
28
+	// 配置检查(更新)
21 29
 	IsDatabaseConfigured() bool
30
+	IsDatabaseConfiguredByName(name string) bool
22 31
 	IsDorisConfigured() bool
23 32
 	IsRedisConfigured() bool
33
+
24 34
 	SetServiceName(name string)
25 35
 	SetServiceVersion(version string)
26 36
 	SetEnv(name string)
27 37
 }
28 38
 
29
-// 根据启动参数设置环境变量
30
-func (c *Config) SetEnv(name string) {
31
-	if c.GetService().Env == "" {
32
-		c.GetService().Env = name
33
-	}
39
+// Config 主配置结构体 - 访问门面
40
+type Config struct {
41
+	initError error // 初始化错误
34 42
 }
35 43
 
36
-// 设置微服务名称。。如果配置文件设置了,就按配置文件
37
-func (c *Config) SetServiceName(name string) {
38
-	if c.GetService().ServiceName == "" {
39
-		c.GetService().ServiceName = name
40
-	}
44
+// ========== 数据库相关方法(更新) ==========
41 45
 
46
+// GetDatabases 获取所有数据库配置
47
+func (c *Config) GetDatabases() *subconfigs.DatabasesConfig {
48
+	if config := subconfigs.GetRegisteredConfig("databases"); config != nil {
49
+		if dbs, ok := config.(*subconfigs.DatabasesConfig); ok {
50
+			return dbs
51
+		}
52
+	}
53
+	return nil
42 54
 }
43 55
 
44
-// 设置微服务版本号
45
-func (c *Config) SetServiceVersion(version string) {
56
+// GetDatabase 获取默认数据库配置(向后兼容)
57
+func (c *Config) GetDatabase() *subconfigs.DatabaseConfig {
58
+	dbs := c.GetDatabases()
59
+	if dbs == nil {
60
+		logger.Errorf("获取默认数据库连接配置发生错误:配置为空!")
61
+		return nil
62
+	}
63
+	return dbs.GetDefaultDatabase()
64
+}
46 65
 
47
-	c.GetService().ServiceVersion = version
66
+// GetDatabaseConfig 按名称获取数据库配置
67
+func (c *Config) GetDatabaseConfig(name string) *subconfigs.DatabaseConfig {
68
+	dbs := c.GetDatabases()
69
+	if dbs == nil {
70
+		return nil
71
+	}
72
+	return dbs.GetDatabase(name)
48 73
 }
49 74
 
50
-// Config 主配置结构体 - 访问门面
51
-type Config struct {
52
-	initError error // 初始化错误
75
+// IsDatabaseConfigured 检查默认数据库是否配置
76
+func (c *Config) IsDatabaseConfigured() bool {
77
+	dbs := c.GetDatabases()
78
+	if dbs == nil {
79
+		return false
80
+	}
81
+	return dbs.IsConfigured()
53 82
 }
54 83
 
55
-// 实现IConfig接口 - 从注册表获取配置
56
-func (c *Config) GetDatabase() *subconfigs.DatabaseConfig {
57
-	if config := subconfigs.GetRegisteredConfig("database"); config != nil {
58
-		return config.(*subconfigs.DatabaseConfig)
84
+// IsDatabaseConfiguredByName 检查指定名称的数据库是否配置
85
+func (c *Config) IsDatabaseConfiguredByName(name string) bool {
86
+	dbs := c.GetDatabases()
87
+	if dbs == nil {
88
+		return false
59 89
 	}
60
-	return nil
90
+	return dbs.IsConfigured()
61 91
 }
62 92
 
93
+// ========== 其他配置方法(保持不变) ==========
94
+
63 95
 func (c *Config) GetRedis() *subconfigs.RedisConfig {
64 96
 	if config := subconfigs.GetRegisteredConfig("redis"); config != nil {
65 97
 		return config.(*subconfigs.RedisConfig)
@@ -120,17 +152,45 @@ func (c *Config) GetInitError() error {
120 152
 	return c.initError
121 153
 }
122 154
 
123
-// GetService 包级便捷函数 - 获取服务配置
124
-func GetService() *subconfigs.ServiceConfig {
125
-	// 直接使用全局的 cfgInstance,假设初始化已完成
155
+// 实现 IConfig 接口的其他方法
156
+func (c *Config) IsDorisConfigured() bool {
157
+	doris := c.GetDoris()
158
+	return doris != nil && doris.IsConfigured()
159
+}
160
+
161
+func (c *Config) IsRedisConfigured() bool {
162
+	redis := c.GetRedis()
163
+	return redis != nil && redis.IsConfigured()
164
+}
165
+
166
+// 设置方法
167
+func (c *Config) SetEnv(name string) {
168
+	if c.GetService().Env == "" {
169
+		c.GetService().Env = name
170
+	}
171
+}
172
+
173
+func (c *Config) SetServiceName(name string) {
174
+	if c.GetService().ServiceName == "" {
175
+		c.GetService().ServiceName = name
176
+	}
177
+}
178
+
179
+func (c *Config) SetServiceVersion(version string) {
180
+	c.GetService().ServiceVersion = version
181
+}
182
+
183
+// ========== 包级便捷函数(更新) ==========
184
+
185
+// GetDatabases 包级便捷函数 - 获取所有数据库配置
186
+func GetDatabases() *subconfigs.DatabasesConfig {
126 187
 	if cfgInstance == nil {
127
-		// 这种情况不应该发生,但为了安全返回默认值
128
-		return &subconfigs.ServiceConfig{}
188
+		return nil
129 189
 	}
130
-	return cfgInstance.GetService()
190
+	return cfgInstance.GetDatabases()
131 191
 }
132 192
 
133
-// GetDatabase 包级便捷函数 - 获取数据库配置
193
+// GetDatabase 包级便捷函数 - 获取默认数据库配置(向后兼容)
134 194
 func GetDatabase() *subconfigs.DatabaseConfig {
135 195
 	if cfgInstance == nil {
136 196
 		return &subconfigs.DatabaseConfig{}
@@ -138,6 +198,14 @@ func GetDatabase() *subconfigs.DatabaseConfig {
138 198
 	return cfgInstance.GetDatabase()
139 199
 }
140 200
 
201
+// GetDatabaseConfig 包级便捷函数 - 按名称获取数据库配置
202
+func GetDatabaseConfig(name string) *subconfigs.DatabaseConfig {
203
+	if cfgInstance == nil {
204
+		return nil
205
+	}
206
+	return cfgInstance.GetDatabaseConfig(name)
207
+}
208
+
141 209
 // GetRedis 包级便捷函数 - 获取Redis配置
142 210
 func GetRedis() *subconfigs.RedisConfig {
143 211
 	if cfgInstance == nil {
@@ -167,7 +235,6 @@ func GetMicro() *subconfigs.MicroConfig {
167 235
 		return &subconfigs.MicroConfig{}
168 236
 	}
169 237
 	return cfgInstance.GetMicro()
170
-
171 238
 }
172 239
 
173 240
 func GetAuth() *subconfigs.AuthConfig {
@@ -177,6 +244,15 @@ func GetAuth() *subconfigs.AuthConfig {
177 244
 	return cfgInstance.GetAuth()
178 245
 }
179 246
 
247
+func GetService() *subconfigs.ServiceConfig {
248
+	if cfgInstance == nil {
249
+		return &subconfigs.ServiceConfig{}
250
+	}
251
+	return cfgInstance.GetService()
252
+}
253
+
254
+// ========== 单例管理 ==========
255
+
180 256
 var (
181 257
 	cfgInstance *Config
182 258
 	once        sync.Once
@@ -201,21 +277,3 @@ func GetConfigWithError() (IConfig, error) {
201 277
 	cfg, err := GetConfig()
202 278
 	return cfg, err
203 279
 }
204
-
205
-// 实现 IConfig 接口的 IsDatabaseConfigured 方法
206
-func (c *Config) IsDatabaseConfigured() bool {
207
-
208
-	return c.GetDatabase().IsConfigured()
209
-}
210
-
211
-// 实现 IConfig 接口的 IsDorisConfigured 方法
212
-func (c *Config) IsDorisConfigured() bool {
213
-
214
-	return c.GetDoris().IsConfigured()
215
-}
216
-
217
-// 实现 IConfig 接口的 IsDorisConfigured 方法
218
-func (c *Config) IsRedisConfigured() bool {
219
-
220
-	return c.GetRedis().IsConfigured()
221
-}

+ 222
- 15
config/subconfigs/database_config.go Visa fil

@@ -1,6 +1,14 @@
1 1
 package subconfigs
2 2
 
3
-import "fmt"
3
+import (
4
+	"fmt"
5
+	"log"
6
+)
7
+
8
+type DatabasesConfig struct {
9
+	BaseConfig
10
+	Databases map[string]*DatabaseConfig `yaml:"databases"`
11
+}
4 12
 
5 13
 // DatabaseConfig 数据库配置
6 14
 type DatabaseConfig struct {
@@ -16,39 +24,238 @@ type DatabaseConfig struct {
16 24
 	ConnMaxLifetime int    `yaml:"conn_max_lifetime"`
17 25
 }
18 26
 
19
-// NewDatabaseConfig 创建数据库配置实例
20
-func NewDatabaseConfig() *DatabaseConfig {
21
-	return &DatabaseConfig{}
27
+// ========== DatabasesConfig 实现 ConfigLoader 接口 ==========
28
+
29
+// SetDefaults 设置默认值 - 实现 ConfigLoader 接口
30
+func (c *DatabasesConfig) SetDefaults() {
31
+	if c.Databases == nil {
32
+		c.Databases = make(map[string]*DatabaseConfig)
33
+		log.Println("⚠️  数据库配置为空,已初始化空的数据库配置")
34
+		return
35
+	}
36
+
37
+	// 为每个数据库配置设置默认值
38
+	for _, db := range c.Databases {
39
+		if db != nil {
40
+			db.SetDefaults()
41
+		}
42
+	}
43
+}
44
+
45
+func (c *DatabasesConfig) Load(data map[string]interface{}) error {
46
+	// 初始化
47
+	c.Databases = make(map[string]*DatabaseConfig)
48
+
49
+	// 遍历所有数据库配置
50
+	for dbName, dbData := range data {
51
+		dbDataMap, ok := dbData.(map[interface{}]interface{})
52
+		if !ok {
53
+			return fmt.Errorf("database '%s' config is not a map", dbName)
54
+		}
55
+
56
+		// 转换为 map[string]interface{}
57
+		stringMap := make(map[string]interface{})
58
+		for k, v := range dbDataMap {
59
+			if key, ok := k.(string); ok {
60
+				stringMap[key] = v
61
+			}
62
+		}
63
+
64
+		// 加载单个数据库配置
65
+		dbConfig := &DatabaseConfig{}
66
+		if err := dbConfig.Load(stringMap); err != nil {
67
+			return fmt.Errorf("failed to load database '%s' config: %v", dbName, err)
68
+		}
69
+
70
+		c.Databases[dbName] = dbConfig
71
+	}
72
+
73
+	// 设置默认值和验证
74
+	c.SetDefaults()
75
+	return c.Validate()
22 76
 }
23 77
 
24
-// SetDefaults 设置默认值
78
+// ========== DatabaseConfig 实现 ConfigLoader 接口 ==========
79
+
80
+// SetDefaults 设置默认值 - 实现 ConfigLoader 接口
25 81
 func (c *DatabaseConfig) SetDefaults() {
26
-	c.Type = "mysql"
27
-	c.Port = 3306
28
-	c.MaxOpenConns = 100
29
-	c.MaxIdleConns = 20
30
-	c.ConnMaxLifetime = 3600
82
+	if c.Type == "" {
83
+		c.Type = "mysql"
84
+	}
85
+	if c.Port == 0 {
86
+		c.Port = 3306
87
+	}
88
+	if c.MaxOpenConns == 0 {
89
+		c.MaxOpenConns = 100
90
+	}
91
+	if c.MaxIdleConns == 0 {
92
+		c.MaxIdleConns = 20
93
+	}
94
+	if c.ConnMaxLifetime == 0 {
95
+		c.ConnMaxLifetime = 3600
96
+	}
31 97
 }
32 98
 
33
-// Load 从yaml数据加载
99
+// Load 从yaml数据加载 - 实现 ConfigLoader 接口
34 100
 func (c *DatabaseConfig) Load(data map[string]interface{}) error {
101
+	// 虽然可能不会被直接调用,但为了接口完整性还是要实现
35 102
 	return c.LoadFromYAML(data, c)
36 103
 }
37 104
 
38
-// Validate 验证配置
105
+// ========== 业务方法 ==========
106
+
107
+// Validate 验证配置(数据库配置)
39 108
 func (c *DatabaseConfig) Validate() error {
40 109
 	if c.Type == "" {
41 110
 		return fmt.Errorf("database type is required")
42 111
 	}
112
+	if c.Host == "" {
113
+		return fmt.Errorf("database host is required")
114
+	}
115
+	if c.Port <= 0 || c.Port > 65535 {
116
+		return fmt.Errorf("invalid database port: %d", c.Port)
117
+	}
118
+	if c.Database == "" {
119
+		return fmt.Errorf("database name is required")
120
+	}
121
+	return nil
122
+}
123
+
124
+// Validate 验证配置(多数据库配置)
125
+func (c *DatabasesConfig) Validate() error {
126
+	if c.Databases == nil || len(c.Databases) == 0 {
127
+		log.Println("❌ 错误: 未找到任何数据库配置,请检查配置文件中的 'databases' 配置")
128
+		return fmt.Errorf("no database configurations found")
129
+	}
130
+
131
+	// 必须要有默认数据库
132
+	if _, exists := c.Databases["default"]; !exists {
133
+		availableDBs := c.GetAllDatabaseNames()
134
+		log.Printf("❌ 错误: 默认数据库未找到。可用的数据库: %v。请在配置文件中添加 'default' 数据库", availableDBs)
135
+		return fmt.Errorf("default database not found")
136
+	}
137
+
138
+	// 验证每个数据库配置
139
+	for name, db := range c.Databases {
140
+		if db == nil {
141
+			log.Printf("❌ 错误: 数据库 '%s' 配置为空", name)
142
+			return fmt.Errorf("database '%s' configuration is nil", name)
143
+		}
144
+
145
+		if err := db.Validate(); err != nil {
146
+			log.Printf("❌ 错误: 数据库 '%s' 验证失败: %v", name, err)
147
+			return fmt.Errorf("database '%s' validation failed: %v", name, err)
148
+		}
149
+	}
150
+
151
+	log.Printf("✅ 数据库配置验证通过,找到 %d 个数据库配置", len(c.Databases))
43 152
 	return nil
44 153
 }
45 154
 
46
-// IsConfigured 判断是否已配置
155
+// IsConfigured 判断是否已配置(数据库配置)
47 156
 func (c *DatabaseConfig) IsConfigured() bool {
48
-	return c.Host != "" && c.Port > 0 && c.Username != "" && c.Database != ""
157
+	if c.Host == "" {
158
+		log.Println("⚠️  警告: 数据库 Host 未配置")
159
+		return false
160
+	}
161
+	if c.Port <= 0 {
162
+		log.Println("⚠️  警告: 数据库 Port 未配置或无效")
163
+		return false
164
+	}
165
+	if c.Username == "" {
166
+		log.Println("⚠️  警告: 数据库 Username 未配置")
167
+		return false
168
+	}
169
+	if c.Database == "" {
170
+		log.Println("⚠️  警告: 数据库名称未配置")
171
+		return false
172
+	}
173
+	return true
174
+}
175
+
176
+// IsConfigured 判断是否已配置(多数据库配置)
177
+func (c *DatabasesConfig) IsConfigured() bool {
178
+	defaultDB := c.GetDefaultDatabase()
179
+	if defaultDB == nil {
180
+		log.Println("❌ 错误: 默认数据库不存在")
181
+		return false
182
+	}
183
+
184
+	if !defaultDB.IsConfigured() {
185
+		log.Println("❌ 错误: 默认数据库配置不完整")
186
+		return false
187
+	}
188
+
189
+	return true
190
+}
191
+
192
+// GetDefaultDatabase 获取默认数据库(必须存在)
193
+func (c *DatabasesConfig) GetDefaultDatabase() *DatabaseConfig {
194
+	if c.Databases == nil {
195
+		log.Println("❌ 错误: 尝试获取默认数据库时,数据库配置为空")
196
+		return nil
197
+	}
198
+
199
+	if db, exists := c.Databases["default"]; exists {
200
+		return db
201
+	}
202
+
203
+	// 如果没有名为default的,尝试返回第一个(但会记录警告)
204
+	for name, db := range c.Databases {
205
+		log.Printf("⚠️  警告: 未找到名为 'default' 的数据库,使用第一个数据库 '%s' 作为默认", name)
206
+		return db
207
+	}
208
+
209
+	log.Println("❌ 错误: 数据库中没有任何配置")
210
+	return nil
211
+}
212
+
213
+// GetDatabase 按名称获取数据库(为空时记录错误)
214
+func (c *DatabasesConfig) GetDatabase(name string) *DatabaseConfig {
215
+	if c.Databases == nil {
216
+		log.Printf("❌ 错误: 尝试获取数据库 '%s' 时,数据库配置为空", name)
217
+		return nil
218
+	}
219
+
220
+	db := c.Databases[name]
221
+	if db == nil {
222
+		availableDBs := c.GetAllDatabaseNames()
223
+		log.Printf("❌ 错误: 数据库 '%s' 不存在。可用的数据库: %v", name, availableDBs)
224
+	}
225
+
226
+	return db
227
+}
228
+
229
+// GetAllDatabaseNames 获取所有数据库名称
230
+func (c *DatabasesConfig) GetAllDatabaseNames() []string {
231
+	if c.Databases == nil {
232
+		log.Println("⚠️  警告: 尝试获取数据库名称时,数据库配置为空")
233
+		return []string{}
234
+	}
235
+
236
+	names := make([]string, 0, len(c.Databases))
237
+	for name := range c.Databases {
238
+		names = append(names, name)
239
+	}
240
+	return names
241
+}
242
+
243
+// GetDatabaseNamesWithoutDefault 获取除default外的所有数据库名称
244
+func (c *DatabasesConfig) GetDatabaseNamesWithoutDefault() []string {
245
+	if c.Databases == nil {
246
+		return []string{}
247
+	}
248
+
249
+	names := make([]string, 0, len(c.Databases)-1)
250
+	for name := range c.Databases {
251
+		if name != "default" {
252
+			names = append(names, name)
253
+		}
254
+	}
255
+	return names
49 256
 }
50 257
 
51 258
 // 自动注册
52 259
 func init() {
53
-	Register("database", &DatabaseConfig{})
260
+	Register("databases", &DatabasesConfig{})
54 261
 }

Loading…
Avbryt
Spara