Browse Source

多数据库测试通过

qdy 2 months ago
parent
commit
97bf8b49eb

BIN
bootstraps/.DS_Store View File


+ 0
- 403
bootstraps/bootstrap.go View File

@@ -1,403 +0,0 @@
1
-package bootstraps
2
-
3
-// import (
4
-// 	"context"
5
-// 	"fmt"
6
-// 	"log"
7
-// 	"net/http"
8
-// 	"os"
9
-// 	"os/signal"
10
-// 	"syscall"
11
-// 	"time"
12
-
13
-// 	"git.x2erp.com/qdy/go-base/client"
14
-// 	"git.x2erp.com/qdy/go-base/config"
15
-// 	"git.x2erp.com/qdy/go-base/consul"
16
-// 	"git.x2erp.com/qdy/go-base/logger"
17
-// )
18
-
19
-// // 定义接口
20
-// type ShutdownHandler interface {
21
-// 	OnShutdown()
22
-// }
23
-
24
-// // WebService Web服务实例
25
-// type WebService struct {
26
-// 	serviceName     string
27
-// 	HttpServer      *http.Server
28
-// 	Router          *http.ServeMux
29
-// 	quit            chan os.Signal
30
-// 	shutdownHandler ShutdownHandler
31
-// 	Port            int
32
-// 	Ip              string
33
-// }
34
-
35
-// // Bootstrapper 服务启动器
36
-// type Bootstrapper struct {
37
-// 	appName    string
38
-// 	appVersion string
39
-// 	Cfg        config.IConfig
40
-// 	services   map[string]*WebService // 存储多个服务实例
41
-// 	quit       chan os.Signal
42
-// }
43
-
44
-// // NewBootstrapper 创建启动器
45
-// func NewBootstrapper(name, version string) *Bootstrapper {
46
-// 	b := &Bootstrapper{
47
-// 		appName:    name,
48
-// 		appVersion: version,
49
-// 		quit:       make(chan os.Signal, 1),
50
-// 		services:   make(map[string]*WebService),
51
-// 	}
52
-
53
-// 	b.init()
54
-
55
-// 	return b
56
-// }
57
-
58
-// // Init 初始化配置和日志
59
-// func (b *Bootstrapper) init() *Bootstrapper {
60
-// 	// 1. 初始化启动日志
61
-// 	if err := logger.InitBootLog(b.appName); err != nil {
62
-// 		log.Fatal("无法初始化启动日志: ", err)
63
-// 	}
64
-
65
-// 	// 2. 加载配置
66
-// 	log.Println("正在加载配置...")
67
-// 	cfg, err := config.GetConfig()
68
-// 	if err != nil {
69
-// 		log.Fatalf("加载配置失败: %v", err)
70
-// 	}
71
-// 	cfg.SetAppName(b.appName)
72
-// 	cfg.SetAppVersion(b.appVersion)
73
-// 	b.Cfg = cfg
74
-
75
-// 	// 3. 初始化运行时日志
76
-// 	logger.InitRuntimeLogger(b.appName, b.appVersion, b.Cfg.GetLog())
77
-// 	log.Println("配置和日志初始化完成")
78
-
79
-// 	return b
80
-// }
81
-
82
-// // CreateService 创建Web服务实例
83
-// func (b *Bootstrapper) createService() *WebService {
84
-
85
-// 	serviceCfg := b.Cfg.GetServiceConfig()
86
-
87
-// 	// 检查是否已存在同名服务
88
-// 	if _, exists := b.services[yamlNode]; exists {
89
-// 		logger.Debug("服务 %s 已存在,将返回现有实例", yamlNode)
90
-// 		return b.services[yamlNode]
91
-// 	}
92
-
93
-// 	io := client.GetServiceIP("")
94
-// 	// 创建新的服务实例
95
-// 	ws := &WebService{
96
-// 		serviceName: serviceCfg.ServiceName,
97
-// 		Router:      http.NewServeMux(),
98
-// 		quit:        make(chan os.Signal, 1),
99
-// 		Port:        serviceCfg.Port,
100
-// 		Ip:          io,
101
-// 	}
102
-
103
-// 	// 创建HTTP服务器
104
-// 	ws.HttpServer = &http.Server{
105
-// 		Addr:         fmt.Sprintf(":%d", serviceCfg.Port),
106
-// 		Handler:      ws.Router,
107
-// 		ReadTimeout:  15 * time.Second,
108
-// 		WriteTimeout: 15 * time.Second,
109
-// 		IdleTimeout:  60 * time.Second,
110
-// 	}
111
-
112
-// 	// 存储服务实例
113
-// 	b.services[yamlNode] = ws
114
-
115
-// 	logger.Debug("已创建服务实例: %s (端口: %d)", serviceCfg.ServiceName, serviceCfg.Port)
116
-
117
-// 	return ws
118
-// }
119
-
120
-// // GetAllServices 获取所有服务实例
121
-// func (b *Bootstrapper) GetAllServices() map[string]*WebService {
122
-// 	return b.services
123
-// }
124
-
125
-// // StartService 启动指定服务
126
-// func (b *Bootstrapper) StartService(yamlNode string) *Bootstrapper {
127
-
128
-// 	service := b.createService(yamlNode)
129
-
130
-// 	log.Printf("正在启动服务: %s ", service.serviceName)
131
-// 	log.Printf("模式: 独立运行 (net/http)")
132
-// 	log.Printf("服务端口: :%d", service.Port)
133
-
134
-// 	return b
135
-// }
136
-
137
-// // Run 运行所有服务
138
-// func (b *Bootstrapper) Run(shutdownHandler ShutdownHandler) {
139
-// 	if len(b.services) == 0 {
140
-// 		log.Fatal("没有要运行的服务,请先创建服务实例")
141
-// 	}
142
-
143
-// 	log.Printf("开始运行 %d 个服务...", len(b.services))
144
-
145
-// 	// 设置全局信号监听
146
-// 	signal.Notify(b.quit, syscall.SIGINT, syscall.SIGTERM)
147
-
148
-// 	// 启动所有服务
149
-// 	for serviceName, service := range b.services {
150
-// 		go func(name string, ws *WebService) {
151
-
152
-// 			consul.Register(service.serviceName, service.Ip, service.Port, b.Cfg.GetConsulConfig())
153
-
154
-// 			log.Printf("启动服务 %s 在 %s", name, ws.HttpServer.Addr)
155
-// 			if err := ws.HttpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
156
-// 				log.Fatalf("服务 %s 运行失败: %v", name, err)
157
-// 			}
158
-// 		}(serviceName, service)
159
-// 	}
160
-
161
-// 	// 等待中断信号
162
-// 	b.waitForShutdown(shutdownHandler)
163
-// }
164
-
165
-// // RunSingle 运行单个服务
166
-// func (b *Bootstrapper) RunSingle(yamlNode string, shutdownHandler ShutdownHandler) {
167
-// 	service := b.createService(yamlNode)
168
-
169
-// 	log.Printf("服务 %s 开始运行...", service.serviceName)
170
-
171
-// 	// 设置服务级别的信号监听
172
-// 	signal.Notify(service.quit, syscall.SIGINT, syscall.SIGTERM)
173
-
174
-// 	// 启动服务
175
-// 	go func() {
176
-// 		log.Printf("服务器启动在 %s", service.HttpServer.Addr)
177
-// 		if err := service.HttpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
178
-// 			log.Fatalf("服务运行失败: %v", err)
179
-// 		}
180
-// 	}()
181
-
182
-// 	consul.Register(service.serviceName, service.Ip, service.Port, b.Cfg.GetConsulConfig())
183
-
184
-// 	// 等待中断信号
185
-// 	b.waitForServiceShutdown(service, shutdownHandler)
186
-// }
187
-
188
-// // RunTLS 运行HTTPS服务
189
-// func (b *Bootstrapper) RunTLS(serviceName, certFile, keyFile string, shutdownHandler ShutdownHandler) {
190
-// 	service := b.createService(serviceName)
191
-
192
-// 	log.Printf("服务 %s 开始运行(HTTPS)...", service.serviceName)
193
-
194
-// 	// 设置服务级别的信号监听
195
-// 	signal.Notify(service.quit, syscall.SIGINT, syscall.SIGTERM)
196
-
197
-// 	// 启动HTTPS服务器
198
-// 	go func() {
199
-// 		log.Printf("HTTPS服务器启动在 %s", service.HttpServer.Addr)
200
-// 		if err := service.HttpServer.ListenAndServeTLS(certFile, keyFile); err != nil && err != http.ErrServerClosed {
201
-// 			log.Fatalf("服务运行失败: %v", err)
202
-// 		}
203
-// 	}()
204
-
205
-// 	// 等待中断信号
206
-// 	b.waitForServiceShutdown(service, shutdownHandler)
207
-// }
208
-
209
-// // waitForShutdown 等待关闭信号(关闭所有服务)
210
-// func (b *Bootstrapper) waitForShutdown(handler ShutdownHandler) {
211
-// 	log.Println("按 Ctrl+C 停止所有服务")
212
-
213
-// 	// 等待信号
214
-// 	<-b.quit
215
-// 	log.Println("接收到终止信号,正在优雅关闭所有服务...")
216
-
217
-// 	// 创建关闭上下文,给30秒完成当前请求
218
-// 	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
219
-// 	defer cancel()
220
-
221
-// 	// 关闭所有服务
222
-// 	shutdownComplete := make(chan bool, len(b.services))
223
-
224
-// 	for serviceName, service := range b.services {
225
-// 		go func(name string, ws *WebService) {
226
-// 			log.Printf("正在关闭服务: %s", name)
227
-// 			//退出注册中心
228
-// 			consul.Deregister(service.serviceName, service.Ip, service.Port, b.Cfg.GetConsulConfig())
229
-
230
-// 			if err := ws.HttpServer.Shutdown(ctx); err != nil {
231
-// 				log.Printf("服务 %s 关闭失败: %v", name, err)
232
-// 			} else {
233
-// 				log.Printf("服务 %s 已关闭", name)
234
-// 			}
235
-// 			shutdownComplete <- true
236
-// 		}(serviceName, service)
237
-// 	}
238
-
239
-// 	// 等待所有服务关闭完成
240
-// 	for i := 0; i < len(b.services); i++ {
241
-// 		<-shutdownComplete
242
-// 	}
243
-
244
-// 	// 执行关闭处理
245
-// 	if handler != nil {
246
-// 		handler.OnShutdown()
247
-// 	}
248
-
249
-// 	// 停止日志写入
250
-// 	logger.StopESWriter()
251
-// 	log.Println("所有服务优雅关闭完成")
252
-
253
-// 	// 等待一小段时间确保日志写入完成
254
-// 	time.Sleep(100 * time.Millisecond)
255
-// 	os.Exit(0)
256
-// }
257
-
258
-// // waitForServiceShutdown 等待单个服务关闭
259
-// func (b *Bootstrapper) waitForServiceShutdown(service *WebService, handler ShutdownHandler) {
260
-
261
-// 	log.Printf("按 Ctrl+C 停止服务 %s", service.serviceName)
262
-
263
-// 	// 等待信号
264
-// 	<-service.quit
265
-// 	log.Printf("接收到终止信号,正在优雅关闭服务 %s...", service.serviceName)
266
-
267
-// 	//退出注册中心
268
-// 	consul.Deregister(service.serviceName, service.Ip, service.Port, b.Cfg.GetConsulConfig())
269
-
270
-// 	// 创建关闭上下文,给30秒完成当前请求
271
-// 	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
272
-// 	defer cancel()
273
-
274
-// 	// 停止接收新请求,完成当前请求
275
-// 	if err := service.HttpServer.Shutdown(ctx); err != nil {
276
-// 		log.Printf("服务 %s 关闭失败: %v", service.serviceName, err)
277
-// 	} else {
278
-// 		log.Printf("服务 %s 已关闭", service.serviceName)
279
-// 	}
280
-
281
-// 	// 执行关闭处理
282
-// 	if handler != nil {
283
-// 		handler.OnShutdown()
284
-// 	}
285
-
286
-// 	// 停止日志写入
287
-// 	logger.StopESWriter()
288
-// 	log.Printf("服务 %s 优雅关闭完成", service.serviceName)
289
-
290
-// 	// 等待一小段时间确保日志写入完成
291
-// 	time.Sleep(100 * time.Millisecond)
292
-// 	os.Exit(0)
293
-// }
294
-
295
-// // GetConfig 获取配置
296
-// func (b *Bootstrapper) GetConfig() config.IConfig {
297
-// 	return b.Cfg
298
-// }
299
-
300
-// // GetWebService 获取指定服务的路由器
301
-// func (b *Bootstrapper) GetRouter(yamlNode string) *http.ServeMux {
302
-// 	service := b.createService(yamlNode)
303
-// 	return service.Router
304
-// }
305
-
306
-// // GetRouterDefault 获取第一个服务的路由器(兼容性方法)
307
-// func (b *Bootstrapper) GetRouterDefault() *http.ServeMux {
308
-// 	for _, service := range b.services {
309
-// 		return service.Router
310
-// 	}
311
-// 	// 如果没有服务,创建默认路由器(向后兼容)
312
-// 	return http.NewServeMux()
313
-// }
314
-
315
-// // // Handle 注册路由处理器到指定服务
316
-// // func (b *Bootstrapper) Handle(serviceName, pattern string, handler http.Handler) bool {
317
-// // 	if service, exists := b.services[serviceName]; exists {
318
-// // 		service.Router.Handle(pattern, handler)
319
-// // 		return true
320
-// // 	}
321
-// // 	log.Printf("错误: 服务 %s 不存在", serviceName)
322
-// // 	return false
323
-// // }
324
-
325
-// // SetShutdownHandler 为服务设置关闭处理器
326
-// func (b *Bootstrapper) SetShutdownHandler(serviceName string, handler ShutdownHandler) bool {
327
-// 	if service, exists := b.services[serviceName]; exists {
328
-// 		service.shutdownHandler = handler
329
-// 		return true
330
-// 	}
331
-// 	return false
332
-// }
333
-
334
-// // ========== WebService 的方法 ==========
335
-
336
-// // GetServiceName 获取服务名称
337
-// // func (ws *WebService) GetServiceName() string {
338
-// // 	return ws.serviceName
339
-// // }
340
-
341
-// // // GetServiceConfig 获取服务配置
342
-// // func (ws *WebService) GetServiceConfig() *subconfigs.ServiceConfig {
343
-// // 	return ws.serviceConfig
344
-// // }
345
-
346
-// // GetRouter 获取服务的路由器
347
-// // func (ws *WebService) GetRouter() *http.ServeMux {
348
-// // 	return ws.Router
349
-// // }
350
-
351
-// // Handle 注册路由处理器
352
-// // func (ws *WebService) Handle(pattern string, handler http.Handler) {
353
-// // 	ws.Router.Handle(pattern, handler)
354
-// // }
355
-
356
-// // SetShutdownHandler 设置关闭处理器
357
-// func (ws *WebService) SetShutdownHandler(handler ShutdownHandler) {
358
-// 	ws.shutdownHandler = handler
359
-// }
360
-
361
-// // Run 运行单个服务
362
-// func (ws *WebService) Run(shutdownHandler ShutdownHandler) {
363
-// 	log.Printf("服务 %s 开始运行...", ws.serviceName)
364
-
365
-// 	// 设置信号监听
366
-// 	signal.Notify(ws.quit, syscall.SIGINT, syscall.SIGTERM)
367
-
368
-// 	// 启动服务
369
-// 	go func() {
370
-// 		log.Printf("服务器启动在 %s", ws.HttpServer.Addr)
371
-// 		if err := ws.HttpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
372
-// 			log.Fatalf("服务 %s 运行失败: %v", ws.serviceName, err)
373
-// 		}
374
-// 	}()
375
-
376
-// 	// 等待中断信号
377
-// 	log.Printf("按 Ctrl+C 停止服务 %s", ws.serviceName)
378
-// 	<-ws.quit
379
-
380
-// 	log.Printf("接收到终止信号,正在优雅关闭服务 %s...", ws.serviceName)
381
-
382
-// 	// 创建关闭上下文
383
-// 	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
384
-// 	defer cancel()
385
-
386
-// 	// 关闭服务
387
-// 	if err := ws.HttpServer.Shutdown(ctx); err != nil {
388
-// 		log.Printf("服务 %s 关闭失败: %v", ws.serviceName, err)
389
-// 	} else {
390
-// 		log.Printf("服务 %s 已关闭", ws.serviceName)
391
-// 	}
392
-
393
-// 	// 执行关闭处理
394
-// 	if shutdownHandler != nil {
395
-// 		shutdownHandler.OnShutdown()
396
-// 	} else if ws.shutdownHandler != nil {
397
-// 		ws.shutdownHandler.OnShutdown()
398
-// 	}
399
-
400
-// 	log.Printf("服务 %s 优雅关闭完成", ws.serviceName)
401
-// 	time.Sleep(100 * time.Millisecond)
402
-// 	os.Exit(0)
403
-// }

+ 0
- 39
bootstraps/shutdown.go View File

@@ -1,39 +0,0 @@
1
-package bootstraps
2
-
3
-// import (
4
-// 	"os"
5
-// 	"os/signal"
6
-// 	"syscall"
7
-// 	"time"
8
-
9
-// 	"git.x2erp.com/qdy/go-base/logger"
10
-// 	"git.x2erp.com/qdy/go-db/factory/database"
11
-// )
12
-
13
-// // setupGracefulShutdown 设置优雅关闭
14
-// func SetupGracefulShutdown(dbFactory *database.DBFactory, cleanupFuncs ...func()) {
15
-// 	signalCh := make(chan os.Signal, 1)
16
-// 	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
17
-
18
-// 	go func() {
19
-// 		<-signalCh
20
-// 		logger.Info("\n接收到关闭信号,正在优雅关闭...")
21
-
22
-// 		// 关闭数据库连接
23
-// 		if dbFactory != nil {
24
-// 			if err := dbFactory.Close(); err != nil {
25
-// 				logger.Error("关闭数据库错误: %v", err)
26
-// 			}
27
-// 			logger.Info("数据库连接已关闭")
28
-// 		}
29
-
30
-// 		// 执行其他清理函数
31
-// 		for _, cleanup := range cleanupFuncs {
32
-// 			cleanup()
33
-// 		}
34
-
35
-// 		// 等待日志写入完成
36
-// 		time.Sleep(100 * time.Millisecond)
37
-// 		os.Exit(0)
38
-// 	}()
39
-// }

+ 38
- 2
config/config.go View File

@@ -20,7 +20,8 @@ const (
20 20
 // IConfig 主配置接口(更新支持多数据库)
21 21
 type IConfig interface {
22 22
 	// 多数据库支持
23
-	GetDatabaseConfig() *subconfigs.DatabaseConfig // 获取默认数据库(向后兼容)
23
+	GetDatabaseConfig() *subconfigs.DatabaseConfig               // 获取默认数据库(向后兼容)
24
+	GetDbsConfig(name string) (*subconfigs.DatabaseConfig, bool) // 获取指定数据库连接
24 25
 
25 26
 	//服务支持
26 27
 	GetServiceConfig() *subconfigs.ServiceConfig //获取默认微服务配置
@@ -67,6 +68,33 @@ func (c *Config) GetDatabaseConfig() *subconfigs.DatabaseConfig {
67 68
 	return nil
68 69
 }
69 70
 
71
+// GetDbsConfig 按名称获取数据库配置
72
+func (c *Config) GetDbsConfig(name string) (*subconfigs.DatabaseConfig, bool) {
73
+	// 获取 dbs 配置
74
+	dbsConfig := subconfigs.GetRegisteredConfig("dbs")
75
+	if dbsConfig == nil {
76
+		return nil, false
77
+	}
78
+
79
+	// 类型断言为 DbsConfig
80
+	dbs, ok := dbsConfig.(*subconfigs.DbsConfig)
81
+	if !ok {
82
+		return nil, false
83
+	}
84
+
85
+	// 按名称获取 DbConfig
86
+	dbConfig, exists := dbs.GetDbConfig(name)
87
+	if !exists || dbConfig == nil {
88
+		return nil, exists
89
+	}
90
+
91
+	// 将 DbConfig 转换为 DatabaseConfig
92
+	return &subconfigs.DatabaseConfig{
93
+		BaseConfig: subconfigs.BaseConfig{},
94
+		DbConfig:   *dbConfig,
95
+	}, true
96
+}
97
+
70 98
 func (c *Config) GetRedisConfig() *subconfigs.RedisConfig {
71 99
 	if config := subconfigs.GetRegisteredConfig("redis"); config != nil {
72 100
 		return config.(*subconfigs.RedisConfig)
@@ -185,13 +213,21 @@ func (c *Config) GetAppAuthToken() string {
185 213
 }
186 214
 
187 215
 // GetDatabaseConfig 包级便捷函数 - 按名称获取数据库配置
188
-func GetDatabaseConfig(name string) *subconfigs.DatabaseConfig {
216
+func GetDatabaseConfig() *subconfigs.DatabaseConfig {
189 217
 	if cfgInstance == nil {
190 218
 		return nil
191 219
 	}
192 220
 	return cfgInstance.GetDatabaseConfig()
193 221
 }
194 222
 
223
+// GetDbsConfig 包级便捷函数 - 按名称获取数据库配置
224
+func GetDbsConfig(name string) (*subconfigs.DatabaseConfig, bool) {
225
+	if cfgInstance == nil {
226
+		return nil, false
227
+	}
228
+	return cfgInstance.GetDbsConfig(name)
229
+}
230
+
195 231
 // GetRedis 包级便捷函数 - 获取Redis配置
196 232
 func GetRedisConfig() *subconfigs.RedisConfig {
197 233
 	if cfgInstance == nil {

+ 92
- 0
config/subconfigs/database_common.go View File

@@ -0,0 +1,92 @@
1
+package subconfigs
2
+
3
+import (
4
+	"fmt"
5
+	"log"
6
+)
7
+
8
+// DbConfig 单个数据库配置(共享结构)
9
+type DbConfig struct {
10
+	Type            string `yaml:"type"`
11
+	Host            string `yaml:"host"`
12
+	Port            int    `yaml:"port"`
13
+	Username        string `yaml:"username"`
14
+	Password        string `yaml:"password"`
15
+	Database        string `yaml:"database"`
16
+	MaxOpenConns    int    `yaml:"max_open_conns"`
17
+	MaxIdleConns    int    `yaml:"max_idle_conns"`
18
+	ConnMaxLifetime int    `yaml:"conn_max_lifetime"`
19
+}
20
+
21
+// SetDbDefaults 设置数据库配置默认值
22
+func SetDbDefaults(c *DbConfig) {
23
+	if c.Type == "" {
24
+		c.Type = "mysql"
25
+	}
26
+	if c.Port == 0 {
27
+		c.Port = 3306
28
+	}
29
+	if c.MaxOpenConns == 0 {
30
+		c.MaxOpenConns = 100
31
+	}
32
+	if c.MaxIdleConns == 0 {
33
+		c.MaxIdleConns = 20
34
+	}
35
+	if c.ConnMaxLifetime == 0 {
36
+		c.ConnMaxLifetime = 3600
37
+	}
38
+}
39
+
40
+// ValidateDbConfig 验证数据库配置
41
+func ValidateDbConfig(c *DbConfig) error {
42
+	if c.Type == "" {
43
+		return fmt.Errorf("database type is required")
44
+	}
45
+	if c.Host == "" {
46
+		return fmt.Errorf("database host is required")
47
+	}
48
+	if c.Port <= 0 || c.Port > 65535 {
49
+		return fmt.Errorf("invalid database port: %d", c.Port)
50
+	}
51
+	if c.Database == "" {
52
+		return fmt.Errorf("database name is required")
53
+	}
54
+	return nil
55
+}
56
+
57
+// IsDbConfigured 判断数据库是否已配置
58
+func IsDbConfigured(c *DbConfig, name string) bool {
59
+	if c.Host == "" {
60
+		if name != "" {
61
+			log.Printf("⚠️  警告: 数据库 '%s' Host 未配置", name)
62
+		} else {
63
+			log.Println("⚠️  警告: 数据库 Host 未配置")
64
+		}
65
+		return false
66
+	}
67
+	if c.Port <= 0 {
68
+		if name != "" {
69
+			log.Printf("⚠️  警告: 数据库 '%s' Port 未配置或无效", name)
70
+		} else {
71
+			log.Println("⚠️  警告: 数据库 Port 未配置或无效")
72
+		}
73
+		return false
74
+	}
75
+	if c.Username == "" {
76
+		if name != "" {
77
+			log.Printf("⚠️  警告: 数据库 '%s' Username 未配置", name)
78
+		} else {
79
+			log.Println("⚠️  警告: 数据库 Username 未配置")
80
+		}
81
+		return false
82
+	}
83
+	if c.Database == "" {
84
+		if name != "" {
85
+			log.Printf("⚠️  警告: 数据库 '%s' 名称未配置", name)
86
+		} else {
87
+			log.Println("⚠️  警告: 数据库名称未配置")
88
+		}
89
+		return false
90
+	}
91
+	return true
92
+}

+ 5
- 66
config/subconfigs/database_config.go View File

@@ -1,88 +1,27 @@
1 1
 package subconfigs
2 2
 
3
-import (
4
-	"fmt"
5
-	"log"
6
-)
7
-
8
-// DatabaseConfig 数据库配置
3
+// DatabaseConfig 单个数据库配置
9 4
 type DatabaseConfig struct {
10 5
 	BaseConfig
11
-	Type            string `yaml:"type"`
12
-	Host            string `yaml:"host"`
13
-	Port            int    `yaml:"port"`
14
-	Username        string `yaml:"username"`
15
-	Password        string `yaml:"password"`
16
-	Database        string `yaml:"database"`
17
-	MaxOpenConns    int    `yaml:"max_open_conns"`
18
-	MaxIdleConns    int    `yaml:"max_idle_conns"`
19
-	ConnMaxLifetime int    `yaml:"conn_max_lifetime"`
6
+	DbConfig `yaml:",inline"` // 嵌入共享的 DbConfig
20 7
 }
21 8
 
22
-// SetDefaults 设置默认值 - 实现 ConfigLoader 接口
23 9
 func (c *DatabaseConfig) SetDefaults() {
24
-	if c.Type == "" {
25
-		c.Type = "mysql"
26
-	}
27
-	if c.Port == 0 {
28
-		c.Port = 3306
29
-	}
30
-	if c.MaxOpenConns == 0 {
31
-		c.MaxOpenConns = 100
32
-	}
33
-	if c.MaxIdleConns == 0 {
34
-		c.MaxIdleConns = 20
35
-	}
36
-	if c.ConnMaxLifetime == 0 {
37
-		c.ConnMaxLifetime = 3600
38
-	}
10
+	SetDbDefaults(&c.DbConfig)
39 11
 }
40 12
 
41
-// Load 从yaml数据加载 - 实现 ConfigLoader 接口
42 13
 func (c *DatabaseConfig) Load(data map[string]interface{}) error {
43
-
44 14
 	return c.LoadFromYAML(data, c)
45 15
 }
46 16
 
47
-// Validate 验证配置(数据库配置)
48 17
 func (c *DatabaseConfig) Validate() error {
49
-	if c.Type == "" {
50
-		return fmt.Errorf("database type is required")
51
-	}
52
-	if c.Host == "" {
53
-		return fmt.Errorf("database host is required")
54
-	}
55
-	if c.Port <= 0 || c.Port > 65535 {
56
-		return fmt.Errorf("invalid database port: %d", c.Port)
57
-	}
58
-	if c.Database == "" {
59
-		return fmt.Errorf("database name is required")
60
-	}
61
-	return nil
18
+	return ValidateDbConfig(&c.DbConfig)
62 19
 }
63 20
 
64
-// IsConfigured 判断是否已配置(数据库配置)
65 21
 func (c *DatabaseConfig) IsConfigured() bool {
66
-	if c.Host == "" {
67
-		log.Println("⚠️  警告: 数据库 Host 未配置")
68
-		return false
69
-	}
70
-	if c.Port <= 0 {
71
-		log.Println("⚠️  警告: 数据库 Port 未配置或无效")
72
-		return false
73
-	}
74
-	if c.Username == "" {
75
-		log.Println("⚠️  警告: 数据库 Username 未配置")
76
-		return false
77
-	}
78
-	if c.Database == "" {
79
-		log.Println("⚠️  警告: 数据库名称未配置")
80
-		return false
81
-	}
82
-	return true
22
+	return IsDbConfigured(&c.DbConfig, "")
83 23
 }
84 24
 
85
-// 自动注册
86 25
 func init() {
87 26
 	Register("database", &DatabaseConfig{})
88 27
 }

+ 69
- 0
config/subconfigs/dbs_config.go View File

@@ -0,0 +1,69 @@
1
+package subconfigs
2
+
3
+import (
4
+	"fmt"
5
+	"log"
6
+)
7
+
8
+// DbsConfig 多数据库配置
9
+type DbsConfig struct {
10
+	BaseConfig
11
+	Databases map[string]*DbConfig `yaml:",inline"`
12
+}
13
+
14
+func (c *DbsConfig) SetDefaults() {
15
+	for _, db := range c.Databases {
16
+		SetDbDefaults(db)
17
+	}
18
+}
19
+
20
+func (c *DbsConfig) Load(data map[string]interface{}) error {
21
+	// 直接使用 LoadFromYAML,会自动处理嵌套的 map[string]*DbConfig
22
+	return c.LoadFromYAML(data, c)
23
+}
24
+
25
+func (c *DbsConfig) Validate() error {
26
+	if len(c.Databases) == 0 {
27
+		return fmt.Errorf("未配置任何数据库")
28
+	}
29
+
30
+	for name, db := range c.Databases {
31
+		if err := ValidateDbConfig(db); err != nil {
32
+			return fmt.Errorf("数据库 '%s' 配置错误: %v", name, err)
33
+		}
34
+	}
35
+	return nil
36
+}
37
+
38
+func (c *DbsConfig) IsConfigured() bool {
39
+	if len(c.Databases) == 0 {
40
+		log.Println("⚠️  警告: 未配置任何数据库")
41
+		return false
42
+	}
43
+
44
+	allConfigured := true
45
+	for name, db := range c.Databases {
46
+		if !IsDbConfigured(db, name) {
47
+			allConfigured = false
48
+		}
49
+	}
50
+	return allConfigured
51
+}
52
+
53
+// Get 获取指定名称的数据库配置
54
+func (c *DbsConfig) GetDbConfig(name string) (*DbConfig, bool) {
55
+	db, exists := c.Databases[name]
56
+	return db, exists
57
+}
58
+
59
+// Default 获取第一个数据库配置
60
+func (c *DbsConfig) Default() *DbConfig {
61
+	for _, db := range c.Databases {
62
+		return db
63
+	}
64
+	return nil
65
+}
66
+
67
+func init() {
68
+	Register("dbs", &DbsConfig{})
69
+}

+ 11
- 0
ctx/RequestContext.go View File

@@ -40,3 +40,14 @@ func GetContext(r *http.Request) *RequestContext {
40 40
 
41 41
 	return &RequestContext{}
42 42
 }
43
+
44
+func GetContextTest() *RequestContext {
45
+
46
+	return &RequestContext{
47
+		TraceID:      "test-TTraceID",
48
+		TenantID:     "test-TenantID",
49
+		ServiceName:  "test-ServiceName",
50
+		InstanceName: "test-InstanceName",
51
+		UserID:       "test-UserID",
52
+	}
53
+}

+ 4
- 4
model/bson/bson_model.go View File

@@ -27,7 +27,7 @@ type BsonStringModel = bsonModel[string]
27 27
 type BsonObjectModel = bsonModel[primitive.ObjectID]
28 28
 
29 29
 // 创建新的 String 模型(自动生成ID)
30
-func NewStringIDModel(ctx ctx.RequestContext) BsonStringModel {
30
+func NewStringIDModel(ctx *ctx.RequestContext) BsonStringModel {
31 31
 	return BsonStringModel{
32 32
 		ID:        primitive.NewObjectID().Hex(),
33 33
 		TenantId:  ctx.TenantID,
@@ -39,7 +39,7 @@ func NewStringIDModel(ctx ctx.RequestContext) BsonStringModel {
39 39
 }
40 40
 
41 41
 // 创建新的 String 模型(自定义ID)
42
-func NewStringIDModelWithID(ctx ctx.RequestContext, id string) BsonStringModel {
42
+func NewStringIDModelWithID(ctx *ctx.RequestContext, id string) BsonStringModel {
43 43
 	return BsonStringModel{
44 44
 		ID:        id,
45 45
 		TenantId:  ctx.TenantID,
@@ -51,7 +51,7 @@ func NewStringIDModelWithID(ctx ctx.RequestContext, id string) BsonStringModel {
51 51
 }
52 52
 
53 53
 // 创建新的 ObjectID 模型(自动生成ID)
54
-func NewObjectIDModel(ctx ctx.RequestContext) BsonObjectModel {
54
+func NewObjectIDModel(ctx *ctx.RequestContext) BsonObjectModel {
55 55
 	return BsonObjectModel{
56 56
 		ID:        primitive.NewObjectID(),
57 57
 		TenantId:  ctx.TenantID,
@@ -63,7 +63,7 @@ func NewObjectIDModel(ctx ctx.RequestContext) BsonObjectModel {
63 63
 }
64 64
 
65 65
 // 用于更新操作的模型(只设置更新信息)
66
-func ModelForUpdate(ctx ctx.RequestContext) BsonStringModel {
66
+func ModelForUpdate(ctx *ctx.RequestContext) BsonStringModel {
67 67
 	return BsonStringModel{
68 68
 		UpdatedAt: time.Now(),
69 69
 		UpdatedBy: ctx.UserID,

+ 8
- 8
util/create_error_result.go View File

@@ -11,25 +11,25 @@ import (
11 11
 //	CreateErrorResult 创建错误结果的泛型辅助函数
12 12
 //
13 13
 // T 是 QueryResult 中 Data 字段的类型
14
-func CreateErrorResult[T any](errorMsg string, startTime time.Time, reqCtx *ctx.RequestContext) *response.QueryResult[T] {
14
+func CreateErrorResult[T any](errorMsg string, reqCtx *ctx.RequestContext) *response.QueryResult[T] {
15 15
 	// 使用 *new(T) 获取 T 类型的零值指针
16 16
 	var zeroValue T
17
-	return createResult(zeroValue, false, errorMsg, startTime, reqCtx)
17
+	return createResult(zeroValue, false, errorMsg, reqCtx)
18 18
 }
19 19
 
20 20
 // CreateSuccessResult 建立返回成功对象
21 21
 //
22 22
 //	T 是 QueryResult 中 Data 字段的类型
23
-func CreateSuccessResultData[T any](data T, startTime time.Time, reqCtx *ctx.RequestContext) *response.QueryResult[T] {
24
-	return createResult(data, true, "", startTime, reqCtx)
23
+func CreateSuccessResultData[T any](data T, reqCtx *ctx.RequestContext) *response.QueryResult[T] {
24
+	return createResult(data, true, "", reqCtx)
25 25
 }
26 26
 
27
-func CreateSuccessResult[T any](startTime time.Time, reqCtx *ctx.RequestContext) *response.QueryResult[T] {
27
+func CreateSuccessResult[T any](reqCtx *ctx.RequestContext) *response.QueryResult[T] {
28 28
 	var zeroValue T
29
-	return createResult(zeroValue, true, "", startTime, reqCtx)
29
+	return createResult(zeroValue, true, "", reqCtx)
30 30
 }
31 31
 
32
-func createResult[T any](data T, success bool, msg string, startTime time.Time, reqCtx *ctx.RequestContext) *response.QueryResult[T] {
32
+func createResult[T any](data T, success bool, msg string, reqCtx *ctx.RequestContext) *response.QueryResult[T] {
33 33
 	if success {
34 34
 		logger.DebugC(reqCtx, msg)
35 35
 	} else {
@@ -39,7 +39,7 @@ func createResult[T any](data T, success bool, msg string, startTime time.Time,
39 39
 	return &response.QueryResult[T]{
40 40
 		Success:  success,
41 41
 		Error:    msg,
42
-		Time:     time.Since(startTime).String(),
42
+		Time:     time.Now().String(),
43 43
 		Metadata: reqCtx,
44 44
 		Data:     data,
45 45
 	}

+ 7
- 0
webx/router/router_service.go View File

@@ -333,6 +333,13 @@ func (ha *handlerAdapter) handleResponse(w http.ResponseWriter, results []reflec
333 333
 		return
334 334
 	}
335 335
 
336
+	// 检查是否是 []byte 类型
337
+	if bytes, ok := data.([]byte); ok {
338
+		// 如果是 []byte,直接写入响应,不进行 JSON 编码
339
+		w.Write(bytes)
340
+		return
341
+	}
342
+
336 343
 	// 返回JSON响应
337 344
 	w.Header().Set("Content-Type", "application/json")
338 345
 	json.NewEncoder(w).Encode(data)

Loading…
Cancel
Save