|
|
@@ -2,203 +2,176 @@ package main
|
|
2
|
2
|
|
|
3
|
3
|
import (
|
|
4
|
4
|
"database/sql"
|
|
5
|
|
- "encoding/json"
|
|
6
|
5
|
"fmt"
|
|
7
|
6
|
"log"
|
|
8
|
7
|
"net/http"
|
|
|
8
|
+ "strings"
|
|
9
|
9
|
"time"
|
|
10
|
10
|
|
|
11
|
11
|
"git.x2erp.com/qdy/go-base/types"
|
|
12
|
|
- // 注意:这里要使用 factory 包的正确导入路径(和你原代码一致)
|
|
13
|
12
|
"git.x2erp.com/qdy/go-db/factory"
|
|
14
|
|
- "github.com/gorilla/mux"
|
|
|
13
|
+ "git.x2erp.com/qdy/go-service-agent/routes"
|
|
|
14
|
+ "github.com/gin-gonic/gin"
|
|
15
|
15
|
)
|
|
16
|
16
|
|
|
17
|
|
-// 全局变量,只初始化一次(复用原逻辑,确保工厂和DB连接单例)
|
|
18
|
|
-var dbFactory *factory.DBFactory
|
|
19
|
|
-var db *sql.DB
|
|
20
|
|
-
|
|
21
|
|
-// QueryRequest 请求结构体(接收前端SQL参数)
|
|
22
|
|
-type QueryRequest struct {
|
|
23
|
|
- SQL string `json:"sql"`
|
|
24
|
|
-}
|
|
|
17
|
+// 单例实例
|
|
|
18
|
+var (
|
|
|
19
|
+ dbFactory *factory.DBFactory
|
|
|
20
|
+ db *sql.DB
|
|
|
21
|
+)
|
|
25
|
22
|
|
|
26
|
|
-func main() {
|
|
|
23
|
+// initDB 初始化数据库连接(单例)
|
|
|
24
|
+func initDB() error {
|
|
27
|
25
|
var err error
|
|
28
|
26
|
|
|
29
|
|
- // 1. 创建数据库工厂(只执行一次)
|
|
30
|
|
- dbFactory, err = factory.NewDBFactory()
|
|
31
|
|
- if err != nil {
|
|
32
|
|
- log.Fatalf("Failed to create DB factory: %v", err)
|
|
|
27
|
+ // 创建数据库工厂(单例)
|
|
|
28
|
+ if dbFactory == nil {
|
|
|
29
|
+ dbFactory, err = factory.NewDBFactory()
|
|
|
30
|
+ if err != nil {
|
|
|
31
|
+ return fmt.Errorf("failed to create DB factory: %v", err)
|
|
|
32
|
+ }
|
|
33
|
33
|
}
|
|
34
|
34
|
|
|
35
|
|
- // 2. 显示基础信息(保留原日志输出)
|
|
36
|
|
- drivers := dbFactory.GetAvailableDrivers()
|
|
37
|
|
- fmt.Printf("Available database drivers: %v\n", drivers)
|
|
|
35
|
+ // 创建数据库连接(单例)
|
|
|
36
|
+ if db == nil {
|
|
|
37
|
+ db, err = dbFactory.CreateDB()
|
|
|
38
|
+ if err != nil {
|
|
|
39
|
+ return fmt.Errorf("failed to create database connection: %v", err)
|
|
|
40
|
+ }
|
|
|
41
|
+ }
|
|
38
|
42
|
|
|
|
43
|
+ // 测试连接
|
|
39
|
44
|
config := dbFactory.GetConfig()
|
|
40
|
|
- fmt.Printf("Using database type: %s\n", config.Database.Type)
|
|
41
|
|
- fmt.Printf("Database host: %s:%d\n", config.Database.Host, config.Database.Port)
|
|
42
|
|
- fmt.Printf("Database name: %s\n", config.Database.Database)
|
|
43
|
|
-
|
|
44
|
|
- // 3. 创建数据库连接(全局复用)
|
|
45
|
|
- db, err = dbFactory.CreateDB()
|
|
46
|
|
- if err != nil {
|
|
47
|
|
- log.Fatalf("Failed to create database connection: %v", err)
|
|
|
45
|
+ if err := routes.TestConnection(db, config.GetDatabase().Type); err != nil {
|
|
|
46
|
+ return fmt.Errorf("database connection test failed: %v", err)
|
|
48
|
47
|
}
|
|
49
|
|
- defer db.Close()
|
|
50
|
48
|
|
|
51
|
|
- // 4. 测试连接(保留原校验逻辑)
|
|
52
|
|
- if err := testConnection(db, config.Database.Type); err != nil {
|
|
53
|
|
- log.Fatalf("Database connection test failed: %v", err)
|
|
54
|
|
- } else {
|
|
55
|
|
- fmt.Println("Database connection test passed!")
|
|
|
49
|
+ return nil
|
|
|
50
|
+}
|
|
|
51
|
+
|
|
|
52
|
+func main() {
|
|
|
53
|
+ // 1. 初始化数据库(单例)
|
|
|
54
|
+ if err := initDB(); err != nil {
|
|
|
55
|
+ log.Fatalf("Database initialization failed: %v", err)
|
|
56
|
56
|
}
|
|
57
|
57
|
|
|
58
|
|
- // 5. 启动HTTP服务
|
|
|
58
|
+ // 2. 显示基础信息
|
|
|
59
|
+ drivers := dbFactory.GetAvailableDrivers()
|
|
|
60
|
+ config := dbFactory.GetConfig()
|
|
|
61
|
+
|
|
|
62
|
+ log.Printf("Service Port: %d", config.GetService().Port)
|
|
|
63
|
+ log.Printf("Service IdleTimeout: %d", config.GetService().IdleTimeout)
|
|
|
64
|
+ log.Printf("Service ReadTimeout: %d", config.GetService().ReadTimeout)
|
|
|
65
|
+ log.Printf("Service WriteTimeout: %d", config.GetService().WriteTimeout)
|
|
|
66
|
+ log.Printf("Service TrustedProxies: %s", config.GetService().TrustedProxies)
|
|
|
67
|
+
|
|
|
68
|
+ log.Printf("Available database drivers: %v", drivers)
|
|
|
69
|
+ log.Printf("Using database type: %s", config.GetDatabase().Type)
|
|
|
70
|
+ log.Printf("Database host: %s:%d", config.GetDatabase().Host, config.GetDatabase().Port)
|
|
|
71
|
+ log.Printf("Database name: %s", config.GetDatabase().Database)
|
|
|
72
|
+ log.Println("Database connection test passed!")
|
|
|
73
|
+
|
|
|
74
|
+ // 3. 启动Gin HTTP服务
|
|
59
|
75
|
startHTTPServer()
|
|
60
|
76
|
}
|
|
61
|
77
|
|
|
62
|
|
-// 启动HTTP服务器(简化,直接使用全局DB连接)
|
|
|
78
|
+// 启动HTTP服务器
|
|
63
|
79
|
func startHTTPServer() {
|
|
64
|
|
- router := mux.NewRouter()
|
|
|
80
|
+ router := gin.Default()
|
|
|
81
|
+
|
|
|
82
|
+ // 添加中间件 重复注册
|
|
|
83
|
+ //router.Use(gin.Logger())
|
|
|
84
|
+ //router.Use(gin.Recovery())
|
|
|
85
|
+
|
|
|
86
|
+ // 核心路由
|
|
|
87
|
+ router.GET("/api/health", routes.HealthHandler(db, "oracle"))
|
|
|
88
|
+ router.POST("/api/query", withQueryRequest(routes.QueryHandler(db)))
|
|
|
89
|
+ router.POST("/api/query/csv", withQueryRequest(routes.QueryHandlerCSV(db)))
|
|
|
90
|
+ router.GET("/api/info", routes.InfoHandler(dbFactory))
|
|
|
91
|
+
|
|
|
92
|
+ config := dbFactory.GetConfig()
|
|
|
93
|
+ serviceConfig := config.GetService()
|
|
65
|
94
|
|
|
66
|
|
- // 核心路由:SQL查询(POST)- 直接返回 types.QueryResult
|
|
67
|
|
- router.HandleFunc("/api/query", queryHandler).Methods("POST")
|
|
68
|
|
- // 辅助路由:健康检查、数据库信息(保留原功能)
|
|
69
|
|
- router.HandleFunc("/api/health", healthHandler).Methods("GET")
|
|
70
|
|
- router.HandleFunc("/api/info", infoHandler).Methods("GET")
|
|
|
95
|
+ // 日志输出配置信息
|
|
|
96
|
+ log.Printf("Service Port: %d", serviceConfig.Port)
|
|
|
97
|
+ log.Printf("Service IdleTimeout: %d", serviceConfig.IdleTimeout)
|
|
|
98
|
+ log.Printf("Service ReadTimeout: %d", serviceConfig.ReadTimeout)
|
|
|
99
|
+ log.Printf("Service WriteTimeout: %d", serviceConfig.WriteTimeout)
|
|
|
100
|
+ log.Printf("Service TrustedProxies: %s", serviceConfig.TrustedProxies)
|
|
71
|
101
|
|
|
72
|
|
- // 服务器配置(保留原超时设置)
|
|
|
102
|
+ // 设置可信代理
|
|
|
103
|
+ setupTrustedProxies(router, serviceConfig.TrustedProxies)
|
|
|
104
|
+
|
|
|
105
|
+ // 启动服务
|
|
|
106
|
+ log.Println("POST /api/query - Execute SQL query to JSON")
|
|
|
107
|
+ log.Println("POST /api/query/csv - Execute SQL query to CSV")
|
|
|
108
|
+ log.Println("GET /api/health - Health check")
|
|
|
109
|
+ log.Println("GET /api/info - Database info")
|
|
|
110
|
+
|
|
|
111
|
+ // 创建HTTP服务器配置
|
|
73
|
112
|
server := &http.Server{
|
|
74
|
|
- Addr: ":8080",
|
|
|
113
|
+ Addr: fmt.Sprintf(":%d", serviceConfig.Port),
|
|
75
|
114
|
Handler: router,
|
|
76
|
|
- ReadTimeout: 30 * time.Second,
|
|
77
|
|
- WriteTimeout: 30 * time.Second,
|
|
78
|
|
- IdleTimeout: 60 * time.Second,
|
|
|
115
|
+ IdleTimeout: time.Duration(serviceConfig.IdleTimeout) * time.Second,
|
|
|
116
|
+ ReadTimeout: time.Duration(serviceConfig.ReadTimeout) * time.Second,
|
|
|
117
|
+ WriteTimeout: time.Duration(serviceConfig.WriteTimeout) * time.Second,
|
|
79
|
118
|
}
|
|
80
|
119
|
|
|
81
|
|
- // 启动日志(保留原格式)
|
|
82
|
|
- fmt.Println("Database microservice starting on :8080")
|
|
83
|
|
- fmt.Println("Endpoints:")
|
|
84
|
|
- fmt.Println(" POST /api/query - Execute SQL query (return types.QueryResult)")
|
|
85
|
|
- fmt.Println(" GET /api/health - Health check")
|
|
86
|
|
- fmt.Println(" GET /api/info - Database info")
|
|
87
|
|
-
|
|
88
|
|
- log.Fatal(server.ListenAndServe())
|
|
|
120
|
+ log.Printf("Starting HTTP server on port %d", serviceConfig.Port)
|
|
|
121
|
+ if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
|
122
|
+ log.Fatalf("Failed to start server: %v", err)
|
|
|
123
|
+ }
|
|
89
|
124
|
}
|
|
90
|
125
|
|
|
91
|
|
-// queryHandler SQL查询处理(核心修改)
|
|
92
|
|
-// 1. 修复 factory.QuickQueryToJSON 调用(确保导入路径正确)
|
|
93
|
|
-// 2. 直接返回 types.QueryResult,不二次封装
|
|
94
|
|
-func queryHandler(w http.ResponseWriter, r *http.Request) {
|
|
95
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
96
|
|
-
|
|
97
|
|
- // 1. 解析请求参数
|
|
98
|
|
- var req QueryRequest
|
|
99
|
|
- if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
100
|
|
- // 参数错误时,返回 types.QueryResult 格式的错误响应
|
|
101
|
|
- json.NewEncoder(w).Encode(&types.QueryResult{
|
|
102
|
|
- Success: false,
|
|
103
|
|
- Error: fmt.Sprintf("Invalid request body: %v", err),
|
|
104
|
|
- Data: nil,
|
|
105
|
|
- })
|
|
106
|
|
- return
|
|
|
126
|
+// 参数绑定包装器
|
|
|
127
|
+func withQueryRequest(handler func(c *gin.Context, req types.QueryRequest)) gin.HandlerFunc {
|
|
|
128
|
+ return func(c *gin.Context) {
|
|
|
129
|
+ var req types.QueryRequest
|
|
|
130
|
+ if err := c.ShouldBindJSON(&req); err != nil {
|
|
|
131
|
+ c.JSON(400, &types.QueryResult{
|
|
|
132
|
+ Success: false,
|
|
|
133
|
+ Error: "Invalid request: " + err.Error(),
|
|
|
134
|
+ Data: nil,
|
|
|
135
|
+ })
|
|
|
136
|
+ return
|
|
|
137
|
+ }
|
|
|
138
|
+
|
|
|
139
|
+ handler(c, req)
|
|
107
|
140
|
}
|
|
|
141
|
+}
|
|
108
|
142
|
|
|
109
|
|
- // 2. 校验SQL非空
|
|
110
|
|
- if req.SQL == "" {
|
|
111
|
|
- json.NewEncoder(w).Encode(&types.QueryResult{
|
|
112
|
|
- Success: false,
|
|
113
|
|
- Error: "SQL statement cannot be empty",
|
|
114
|
|
- Data: nil,
|
|
115
|
|
- })
|
|
|
143
|
+// 设置可信代理
|
|
|
144
|
+func setupTrustedProxies(router *gin.Engine, trustedProxiesStr string) {
|
|
|
145
|
+ if trustedProxiesStr == "" {
|
|
|
146
|
+ setupTrustedProxiesRouter(router, nil)
|
|
116
|
147
|
return
|
|
117
|
148
|
}
|
|
118
|
149
|
|
|
119
|
|
- // 3. 核心逻辑:调用 factory.QuickQueryToJSON(确保工厂包导出了该方法)
|
|
120
|
|
- // 注意:如果仍提示 undefined,检查 factory 包是否真的导出了 QuickQueryToJSON(首字母大写)
|
|
121
|
|
- result := factory.QuickQueryToJSON(db, req.SQL)
|
|
|
150
|
+ // 按逗号分割字符串,并去除空格
|
|
|
151
|
+ proxies := strings.Split(trustedProxiesStr, ",")
|
|
|
152
|
+ trimmedProxies := make([]string, 0, len(proxies))
|
|
122
|
153
|
|
|
123
|
|
- // 4. 直接返回结果(types.QueryResult 原生格式)
|
|
124
|
|
- json.NewEncoder(w).Encode(result)
|
|
125
|
|
-}
|
|
126
|
|
-
|
|
127
|
|
-// healthHandler 健康检查(修复语法错误,保持返回格式统一)
|
|
128
|
|
-func healthHandler(w http.ResponseWriter, r *http.Request) {
|
|
129
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
130
|
|
-
|
|
131
|
|
- // 校验DB连接状态
|
|
132
|
|
- err := db.Ping()
|
|
133
|
|
- success := err == nil
|
|
|
154
|
+ for _, proxy := range proxies {
|
|
|
155
|
+ trimmed := strings.TrimSpace(proxy)
|
|
|
156
|
+ if trimmed != "" {
|
|
|
157
|
+ trimmedProxies = append(trimmedProxies, trimmed)
|
|
|
158
|
+ }
|
|
|
159
|
+ }
|
|
134
|
160
|
|
|
135
|
|
- // 标准 if-else 赋值状态
|
|
136
|
|
- var status string
|
|
137
|
|
- if success {
|
|
138
|
|
- status = "UP"
|
|
|
161
|
+ if len(trimmedProxies) > 0 {
|
|
|
162
|
+ setupTrustedProxiesRouter(router, trimmedProxies)
|
|
139
|
163
|
} else {
|
|
140
|
|
- status = "DOWN"
|
|
|
164
|
+ setupTrustedProxiesRouter(router, nil)
|
|
141
|
165
|
}
|
|
142
|
|
-
|
|
143
|
|
- // 返回 types.QueryResult 格式(和查询接口保持一致)
|
|
144
|
|
- json.NewEncoder(w).Encode(&types.QueryResult{
|
|
145
|
|
- Success: success,
|
|
146
|
|
- Data: map[string]interface{}{
|
|
147
|
|
- "status": status,
|
|
148
|
|
- "time": time.Now().Format(time.RFC3339),
|
|
149
|
|
- "database": dbFactory.GetConfig().Database.Type,
|
|
150
|
|
- },
|
|
151
|
|
- Error: func() string {
|
|
152
|
|
- if err != nil {
|
|
153
|
|
- return err.Error()
|
|
154
|
|
- }
|
|
155
|
|
- return ""
|
|
156
|
|
- }(),
|
|
157
|
|
- })
|
|
158
|
166
|
}
|
|
159
|
167
|
|
|
160
|
|
-// infoHandler 数据库信息(返回 types.QueryResult 格式)
|
|
161
|
|
-func infoHandler(w http.ResponseWriter, r *http.Request) {
|
|
162
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
168
|
+func setupTrustedProxiesRouter(router *gin.Engine, trimmedProxies []string) {
|
|
163
|
169
|
|
|
164
|
|
- config := dbFactory.GetConfig()
|
|
165
|
|
- drivers := dbFactory.GetAvailableDrivers()
|
|
166
|
|
-
|
|
167
|
|
- // 直接返回 types.QueryResult 格式
|
|
168
|
|
- json.NewEncoder(w).Encode(&types.QueryResult{
|
|
169
|
|
- Success: true,
|
|
170
|
|
- Data: map[string]interface{}{
|
|
171
|
|
- "database_type": config.Database.Type,
|
|
172
|
|
- "database_host": fmt.Sprintf("%s:%d", config.Database.Host, config.Database.Port),
|
|
173
|
|
- "database_name": config.Database.Database,
|
|
174
|
|
- "available_drivers": drivers,
|
|
175
|
|
- "service_time": time.Now().Format(time.RFC3339),
|
|
176
|
|
- },
|
|
177
|
|
- Error: "",
|
|
178
|
|
- })
|
|
179
|
|
-}
|
|
180
|
|
-
|
|
181
|
|
-// testConnection 测试数据库连接(保留原逻辑)
|
|
182
|
|
-func testConnection(db *sql.DB, dbType string) error {
|
|
183
|
|
- var query string
|
|
184
|
|
- switch dbType {
|
|
185
|
|
- case "mysql", "postgres", "sqlserver":
|
|
186
|
|
- query = "SELECT 1"
|
|
187
|
|
- case "oracle":
|
|
188
|
|
- query = "SELECT 1 FROM DUAL"
|
|
189
|
|
- default:
|
|
190
|
|
- query = "SELECT 1"
|
|
191
|
|
- }
|
|
192
|
|
-
|
|
193
|
|
- var result int
|
|
194
|
|
- err := db.QueryRow(query).Scan(&result)
|
|
|
170
|
+ err := router.SetTrustedProxies(trimmedProxies)
|
|
195
|
171
|
if err != nil {
|
|
196
|
|
- return fmt.Errorf("test query failed: %v", err)
|
|
197
|
|
- }
|
|
198
|
|
-
|
|
199
|
|
- if result != 1 {
|
|
200
|
|
- return fmt.Errorf("unexpected test result: %d", result)
|
|
|
172
|
+ log.Printf("Warning: Failed to set trusted proxies: %v", err)
|
|
|
173
|
+ } else {
|
|
|
174
|
+ log.Printf("Trusted proxies set: %v", trimmedProxies)
|
|
201
|
175
|
}
|
|
202
|
176
|
|
|
203
|
|
- return nil
|
|
204
|
177
|
}
|