|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+package main
|
|
|
2
|
+
|
|
|
3
|
+import (
|
|
|
4
|
+ "database/sql"
|
|
|
5
|
+ "encoding/json"
|
|
|
6
|
+ "fmt"
|
|
|
7
|
+ "log"
|
|
|
8
|
+ "net/http"
|
|
|
9
|
+ "time"
|
|
|
10
|
+
|
|
|
11
|
+ "git.x2erp.com/qdy/go-base/types"
|
|
|
12
|
+ // 注意:这里要使用 factory 包的正确导入路径(和你原代码一致)
|
|
|
13
|
+ "git.x2erp.com/qdy/go-db/factory"
|
|
|
14
|
+ "github.com/gorilla/mux"
|
|
|
15
|
+)
|
|
|
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
|
+}
|
|
|
25
|
+
|
|
|
26
|
+func main() {
|
|
|
27
|
+ var err error
|
|
|
28
|
+
|
|
|
29
|
+ // 1. 创建数据库工厂(只执行一次)
|
|
|
30
|
+ dbFactory, err = factory.NewDBFactory()
|
|
|
31
|
+ if err != nil {
|
|
|
32
|
+ log.Fatalf("Failed to create DB factory: %v", err)
|
|
|
33
|
+ }
|
|
|
34
|
+
|
|
|
35
|
+ // 2. 显示基础信息(保留原日志输出)
|
|
|
36
|
+ drivers := dbFactory.GetAvailableDrivers()
|
|
|
37
|
+ fmt.Printf("Available database drivers: %v\n", drivers)
|
|
|
38
|
+
|
|
|
39
|
+ 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)
|
|
|
48
|
+ }
|
|
|
49
|
+ defer db.Close()
|
|
|
50
|
+
|
|
|
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!")
|
|
|
56
|
+ }
|
|
|
57
|
+
|
|
|
58
|
+ // 5. 启动HTTP服务
|
|
|
59
|
+ startHTTPServer()
|
|
|
60
|
+}
|
|
|
61
|
+
|
|
|
62
|
+// 启动HTTP服务器(简化,直接使用全局DB连接)
|
|
|
63
|
+func startHTTPServer() {
|
|
|
64
|
+ router := mux.NewRouter()
|
|
|
65
|
+
|
|
|
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")
|
|
|
71
|
+
|
|
|
72
|
+ // 服务器配置(保留原超时设置)
|
|
|
73
|
+ server := &http.Server{
|
|
|
74
|
+ Addr: ":8080",
|
|
|
75
|
+ Handler: router,
|
|
|
76
|
+ ReadTimeout: 30 * time.Second,
|
|
|
77
|
+ WriteTimeout: 30 * time.Second,
|
|
|
78
|
+ IdleTimeout: 60 * time.Second,
|
|
|
79
|
+ }
|
|
|
80
|
+
|
|
|
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())
|
|
|
89
|
+}
|
|
|
90
|
+
|
|
|
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
|
|
|
107
|
+ }
|
|
|
108
|
+
|
|
|
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
|
+ })
|
|
|
116
|
+ return
|
|
|
117
|
+ }
|
|
|
118
|
+
|
|
|
119
|
+ // 3. 核心逻辑:调用 factory.QuickQueryToJSON(确保工厂包导出了该方法)
|
|
|
120
|
+ // 注意:如果仍提示 undefined,检查 factory 包是否真的导出了 QuickQueryToJSON(首字母大写)
|
|
|
121
|
+ result := factory.QuickQueryToJSON(db, req.SQL)
|
|
|
122
|
+
|
|
|
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
|
|
|
134
|
+
|
|
|
135
|
+ // 标准 if-else 赋值状态
|
|
|
136
|
+ var status string
|
|
|
137
|
+ if success {
|
|
|
138
|
+ status = "UP"
|
|
|
139
|
+ } else {
|
|
|
140
|
+ status = "DOWN"
|
|
|
141
|
+ }
|
|
|
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
|
+}
|
|
|
159
|
+
|
|
|
160
|
+// infoHandler 数据库信息(返回 types.QueryResult 格式)
|
|
|
161
|
+func infoHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
162
|
+ w.Header().Set("Content-Type", "application/json")
|
|
|
163
|
+
|
|
|
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)
|
|
|
195
|
+ 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)
|
|
|
201
|
+ }
|
|
|
202
|
+
|
|
|
203
|
+ return nil
|
|
|
204
|
+}
|