Bladeren bron

可以查询配置文件

qdy 2 maanden geleden
bovenliggende
commit
356cb68536
6 gewijzigde bestanden met toevoegingen van 421 en 24 verwijderingen
  1. 13
    23
      main.go
  2. 227
    0
      service/query_yaml_configure.go
  3. 55
    0
      tables/config_environment.go
  4. 24
    1
      tables/config_service.go
  5. 73
    0
      test/my_post_query_test.go
  6. 29
    0
      yaml-插入启动配置.sql

+ 13
- 23
main.go Bestand weergeven

@@ -9,16 +9,15 @@ import (
9 9
 	"git.x2erp.com/qdy/go-db/factory/database"
10 10
 	"git.x2erp.com/qdy/go-db/myhandle"
11 11
 	"git.x2erp.com/qdy/go-db/sqldef"
12
-	"git.x2erp.com/qdy/go-svc-agent/functions"
13 12
 	"go-micro.dev/v4/web"
14 13
 
15
-	// 导入表定义包,触发 init() 函数
16
-	_ "git.x2erp.com/qdy/go-svc-configure/tables" // 确保这里的路径正确
14
+	"git.x2erp.com/qdy/go-svc-configure/service"
15
+	_ "git.x2erp.com/qdy/go-svc-configure/tables" // 导入表定义包,触发 init() 函数
17 16
 )
18 17
 
19 18
 var (
20
-	serviceName    = "svc-agent"
21
-	serviceVersion = "1.0.0"
19
+	serviceName    = "svc-configure"
20
+	serviceVersion = "1"
22 21
 )
23 22
 
24 23
 func main() {
@@ -31,10 +30,9 @@ func main() {
31 30
 
32 31
 	//构建数据库工厂
33 32
 	bootstrapper.InitDatabase()
34
-	defer bootstrapper.CleanupDatabase()
35 33
 
36 34
 	//创建表到数据库
37
-	cretetabel(bootstrapper.DbFactory)
35
+	creteTabel(bootstrapper.DbFactory)
38 36
 
39 37
 	// 启动服务,传入路由注册函数
40 38
 	bootstrapper.Run(registerRoutes)
@@ -44,23 +42,15 @@ func main() {
44 42
 // 注册所有路由
45 43
 func registerRoutes(webService web.Service, dbFactory *database.DBFactory) {
46 44
 
47
-	// 查询接口 - JSON
48
-	webService.Handle("/api/query/json", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
49
-		myhandle.QueryHandlerJson(w, r, dbFactory, functions.QueryToJSON)
45
+	// 查询yaml配置文件
46
+	webService.Handle("/api/query/yaml", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
47
+		myhandle.QueryHandlerMap(w, r, dbFactory, service.QueryYamlConfigure)
50 48
 	})))
51 49
 
52
-	// 查询接口 - CSV
53
-	webService.Handle("/api/query/csv", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
54
-		myhandle.QueryHandlerBytes(w, r, dbFactory, functions.QueryToCSV)
55
-	})))
56
-
57
-	// 查询接口 - CSV with positional params
58
-	webService.Handle("/api/query/csv/param", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
59
-		myhandle.QueryHandlerBytes(w, r, dbFactory, functions.QueryPositionalToCSV)
60
-	})))
61 50
 }
62 51
 
63
-func cretetabel(factory *database.DBFactory) {
52
+func creteTabel(factory *database.DBFactory) {
53
+
64 54
 	// 获取数据库连接和类型
65 55
 	db := factory.GetDB()
66 56
 	dbType := factory.GetDBType()
@@ -68,12 +58,12 @@ func cretetabel(factory *database.DBFactory) {
68 58
 	// 创建表同步器
69 59
 	syncer, err := sqldef.NewTableSyncer(db, dbType)
70 60
 	if err != nil {
71
-		log.Fatalf("创建 - 建立器失败: %v", err)
61
+		log.Printf("创建 - 建立器失败: %v", err)
72 62
 	}
73 63
 
74 64
 	// 创建表
75
-	if err := syncer.RecreateTables(); err != nil {
76
-		log.Fatalf("建表失败: %v", err)
65
+	if err := syncer.CreateTables(); err != nil {
66
+		log.Printf("建表失败: %v", err)
77 67
 	}
78 68
 
79 69
 	log.Println("数据库表建立完成!")

+ 227
- 0
service/query_yaml_configure.go Bestand weergeven

@@ -0,0 +1,227 @@
1
+package service
2
+
3
+import (
4
+	"fmt"
5
+	"strconv"
6
+	"strings"
7
+	"time"
8
+
9
+	"git.x2erp.com/qdy/go-base/ctx"
10
+	"git.x2erp.com/qdy/go-base/logger"
11
+	"git.x2erp.com/qdy/go-base/types"
12
+	"git.x2erp.com/qdy/go-db/factory/database"
13
+	"github.com/jmoiron/sqlx"
14
+)
15
+
16
+func QueryYamlConfigure(dbFactory *database.DBFactory, req types.QueryRequest, reqCtx *ctx.RequestContext) *types.QueryResult[map[string]interface{}] {
17
+	return query(dbFactory.GetDB(), req, reqCtx)
18
+
19
+}
20
+
21
+// QueryYamlConfigure 执行带位置参数的查询
22
+func query(db *sqlx.DB, req types.QueryRequest, reqCtx *ctx.RequestContext) *types.QueryResult[map[string]interface{}] {
23
+
24
+	startTime := time.Now()
25
+
26
+	// 安全检查
27
+	if db == nil {
28
+		return createErrorResult("database connection is nil", startTime, reqCtx)
29
+	}
30
+
31
+	sqlStr := `
32
+		SELECT b.config_startup_detail_id, b.config_key, b.config_value 
33
+		FROM config_startup a
34
+		JOIN config_startup_detail b ON a.config_startup_id = b.config_startup_id 
35
+		WHERE a.config_environment_id = $1
36
+		  AND a.config_service_id = $2;`
37
+
38
+	params := req.PositionalParams
39
+	logger.DebugC(reqCtx, sqlStr)
40
+	logger.DebugC(reqCtx, fmt.Sprintf("PositionalParams: %v", params))
41
+
42
+	// 执行查询
43
+	rows, err := db.Queryx(sqlStr, params...)
44
+	if err != nil {
45
+		return createErrorResult(fmt.Sprintf("Query execution failed: %v", err), startTime, reqCtx)
46
+	}
47
+	defer rows.Close()
48
+
49
+	// 处理结果集
50
+	return processQueryResult(rows, startTime, reqCtx)
51
+}
52
+
53
+func processQueryResult(rows *sqlx.Rows, startTime time.Time, reqCtx *ctx.RequestContext) *types.QueryResult[map[string]interface{}] {
54
+	// 初始化结果结构体
55
+	result := &types.QueryResult[map[string]interface{}]{
56
+		Metadata: reqCtx,
57
+	}
58
+
59
+	// 获取列信息
60
+	columns, err := rows.Columns()
61
+	if err != nil {
62
+		return createErrorResult(fmt.Sprintf("Failed to get columns: %v", err), startTime, reqCtx)
63
+	}
64
+
65
+	// 存储最终结果:根key -> 内层配置map
66
+	results := make(map[string]interface{})
67
+	count := 0
68
+
69
+	// 记录已经出现的内层key,用于检测重复
70
+	// outerKey -> innerKey -> rowNumber
71
+	//keyTracker := make(map[string]map[string]int)
72
+
73
+	// 遍历行数据
74
+	for rows.Next() {
75
+		count++
76
+		values := make([]interface{}, len(columns))
77
+		valuePtrs := make([]interface{}, len(columns))
78
+		for i := range columns {
79
+			valuePtrs[i] = &values[i]
80
+		}
81
+
82
+		// 扫描行数据
83
+		if err := rows.Scan(valuePtrs...); err != nil {
84
+			return createErrorResult(fmt.Sprintf("Scan row %d failed: %v", count, err), startTime, reqCtx)
85
+		}
86
+
87
+		// 提取当前行的三个核心字段
88
+		var detailID, configKey string
89
+		var configValue interface{}
90
+		for i, col := range columns {
91
+			v := values[i]
92
+			if v == nil {
93
+				continue
94
+			}
95
+
96
+			switch col {
97
+			case "config_startup_detail_id":
98
+				detailID = toString(v) // detailID 需要字符串
99
+			case "config_key":
100
+				configKey = toString(v) // config_key 需要字符串
101
+			case "config_value":
102
+				configValue = convertValue(v) // config_value 需要带类型转换
103
+			}
104
+		}
105
+
106
+		//logger.DebugC(reqCtx, fmt.Sprintf("Row %d - detailID: %s, configKey: %s", count, detailID, configKey))
107
+
108
+		// 解析 detailID 获取根key(倒数第3段)
109
+		parts := strings.Split(detailID, ".")
110
+		if len(parts) < 3 {
111
+			return createErrorResult(fmt.Sprintf("Row %d detailID format error: %s", count, detailID), startTime, reqCtx)
112
+		}
113
+
114
+		rootKey := parts[len(parts)-3] // 取倒数第3段,例如 "database"
115
+
116
+		// 获取或创建外层key对应的map
117
+		configMap, exists := results[rootKey]
118
+		if !exists {
119
+			// 不存在则创建
120
+			configMap = make(map[string]interface{})
121
+			results[rootKey] = configMap
122
+		}
123
+
124
+		// 添加或覆盖内层配置
125
+		configMap.(map[string]interface{})[configKey] = configValue
126
+	}
127
+
128
+	// 检查行遍历错误
129
+	if err := rows.Err(); err != nil {
130
+		return createErrorResult(fmt.Sprintf("Row iteration error: %v", err), startTime, reqCtx)
131
+	}
132
+
133
+	// 构建成功结果
134
+	result.Success = true
135
+	result.Data = results
136
+	result.Count = count
137
+	result.Time = time.Since(startTime).String()
138
+	result.Message = "Query success"
139
+
140
+	return result
141
+}
142
+
143
+// createErrorResult 创建错误结果的辅助函数(复用你提供的)
144
+func createErrorResult(errorMsg string, startTime time.Time, reqCtx *ctx.RequestContext) *types.QueryResult[map[string]interface{}] {
145
+	logger.ErrorC(reqCtx, errorMsg)
146
+	return &types.QueryResult[map[string]interface{}]{
147
+		Success:  false,
148
+		Error:    errorMsg,
149
+		Time:     time.Since(startTime).String(),
150
+		Metadata: reqCtx,
151
+	}
152
+}
153
+
154
+// 辅助函数:转换为字符串
155
+func toString(v interface{}) string {
156
+	switch val := v.(type) {
157
+	case string:
158
+		return val
159
+	case []byte:
160
+		return string(val)
161
+	case int64:
162
+		return strconv.FormatInt(val, 10)
163
+	case int32:
164
+		return strconv.FormatInt(int64(val), 10)
165
+	case int:
166
+		return strconv.Itoa(val)
167
+	case float64:
168
+		return strconv.FormatFloat(val, 'f', -1, 64)
169
+	case float32:
170
+		return strconv.FormatFloat(float64(val), 'f', -1, 32)
171
+	case bool:
172
+		return strconv.FormatBool(val)
173
+	default:
174
+		return fmt.Sprintf("%v", val)
175
+	}
176
+}
177
+
178
+// 辅助函数:转换值,保持合适的数据类型
179
+func convertValue(v interface{}) interface{} {
180
+	switch val := v.(type) {
181
+	case []byte:
182
+		// 尝试解析为合适的数据类型
183
+		strVal := string(val)
184
+		return parseStringValue(strVal)
185
+
186
+	case string:
187
+		return parseStringValue(val)
188
+
189
+	case int64, int32, int:
190
+		return val
191
+
192
+	case float64, float32:
193
+		return val
194
+
195
+	case bool:
196
+		return val
197
+
198
+	default:
199
+		// 其他类型转换为字符串
200
+		return toString(val)
201
+	}
202
+}
203
+
204
+// 辅助函数:解析字符串值为合适的数据类型
205
+func parseStringValue(strVal string) interface{} {
206
+	// 1. 尝试布尔值 (支持 true/false, yes/no, on/off, 1/0)
207
+	lowerVal := strings.ToLower(strings.TrimSpace(strVal))
208
+	if lowerVal == "true" || lowerVal == "yes" || lowerVal == "on" || lowerVal == "1" {
209
+		return true
210
+	}
211
+	if lowerVal == "false" || lowerVal == "no" || lowerVal == "off" || lowerVal == "0" {
212
+		return false
213
+	}
214
+
215
+	// 2. 尝试整数
216
+	if intVal, err := strconv.ParseInt(strVal, 10, 64); err == nil {
217
+		return intVal
218
+	}
219
+
220
+	// 3. 尝试浮点数
221
+	if floatVal, err := strconv.ParseFloat(strVal, 64); err == nil {
222
+		return floatVal
223
+	}
224
+
225
+	// 4. 保持为字符串
226
+	return strVal
227
+}

+ 55
- 0
tables/config_environment.go Bestand weergeven

@@ -1,6 +1,9 @@
1 1
 package tables
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"time"
6
+
4 7
 	"git.x2erp.com/qdy/go-db/sqldef"
5 8
 )
6 9
 
@@ -20,3 +23,55 @@ func init() {
20 23
 		r.RegisterTable(tb.Build())
21 24
 	})
22 25
 }
26
+
27
+// ConfigEnvironment 环境表结构体
28
+type ConfigEnvironment struct {
29
+	// 环境编码,主键
30
+	ConfigEnvironmentID string `gorm:"column:config_environment_id;type:varchar(8);primaryKey;not null;comment:环境编码,主键"`
31
+	// 环境名称
32
+	EnvName string `gorm:"column:env_name;type:varchar(64);not null;comment:环境名称"`
33
+	// 环境描述
34
+	Description *string `gorm:"column:description;type:varchar(64);comment:环境描述"`
35
+	// 创建人
36
+	Creator string `gorm:"column:creator;type:varchar(32);not null;comment:创建人"`
37
+	// 创建时间
38
+	CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间"`
39
+	// 更新时间
40
+	UpdatedAt *time.Time `gorm:"column:updated_at;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;comment:更新时间"`
41
+}
42
+
43
+// TableName 指定表名
44
+func (ConfigEnvironment) TableName() string {
45
+	return "config_environment"
46
+}
47
+
48
+// GetDefaultDataSQL 获取环境表默认数据的SQL语句(PostgreSQL版本)
49
+func GetDefaultDataSQL() []string {
50
+	now := time.Now().Format("2006-01-02 15:04:05")
51
+
52
+	return []string{
53
+		// 清空表数据(可选,根据需要决定是否包含)
54
+		// "TRUNCATE TABLE config_environment RESTART IDENTITY;",
55
+
56
+		// 单条SQL插入所有数据(PostgreSQL多行插入)
57
+		fmt.Sprintf(`INSERT INTO config_environment 
58
+			(config_environment_id, env_name, description, creator, created_at, updated_at) 
59
+			VALUES 
60
+			('dev', '开发环境', '用于开发人员本地开发和调试的环境', 'system', '%s', '%s'),
61
+			('test', '测试环境', '用于功能测试、集成测试的环境', 'system', '%s', '%s'),
62
+			('pre', '预发布环境', '用于生产前最后验证的环境,与生产环境配置一致', 'system', '%s', '%s'),
63
+			('prod', '生产环境', '正式对外提供服务的生产环境', 'system', '%s', '%s'),
64
+			('demo', '演示环境', '用于产品演示和客户体验的环境', 'system', '%s', '%s')
65
+			ON CONFLICT (config_environment_id) DO UPDATE SET
66
+			env_name = EXCLUDED.env_name,
67
+			description = EXCLUDED.description,
68
+			creator = EXCLUDED.creator,
69
+			updated_at = EXCLUDED.updated_at;`,
70
+			now, now, // dev
71
+			now, now, // test
72
+			now, now, // pre
73
+			now, now, // prod
74
+			now, now, // demo
75
+		),
76
+	}
77
+}

+ 24
- 1
tables/config_service.go Bestand weergeven

@@ -1,7 +1,11 @@
1 1
 // 微服务登记表。保存整个项目有哪些微服务
2 2
 package tables
3 3
 
4
-import "git.x2erp.com/qdy/go-db/sqldef"
4
+import (
5
+	"time"
6
+
7
+	"git.x2erp.com/qdy/go-db/sqldef"
8
+)
5 9
 
6 10
 func init() {
7 11
 	sqldef.AddRegistration(func(r *sqldef.Registry) {
@@ -17,3 +21,22 @@ func init() {
17 21
 		r.RegisterTable(tb.Build())
18 22
 	})
19 23
 }
24
+
25
+// ConfigService 微服务档案表结构体
26
+type ConfigService struct {
27
+	// 服务编码,主键
28
+	ConfigServiceID string `gorm:"column:config_service_id;type:varchar(32);primaryKey;not null;comment:服务编码,主键"`
29
+	// 服务名称
30
+	ServiceName string `gorm:"column:service_name;type:varchar(64);not null;comment:服务名称"`
31
+	// 当前版本
32
+	ServiceVersion string `gorm:"column:service_version;type:varchar(32);not null;default:'1.0.0';comment:当前版本"`
33
+	// 创建人
34
+	Creator string `gorm:"column:creator;type:varchar(32);not null;comment:创建人"`
35
+	// 创建时间
36
+	CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间"`
37
+}
38
+
39
+// TableName 指定表名
40
+func (ConfigService) TableName() string {
41
+	return "config_service"
42
+}

+ 73
- 0
test/my_post_query_test.go Bestand weergeven

@@ -0,0 +1,73 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"log"
6
+	"testing"
7
+
8
+	"git.x2erp.com/qdy/go-base/types"
9
+	"git.x2erp.com/qdy/go-db/factory/http"
10
+)
11
+
12
+func TestQuery(t *testing.T) {
13
+	// 记录总开始时间
14
+	//totalStartTime := time.Now()
15
+
16
+	// 1. 获取HTTP工厂实例
17
+	httpFactory, err := http.GetHTTPFactory()
18
+	if err != nil {
19
+		t.Fatalf("Failed to get HTTP factory: %v", err)
20
+	}
21
+	fmt.Println("HTTP factory created successfully")
22
+
23
+	// 7. 获取Doris工厂实例
24
+	//dorisFactory1, err := doris.GetDorisFactory(httpFactory)
25
+	if err != nil {
26
+		t.Fatalf("Failed to get Doris factory: %v", err)
27
+	}
28
+	fmt.Println("Doris factory created successfully")
29
+
30
+	// 获取Doris配置
31
+	//cfg, err := config.GetConfig()
32
+	if err != nil {
33
+		t.Fatalf("failed to load config: %v", err)
34
+		return
35
+	}
36
+
37
+	queryParams := []interface{}{
38
+		"dev",
39
+		"svc-worker",
40
+	}
41
+	// 准备查询请求
42
+	queryRequest := types.QueryRequest{
43
+
44
+		PositionalParams: queryParams,
45
+	}
46
+
47
+	httpClient := httpFactory.CreateClient()
48
+	// 发送POST请求到 /api/query/csv 获取CSV格式数据
49
+	resp, err := httpClient.PostWithAuth(
50
+		"http://localhost:8080/api/query/yaml",
51
+		queryRequest,
52
+		"123", // Bearer Token
53
+		nil,
54
+	)
55
+
56
+	if err != nil {
57
+		csvData := string(resp.Body())
58
+		log.Printf("查询失败:%v", err)
59
+		log.Printf("csvData--err:%s", csvData)
60
+
61
+		return
62
+	}
63
+
64
+	if resp.StatusCode() != 200 {
65
+		log.Printf("\n 询请求失败, 状态码: %d", resp.StatusCode())
66
+	}
67
+
68
+	// 获取CSV数据
69
+	csvData := string(resp.Body())
70
+
71
+	log.Printf("\n csvData:%s", csvData)
72
+
73
+}

+ 29
- 0
yaml-插入启动配置.sql Bestand weergeven

@@ -0,0 +1,29 @@
1
+INSERT INTO public.config_startup
2
+    (config_startup_id, config_template_id, config_service_id,config_environment_id,"version", creator, created_at, updated_at)
3
+SELECT 
4
+    'svn-configure.dev.'|| config_template_id || '.1' as config_startup_id,
5
+    config_template_id,
6
+    'svn-configure' as config_service_id,
7
+    'dev' as config_environment_id,
8
+    1 as "version",
9
+   	creator,
10
+    created_at,
11
+    updated_at
12
+FROM public.config_template 
13
+where config_template_id='postgresql';
14
+
15
+
16
+
17
+INSERT INTO public.config_startup_detail
18
+    (config_startup_detail_id, config_template_detail_id, config_startup_id, config_key, config_value, creator, created_at, updated_at)
19
+SELECT 
20
+    'dev.'|| a.yaml_root_key || '.' || b.config_key  as config_startup_detail_id,
21
+    b.config_template_detail_id,
22
+    'dev.'|| b.config_template_id as config_startup_id,
23
+    b.config_key,
24
+    b.config_value,
25
+    b.creator,
26
+    b.created_at,
27
+    b.updated_at
28
+FROM public.config_template a , public.config_template_detail b 
29
+WHERE a.config_template_id =b.config_template_id and b.config_template_id='postgresql';

Laden…
Annuleren
Opslaan