Selaa lähdekoodia

添加注册中心代码,注册到consul

qdy 2 kuukautta sitten
commit
f1be7070f9
9 muutettua tiedostoa jossa 704 lisäystä ja 0 poistoa
  1. BIN
      .DS_Store
  2. 1
    0
      .gitignore
  3. 25
    0
      gct.sh
  4. 17
    0
      go.mod
  5. 14
    0
      go.sum
  6. 67
    0
      internal/config/mockdb.go
  7. 319
    0
      internal/factory/factory.go
  8. 38
    0
      internal/factory/types.go
  9. 223
    0
      main.go

BIN
.DS_Store Näytä tiedosto


+ 1
- 0
.gitignore Näytä tiedosto

@@ -0,0 +1 @@
1
+logs/

+ 25
- 0
gct.sh Näytä tiedosto

@@ -0,0 +1,25 @@
1
+#!/bin/bash
2
+
3
+# 使用命令行参数,如果没有提供则使用默认值
4
+COMMIT_MSG="${1:-初始提交}"
5
+TAG_VERSION="${2:-v0.1.0}"
6
+
7
+echo "提交信息: $COMMIT_MSG"
8
+echo "标签版本: $TAG_VERSION"
9
+
10
+# 提交和推送
11
+git add .
12
+git commit -m "$COMMIT_MSG"
13
+git push -u origin master
14
+
15
+# 创建标签(关键修改在这里)
16
+# 方法1:使用注释标签(推荐,不会打开编辑器)
17
+git tag -a "$TAG_VERSION" -m "Release $TAG_VERSION"
18
+
19
+# 或者方法2:如果你需要GPG签名,使用这个
20
+# GIT_EDITOR=true git tag -s "$TAG_VERSION" -m "Release $TAG_VERSION"
21
+
22
+# 推送标签
23
+git push origin "$TAG_VERSION"
24
+
25
+echo "完成!"

+ 17
- 0
go.mod Näytä tiedosto

@@ -0,0 +1,17 @@
1
+module git.x2erp.com/qdy/go-svc-ai
2
+
3
+go 1.25.4
4
+
5
+require (
6
+	git.x2erp.com/qdy/go-base v0.1.70
7
+	git.x2erp.com/qdy/go-db v0.1.70
8
+	go-micro.dev/v4 v4.11.0
9
+)
10
+
11
+require (
12
+	github.com/kr/pretty v0.3.1 // indirect
13
+	github.com/rogpeppe/go-internal v1.14.1 // indirect
14
+	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
15
+)
16
+
17
+require gopkg.in/yaml.v2 v2.4.0 // indirect

+ 14
- 0
go.sum Näytä tiedosto

@@ -0,0 +1,14 @@
1
+git.x2erp.com/qdy/go-base v0.1.44 h1:xHpMppSNj79lqdUc+lmgGXOgEvHyNotUayoe5/hHWr4=
2
+git.x2erp.com/qdy/go-base v0.1.44/go.mod h1:Q+YLwpCoU8CVSnzATLdz2LAzVMlz/CEGzo8DePf7cug=
3
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
4
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
5
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
6
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
7
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
8
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
9
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
10
+go-micro.dev/v4 v4.11.0 h1:DZ2xcr0pnZJDlp6MJiCLhw4tXRxLw9xrJlPT91kubr0=
11
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
12
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
13
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
14
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

+ 67
- 0
internal/config/mockdb.go Näytä tiedosto

@@ -0,0 +1,67 @@
1
+package config
2
+
3
+import "git.x2erp.com/qdy/go-svc-ai/internal/factory"
4
+
5
+// 模拟数据库中的配置
6
+var MockConfigs = map[string]*factory.ModelConfig{
7
+	// OpenAI配置
8
+	"openai-gpt4": {
9
+		Provider:    "openai",
10
+		BaseURL:     "https://api.openai.com/v1",
11
+		APIKey:      "sk-xxx", // 实际使用时从环境变量/数据库读取
12
+		Model:       "gpt-4",
13
+		MaxTokens:   2000,
14
+		Temperature: 0.7,
15
+		Timeout:     30,
16
+	},
17
+
18
+	// DeepSeek配置
19
+	"deepseek-chat": {
20
+		Provider:    "deepseek",
21
+		BaseURL:     "https://api.deepseek.com",
22
+		APIKey:      "sk-xxx",
23
+		Model:       "deepseek-chat",
24
+		MaxTokens:   2048,
25
+		Temperature: 0.7,
26
+		Timeout:     30,
27
+	},
28
+
29
+	// Claude配置
30
+	"claude-3": {
31
+		Provider:    "claude",
32
+		BaseURL:     "https://api.anthropic.com/v1",
33
+		APIKey:      "sk-xxx",
34
+		Model:       "claude-3-sonnet",
35
+		MaxTokens:   4096,
36
+		Temperature: 0.8,
37
+		Timeout:     60,
38
+	},
39
+
40
+	// 流式专用配置
41
+	"openai-stream": {
42
+		Provider:    "openai",
43
+		BaseURL:     "https://api.openai.com/v1",
44
+		APIKey:      "sk-xxx",
45
+		Model:       "gpt-3.5-turbo",
46
+		MaxTokens:   1000,
47
+		Temperature: 0.9,
48
+		Timeout:     60, // 流式需要更长超时
49
+	},
50
+}
51
+
52
+// GetConfig 模拟从数据库读取配置
53
+func GetConfig(configKey string) (*factory.ModelConfig, error) {
54
+	if config, exists := MockConfigs[configKey]; exists {
55
+		return config, nil
56
+	}
57
+	return nil, &ConfigError{Msg: "配置不存在: " + configKey}
58
+}
59
+
60
+// ConfigError 配置错误
61
+type ConfigError struct {
62
+	Msg string
63
+}
64
+
65
+func (e *ConfigError) Error() string {
66
+	return e.Msg
67
+}

+ 319
- 0
internal/factory/factory.go Näytä tiedosto

@@ -0,0 +1,319 @@
1
+package factory
2
+
3
+import (
4
+	"context"
5
+	"encoding/json"
6
+	"fmt"
7
+	"io"
8
+	"net/http"
9
+	"strings"
10
+	"time"
11
+)
12
+
13
+// 极简HTTP客户端实现
14
+type SimpleHTTPClient struct {
15
+	config *ModelConfig
16
+	client *http.Client
17
+}
18
+
19
+// NewSimpleHTTPClient 创建客户端(每次请求创建新的)
20
+func NewSimpleHTTPClient(config *ModelConfig) *SimpleHTTPClient {
21
+	return &SimpleHTTPClient{
22
+		config: config,
23
+		client: &http.Client{
24
+			Timeout: time.Duration(config.Timeout) * time.Second,
25
+		},
26
+	}
27
+}
28
+
29
+// Chat 一次性返回(阻塞式)
30
+func (c *SimpleHTTPClient) Chat(ctx context.Context, messages []ChatMessage) (string, error) {
31
+	// 构建请求
32
+	reqBody := map[string]interface{}{
33
+		"model":       c.config.Model,
34
+		"messages":    messages,
35
+		"max_tokens":  c.config.MaxTokens,
36
+		"temperature": c.config.Temperature,
37
+		"stream":      false,
38
+	}
39
+
40
+	// 根据不同提供商调整请求格式
41
+	reqBody = c.adjustRequestForProvider(reqBody)
42
+
43
+	// 发送请求
44
+	resp, err := c.doRequest(ctx, reqBody)
45
+	if err != nil {
46
+		return "", err
47
+	}
48
+	defer resp.Body.Close()
49
+
50
+	// 解析响应
51
+	var result map[string]interface{}
52
+	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
53
+		return "", fmt.Errorf("解析响应失败: %v", err)
54
+	}
55
+
56
+	// 提取回复内容
57
+	return c.extractContent(result), nil
58
+}
59
+
60
+// ChatStream 流式返回
61
+func (c *SimpleHTTPClient) ChatStream(ctx context.Context, messages []ChatMessage) (<-chan string, error) {
62
+	ch := make(chan string)
63
+
64
+	go func() {
65
+		defer close(ch)
66
+
67
+		// 构建请求
68
+		reqBody := map[string]interface{}{
69
+			"model":       c.config.Model,
70
+			"messages":    messages,
71
+			"max_tokens":  c.config.MaxTokens,
72
+			"temperature": c.config.Temperature,
73
+			"stream":      true,
74
+		}
75
+
76
+		reqBody = c.adjustRequestForProvider(reqBody)
77
+
78
+		// 发送请求
79
+		resp, err := c.doRequest(ctx, reqBody)
80
+		if err != nil {
81
+			ch <- fmt.Sprintf("错误: %v", err)
82
+			return
83
+		}
84
+		defer resp.Body.Close()
85
+
86
+		// 流式读取
87
+		reader := c.createStreamReader(resp.Body)
88
+		for {
89
+			select {
90
+			case <-ctx.Done():
91
+				return
92
+			default:
93
+				line, err := reader()
94
+				if err != nil {
95
+					if err != io.EOF {
96
+						ch <- fmt.Sprintf("读取错误: %v", err)
97
+					}
98
+					return
99
+				}
100
+				if line != "" {
101
+					ch <- line
102
+				}
103
+			}
104
+		}
105
+	}()
106
+
107
+	return ch, nil
108
+}
109
+
110
+// doRequest 执行HTTP请求
111
+func (c *SimpleHTTPClient) doRequest(ctx context.Context, body map[string]interface{}) (*http.Response, error) {
112
+	// 序列化请求体
113
+	jsonData, err := json.Marshal(body)
114
+	if err != nil {
115
+		return nil, fmt.Errorf("序列化请求失败: %v", err)
116
+	}
117
+
118
+	// 确定端点URL
119
+	endpoint := c.getEndpointURL()
120
+
121
+	// 创建请求
122
+	req, err := http.NewRequestWithContext(ctx, "POST", endpoint, strings.NewReader(string(jsonData)))
123
+	if err != nil {
124
+		return nil, fmt.Errorf("创建请求失败: %v", err)
125
+	}
126
+
127
+	// 设置请求头
128
+	c.setHeaders(req)
129
+
130
+	// 发送请求
131
+	resp, err := c.client.Do(req)
132
+	if err != nil {
133
+		return nil, fmt.Errorf("请求失败: %v", err)
134
+	}
135
+
136
+	// 检查状态码
137
+	if resp.StatusCode != http.StatusOK {
138
+		body, _ := io.ReadAll(resp.Body)
139
+		resp.Body.Close()
140
+		return nil, fmt.Errorf("API错误: %s, 响应: %s", resp.Status, string(body))
141
+	}
142
+
143
+	return resp, nil
144
+}
145
+
146
+// getEndpointURL 获取API端点URL
147
+func (c *SimpleHTTPClient) getEndpointURL() string {
148
+	switch c.config.Provider {
149
+	case "openai":
150
+		return c.config.BaseURL + "/chat/completions"
151
+	case "deepseek":
152
+		return c.config.BaseURL + "/chat/completions"
153
+	case "claude":
154
+		return c.config.BaseURL + "/messages"
155
+	default:
156
+		return c.config.BaseURL + "/chat/completions"
157
+	}
158
+}
159
+
160
+// setHeaders 设置请求头
161
+func (c *SimpleHTTPClient) setHeaders(req *http.Request) {
162
+	req.Header.Set("Content-Type", "application/json")
163
+	req.Header.Set("Authorization", "Bearer "+c.config.APIKey)
164
+
165
+	// 提供商特定头
166
+	switch c.config.Provider {
167
+	case "claude":
168
+		req.Header.Set("x-api-key", c.config.APIKey)
169
+		req.Header.Set("anthropic-version", "2023-06-01")
170
+	case "openai", "deepseek":
171
+		// 标准头
172
+	}
173
+}
174
+
175
+// adjustRequestForProvider 调整请求格式
176
+func (c *SimpleHTTPClient) adjustRequestForProvider(reqBody map[string]interface{}) map[string]interface{} {
177
+	switch c.config.Provider {
178
+	case "claude":
179
+		// Claude使用不同的格式
180
+		return map[string]interface{}{
181
+			"model":      c.config.Model,
182
+			"messages":   reqBody["messages"],
183
+			"max_tokens": c.config.MaxTokens,
184
+		}
185
+	default:
186
+		return reqBody
187
+	}
188
+}
189
+
190
+// extractContent 从响应中提取内容
191
+func (c *SimpleHTTPClient) extractContent(resp map[string]interface{}) string {
192
+	switch c.config.Provider {
193
+	case "openai", "deepseek":
194
+		if choices, ok := resp["choices"].([]interface{}); ok && len(choices) > 0 {
195
+			if choice, ok := choices[0].(map[string]interface{}); ok {
196
+				if message, ok := choice["message"].(map[string]interface{}); ok {
197
+					if content, ok := message["content"].(string); ok {
198
+						return content
199
+					}
200
+				}
201
+			}
202
+		}
203
+	case "claude":
204
+		if content, ok := resp["content"].([]interface{}); ok && len(content) > 0 {
205
+			if first, ok := content[0].(map[string]interface{}); ok {
206
+				if text, ok := first["text"].(string); ok {
207
+					return text
208
+				}
209
+			}
210
+		}
211
+	}
212
+	return ""
213
+}
214
+
215
+// createStreamReader 创建流式读取器
216
+func (c *SimpleHTTPClient) createStreamReader(body io.Reader) func() (string, error) {
217
+	buf := make([]byte, 4096)
218
+	var leftover []byte
219
+
220
+	return func() (string, error) {
221
+		// 读取数据
222
+		n, err := body.Read(buf)
223
+		if err != nil {
224
+			return "", err
225
+		}
226
+
227
+		data := append(leftover, buf[:n]...)
228
+		lines := strings.Split(string(data), "\n")
229
+
230
+		// 处理完整的行
231
+		var result strings.Builder
232
+		for i, line := range lines {
233
+			if i == len(lines)-1 {
234
+				// 最后一行可能不完整,留到下次
235
+				leftover = []byte(line)
236
+				continue
237
+			}
238
+
239
+			line = strings.TrimSpace(line)
240
+			if line == "" || !strings.HasPrefix(line, "data: ") {
241
+				continue
242
+			}
243
+
244
+			// 去除"data: "前缀
245
+			line = line[6:]
246
+			if line == "[DONE]" {
247
+				return "", io.EOF
248
+			}
249
+
250
+			// 解析JSON
251
+			var chunk map[string]interface{}
252
+			if err := json.Unmarshal([]byte(line), &chunk); err != nil {
253
+				continue
254
+			}
255
+
256
+			// 提取内容
257
+			content := c.extractStreamContent(chunk)
258
+			if content != "" {
259
+				result.WriteString(content)
260
+			}
261
+		}
262
+
263
+		return result.String(), nil
264
+	}
265
+}
266
+
267
+// extractStreamContent 提取流式响应内容
268
+func (c *SimpleHTTPClient) extractStreamContent(chunk map[string]interface{}) string {
269
+	switch c.config.Provider {
270
+	case "openai", "deepseek":
271
+		if choices, ok := chunk["choices"].([]interface{}); ok && len(choices) > 0 {
272
+			if choice, ok := choices[0].(map[string]interface{}); ok {
273
+				if delta, ok := choice["delta"].(map[string]interface{}); ok {
274
+					if content, ok := delta["content"].(string); ok {
275
+						return content
276
+					}
277
+				}
278
+			}
279
+		}
280
+	case "claude":
281
+		if content, ok := chunk["content"].([]interface{}); ok && len(content) > 0 {
282
+			if first, ok := content[0].(map[string]interface{}); ok {
283
+				if text, ok := first["text"].(string); ok {
284
+					return text
285
+				}
286
+			}
287
+		}
288
+	}
289
+	return ""
290
+}
291
+
292
+// ==================== 工厂函数 ====================
293
+
294
+// CreateClient 工厂函数:根据配置创建客户端
295
+func CreateClient(configKey string) (AIClient, error) {
296
+	// 这里模拟从"数据库"读取配置
297
+	// 实际项目中应该从数据库/配置中心读取
298
+	config, err := GetConfigFromMockDB(configKey)
299
+	if err != nil {
300
+		return nil, err
301
+	}
302
+
303
+	return NewSimpleHTTPClient(config), nil
304
+}
305
+
306
+// GetConfigFromMockDB 模拟从数据库获取配置
307
+func GetConfigFromMockDB(configKey string) (*ModelConfig, error) {
308
+	// 这里调用模拟数据库
309
+	// 实际应该调用数据库查询
310
+	return &ModelConfig{
311
+		Provider:    "openai",
312
+		BaseURL:     "https://api.openai.com/v1",
313
+		APIKey:      "fake-key-for-demo",
314
+		Model:       "gpt-3.5-turbo",
315
+		MaxTokens:   1000,
316
+		Temperature: 0.7,
317
+		Timeout:     30,
318
+	}, nil
319
+}

+ 38
- 0
internal/factory/types.go Näytä tiedosto

@@ -0,0 +1,38 @@
1
+package factory
2
+
3
+import (
4
+	"context"
5
+)
6
+
7
+// ModelConfig 模型配置(从数据库读取)
8
+type ModelConfig struct {
9
+	Provider    string  // "openai", "deepseek", "claude"
10
+	BaseURL     string  // API地址
11
+	APIKey      string  // API密钥
12
+	Model       string  // 模型名称
13
+	MaxTokens   int     // 最大token
14
+	Temperature float64 // 温度
15
+	Timeout     int     // 超时时间(秒)
16
+}
17
+
18
+// AIClient AI客户端接口
19
+type AIClient interface {
20
+	// Chat 一次性返回
21
+	Chat(ctx context.Context, messages []ChatMessage) (string, error)
22
+
23
+	// ChatStream 流式返回
24
+	ChatStream(ctx context.Context, messages []ChatMessage) (<-chan string, error)
25
+}
26
+
27
+// ChatMessage 聊天消息
28
+type ChatMessage struct {
29
+	Role    string `json:"role"` // "system", "user", "assistant"
30
+	Content string `json:"content"`
31
+}
32
+
33
+// RequestOptions 请求选项
34
+type RequestOptions struct {
35
+	MaxTokens   int
36
+	Temperature float64
37
+	Stream      bool
38
+}

+ 223
- 0
main.go Näytä tiedosto

@@ -0,0 +1,223 @@
1
+package main
2
+
3
+import (
4
+	"log"
5
+
6
+	"git.x2erp.com/qdy/go-db/dbstart"
7
+	"git.x2erp.com/qdy/go-db/factory/database"
8
+
9
+	"git.x2erp.com/qdy/go-base/bootstraps"
10
+	"git.x2erp.com/qdy/go-base/ctx"
11
+	"git.x2erp.com/qdy/go-base/logger"
12
+	"git.x2erp.com/qdy/go-base/middleware"
13
+	"git.x2erp.com/qdy/go-base/webx"
14
+)
15
+
16
+var (
17
+	serviceName    = "svc-ai"
18
+	serviceVersion = "1"
19
+)
20
+
21
+// 定义结构体
22
+type CreateUserRequest struct {
23
+	Name  string `json:"name"`
24
+	Email string `json:"email"`
25
+	Age   int    `json:"age"`
26
+}
27
+
28
+type UserResponse struct {
29
+	ID    string `json:"id"`
30
+	Name  string `json:"name"`
31
+	Email string `json:"email"`
32
+	Data  []map[string]interface{}
33
+}
34
+
35
+func main() {
36
+	// 1. 初始化基础启动器
37
+	boot := bootstraps.NewBootstrapper(serviceName, serviceVersion)
38
+
39
+	// 2. 初始化数据库(都在bootstraps包中)
40
+	dbBoot := dbstart.NewDBBootstrapper(boot.GetConfig())
41
+	dbBoot.Init()
42
+
43
+	agentDB := dbBoot.GetDBFactory("agent")
44
+	// 赋值认证中间件参数
45
+	middleware.JWTAuthMiddlewareInit(boot.GetConfig())
46
+
47
+	// 3. 启动服务
48
+	//boot.StartService("default")
49
+
50
+	router := boot.GetRouter("default")
51
+	// 3. 创建 WebService 并传入数据库接口
52
+	ws := webx.NewWebService(router)
53
+
54
+	//使用默认数据库
55
+	registerDefaultRouter(ws, dbBoot.GetDefaultDBFactory())
56
+
57
+	//启动第2个服务
58
+	routerDoris := boot.GetRouter("doris")
59
+	wsDoris := webx.NewWebService(routerDoris)
60
+	registerDorisRouter(wsDoris, agentDB)
61
+
62
+	// 6. 运行服务
63
+	boot.Run(dbBoot)
64
+}
65
+
66
+func registerDorisRouter(ws *webx.WebService, dbFactory *database.DBFactory) {
67
+	// GET示例:路径参数绑定
68
+	ws.GET("/app/users/{id}",
69
+		func(id string, reqCtx *ctx.RequestContext) (UserResponse, error) {
70
+
71
+			log.Print("ctx TenantID:", reqCtx.TenantID)
72
+			// id 自动从路径绑定
73
+			// 注意:webx版本没有自动注入dbFactory
74
+			return getUserOracle(id, dbFactory) // 需要修改getUser函数以获取dbFactory
75
+		},
76
+	).Use(middleware.JWTAuthMiddleware).Register()
77
+
78
+	// POST示例:Body参数绑定
79
+	ws.POST("/app/users",
80
+		func(req CreateUserRequest, reqCtx *ctx.RequestContext) (UserResponse, error) {
81
+			log.Print("ctx TenantID:", reqCtx.TenantID)
82
+			// req 自动从JSON Body绑定
83
+			return createUser(req, nil) // 需要修改createUser函数以获取dbFactory
84
+		},
85
+	).Use(middleware.JWTAuthMiddleware).Register()
86
+
87
+	// 您的业务路由
88
+	ws.POST("/app/query/yaml",
89
+		func() (interface{}, error) {
90
+			return queryYaml(nil) // 需要修改queryYaml函数以获取dbFactory
91
+		},
92
+	).Use(middleware.JWTAuthMiddleware).Register()
93
+
94
+	ws.POST("/app/init/config/template",
95
+		func() (interface{}, error) {
96
+			return initConfigTemplate(nil) // 需要修改initConfigTemplate函数以获取dbFactory
97
+		},
98
+	).Use(middleware.JWTAuthMiddleware).Register()
99
+
100
+}
101
+
102
+func registerDefaultRouter(ws *webx.WebService, dbFactory *database.DBFactory) {
103
+	// GET示例:路径参数绑定
104
+	ws.GET("/api/users/{id}",
105
+		func(id string, reqCtx *ctx.RequestContext) (UserResponse, error) {
106
+
107
+			log.Print("ctx TenantID:", reqCtx.TenantID)
108
+			// id 自动从路径绑定
109
+			// 注意:webx版本没有自动注入dbFactory
110
+			return getUser(id, dbFactory) // 需要修改getUser函数以获取dbFactory
111
+		},
112
+	).Use(middleware.JWTAuthMiddleware).Register()
113
+
114
+	// POST示例:Body参数绑定
115
+	ws.POST("/api/users",
116
+		func(req CreateUserRequest, reqCtx *ctx.RequestContext) (UserResponse, error) {
117
+			log.Print("ctx TenantID:", reqCtx.TenantID)
118
+			// req 自动从JSON Body绑定
119
+			return createUser(req, nil) // 需要修改createUser函数以获取dbFactory
120
+		},
121
+	).Use(middleware.JWTAuthMiddleware).Register()
122
+
123
+	// 您的业务路由
124
+	ws.POST("/api/query/yaml",
125
+		func() (interface{}, error) {
126
+			return queryYaml(nil) // 需要修改queryYaml函数以获取dbFactory
127
+		},
128
+	).Use(middleware.JWTAuthMiddleware).Register()
129
+
130
+	ws.POST("/api/init/config/template",
131
+		func() (interface{}, error) {
132
+			return initConfigTemplate(nil) // 需要修改initConfigTemplate函数以获取dbFactory
133
+		},
134
+	).Use(middleware.JWTAuthMiddleware).Register()
135
+
136
+}
137
+
138
+// 4. 创建路由器
139
+// 首先需要创建标准库的ServeMux
140
+//mux := http.NewServeMux()
141
+//router := webx.NewRouter(boot.GetRouter())
142
+//router := webx.NewWebService(mux)
143
+
144
+// 5. 注册路由(Spring风格!)
145
+
146
+func getUser(id string, dbFactory *database.DBFactory) (UserResponse, error) {
147
+	if dbFactory != nil {
148
+		dbFactory.TestConnection(dbFactory.GetDBType())
149
+	}
150
+
151
+	// 查询数据库...
152
+	return UserResponse{
153
+		ID:    id,
154
+		Name:  "掌扇过去-AAAAA",
155
+		Email: "zhangsan@example.com",
156
+	}, nil
157
+}
158
+
159
+// 业务函数示例
160
+func getUserOracle(id string, dbFactory *database.DBFactory) (UserResponse, error) {
161
+	if dbFactory != nil {
162
+		dbFactory.TestConnection(dbFactory.GetDBType())
163
+	}
164
+
165
+	sql, queryParams := getSQLWithPaginationSQL(0, 10)
166
+
167
+	jsonData := dbFactory.QueryPositionalToJSON(sql, queryParams, nil)
168
+	// 查询数据库...
169
+	return UserResponse{
170
+		ID:    id,
171
+		Name:  "掌扇过去",
172
+		Email: "zhangsan@example.com",
173
+		Data:  jsonData.Data,
174
+	}, nil
175
+}
176
+
177
+func createUser(req CreateUserRequest, dbFactory *database.DBFactory) (UserResponse, error) {
178
+	logger.Debug("CreateUserRequest:%v", req)
179
+	return UserResponse{
180
+		ID:    "1",
181
+		Name:  req.Name,
182
+		Email: req.Email,
183
+	}, nil
184
+}
185
+
186
+func queryYaml(dbFactory *database.DBFactory) (interface{}, error) {
187
+	// 您的业务逻辑
188
+	return map[string]interface{}{"message": "query yaml success"}, nil
189
+}
190
+
191
+func initConfigTemplate(dbFactory *database.DBFactory) (interface{}, error) {
192
+	// 您的业务逻辑
193
+	return map[string]interface{}{"message": "init config success"}, nil
194
+}
195
+
196
+// getSQLWithPagination 生成带分页的SQL语句(参数模式)
197
+// 返回SQL语句和参数映射
198
+func getSQLWithPaginationSQL(startRow, endRow int) (string, []interface{}) {
199
+	sql := `SELECT
200
+    CLOTHING_ID,
201
+    CLOTHING_YEAR,
202
+    CLOTHING_NAME
203
+
204
+   FROM (
205
+    SELECT a.*, ROWNUM as rn
206
+    FROM (
207
+        SELECT *
208
+        FROM X6_STOCK_DEV.A3_CLOTHING 
209
+     
210
+        ORDER BY CLOTHING_ID
211
+    ) a
212
+    WHERE ROWNUM <= :1
213
+)
214
+WHERE rn > :2`
215
+
216
+	// 创建参数映射
217
+	params := []interface{}{
218
+		endRow,
219
+		startRow - 1, // WHERE rn > :start_row 所以是startRow-1
220
+	}
221
+
222
+	return sql, params
223
+}

Loading…
Peruuta
Tallenna