qdy 2 miesięcy temu
commit
73d43d2a50

+ 126
- 0
consumer/consumer_manager.go Wyświetl plik

@@ -0,0 +1,126 @@
1
+package consumer
2
+
3
+import (
4
+	"fmt"
5
+	"log"
6
+	"sync"
7
+
8
+	"git.x2erp.com/qdy/go-base/types"
9
+	"git.x2erp.com/qdy/go-db/factory/rabbitmq"
10
+
11
+	"git.x2erp.com/qdy/go-svc-mqconsumer/handlers"
12
+)
13
+
14
+// ConsumerManager 消费者管理器
15
+type ConsumerManager struct {
16
+	rabbitFactory *rabbitmq.RabbitMQFactory
17
+	consumers     sync.Map // consumerID -> *rabbitmq.Consumer
18
+	handler       *handlers.HTTPHandler
19
+}
20
+
21
+// NewConsumerManager 创建消费者管理器
22
+func NewConsumerManager(rabbitFactory *rabbitmq.RabbitMQFactory) *ConsumerManager {
23
+	return &ConsumerManager{
24
+		rabbitFactory: rabbitFactory,
25
+		handler:       handlers.NewHTTPHandler(),
26
+	}
27
+}
28
+
29
+// StartAllConsumers 启动所有消费者
30
+func (cm *ConsumerManager) StartAllConsumers() error {
31
+	queueConfigs, err := config.GetQueueConfigsFromDB()
32
+	if err != nil {
33
+		return fmt.Errorf("failed to load config: %v", err)
34
+	}
35
+	log.Printf("Starting %d consumers...", len(queueConfigs))
36
+
37
+	for _, config := range queueConfigs {
38
+		if err := cm.StartConsumer(config); err != nil {
39
+			log.Printf("Failed to start consumer for queue %s: %v", config.QueueName, err)
40
+			continue
41
+		}
42
+		log.Printf("Consumer started for queue: %s", config.QueueName)
43
+	}
44
+
45
+	return nil
46
+}
47
+
48
+// StartConsumer 启动单个消费者
49
+func (cm *ConsumerManager) StartConsumer(queueReq types.QueueRequest) error {
50
+	// 生成消费者ID
51
+	consumerID := fmt.Sprintf("%s_consumer", queueReq.QueueName)
52
+
53
+	// 检查是否已存在
54
+	if _, exists := cm.consumers.Load(consumerID); exists {
55
+		return fmt.Errorf("consumer %s already exists", consumerID)
56
+	}
57
+
58
+	// 创建消费者
59
+	consumer, err := cm.rabbitFactory.CreateConsumer(&queueReq, consumerID, cm.handler)
60
+	if err != nil {
61
+		return fmt.Errorf("failed to create consumer: %v", err)
62
+	}
63
+
64
+	// 保存消费者
65
+	cm.consumers.Store(consumerID, consumer)
66
+	log.Printf("Consumer %s started for queue %s", consumerID, queueReq.QueueName)
67
+
68
+	return nil
69
+}
70
+
71
+// StopConsumer 停止指定消费者
72
+func (cm *ConsumerManager) StopConsumer(consumerID string) error {
73
+	// 获取消费者
74
+	consumerInterface, exists := cm.consumers.Load(consumerID)
75
+	if !exists {
76
+		return fmt.Errorf("consumer %s not found", consumerID)
77
+	}
78
+
79
+	consumer := consumerInterface.(*rabbitmq.Consumer)
80
+
81
+	// 关闭通道
82
+	if err := consumer.Channel.Close(); err != nil {
83
+		return fmt.Errorf("failed to close channel: %v", err)
84
+	}
85
+
86
+	// 从map中移除
87
+	cm.consumers.Delete(consumerID)
88
+
89
+	// 关闭RabbitMQ通道
90
+	if err := cm.rabbitFactory.CloseChannel(consumerID); err != nil {
91
+		log.Printf("Warning: failed to close RabbitMQ channel %s: %v", consumerID, err)
92
+	}
93
+
94
+	log.Printf("Consumer %s stopped", consumerID)
95
+	return nil
96
+}
97
+
98
+// StopAllConsumers 停止所有消费者
99
+func (cm *ConsumerManager) StopAllConsumers() error {
100
+	var errs []error
101
+
102
+	cm.consumers.Range(func(key, value interface{}) bool {
103
+		consumerID := key.(string)
104
+		if err := cm.StopConsumer(consumerID); err != nil {
105
+			errs = append(errs, fmt.Errorf("failed to stop consumer %s: %v", consumerID, err))
106
+		}
107
+		return true
108
+	})
109
+
110
+	if len(errs) > 0 {
111
+		return fmt.Errorf("errors stopping consumers: %v", errs)
112
+	}
113
+
114
+	log.Println("All consumers stopped")
115
+	return nil
116
+}
117
+
118
+// GetConsumerCount 获取消费者数量
119
+func (cm *ConsumerManager) GetConsumerCount() int {
120
+	count := 0
121
+	cm.consumers.Range(func(_, _ interface{}) bool {
122
+		count++
123
+		return true
124
+	})
125
+	return count
126
+}

+ 57
- 0
functions/exchange_functions.go Wyświetl plik

@@ -0,0 +1,57 @@
1
+package functions
2
+
3
+import (
4
+	"time"
5
+
6
+	"git.x2erp.com/qdy/go-base/ctx"
7
+	"git.x2erp.com/qdy/go-base/types"
8
+	"git.x2erp.com/qdy/go-db/factory/rabbitmq"
9
+)
10
+
11
+// CreateExchange 创建交换机
12
+func CreateExchange(rabbitFactory *rabbitmq.RabbitMQFactory, req types.ExchangeRequest, reqCtx *ctx.RequestContext) *types.QueryResult {
13
+	// 设置默认值
14
+	if req.ChannelName == "" {
15
+		req.ChannelName = "default"
16
+	}
17
+	if req.ExchangeType == "" {
18
+		req.ExchangeType = "direct"
19
+	}
20
+
21
+	// 确保通道存在
22
+	_, err := rabbitFactory.CreateChannel(req.ChannelName)
23
+	if err != nil {
24
+		return &types.QueryResult{
25
+			Success: false,
26
+			Error:   "Failed to create channel: " + err.Error(),
27
+			Time:    time.Now().Format(time.RFC3339),
28
+		}
29
+	}
30
+
31
+	// 创建交换机
32
+	err = rabbitFactory.AddExchange(
33
+		req.ChannelName,
34
+		req.ExchangeName,
35
+		req.ExchangeType,
36
+		req.Durable,
37
+	)
38
+	if err != nil {
39
+		return &types.QueryResult{
40
+			Success: false,
41
+			Error:   "Failed to create exchange: " + err.Error(),
42
+			Time:    time.Now().Format(time.RFC3339),
43
+		}
44
+	}
45
+
46
+	return &types.QueryResult{
47
+		Success: true,
48
+		Message: "Exchange created successfully",
49
+		Time:    time.Now().Format(time.RFC3339),
50
+		Data: map[string]interface{}{
51
+			"exchange_name": req.ExchangeName,
52
+			"exchange_type": req.ExchangeType,
53
+			"durable":       req.Durable,
54
+			"channel":       req.ChannelName,
55
+		},
56
+	}
57
+}

+ 165
- 0
functions/message_functions.go Wyświetl plik

@@ -0,0 +1,165 @@
1
+package functions
2
+
3
+import (
4
+	"encoding/json"
5
+	"time"
6
+
7
+	"git.x2erp.com/qdy/go-base/ctx"
8
+	"git.x2erp.com/qdy/go-base/types"
9
+	"git.x2erp.com/qdy/go-db/factory/rabbitmq"
10
+	"github.com/streadway/amqp"
11
+)
12
+
13
+// SendMessage 发送JSON消息
14
+func SendMessage(rabbitFactory *rabbitmq.RabbitMQFactory, req types.MessageRequest, reqCtx *ctx.RequestContext) *types.QueryResult {
15
+	// 设置默认值
16
+	if req.ChannelName == "" {
17
+		req.ChannelName = "default"
18
+	}
19
+	if req.ExchangeName == "" {
20
+		req.ExchangeName = "" // 默认交换机
21
+	}
22
+	if req.ContentType == "" {
23
+		req.ContentType = "application/json"
24
+	}
25
+	if req.Timestamp.IsZero() {
26
+		req.Timestamp = time.Now()
27
+	}
28
+
29
+	// 确保通道存在
30
+	channel, err := rabbitFactory.GetChannel(req.ChannelName)
31
+	if err != nil {
32
+		return &types.QueryResult{
33
+			Success: false,
34
+			Error:   "Failed to get channel: " + err.Error(),
35
+			Time:    time.Now().Format(time.RFC3339),
36
+		}
37
+	}
38
+
39
+	// 序列化消息
40
+	messageBody, err := json.Marshal(req.Message)
41
+	if err != nil {
42
+		return &types.QueryResult{
43
+			Success: false,
44
+			Error:   "Failed to marshal message: " + err.Error(),
45
+			Time:    time.Now().Format(time.RFC3339),
46
+		}
47
+	}
48
+
49
+	// 创建消息
50
+	msg := amqp.Publishing{
51
+		ContentType:   req.ContentType,
52
+		Body:          messageBody,
53
+		Headers:       amqp.Table(req.Headers),
54
+		Priority:      req.Priority,
55
+		CorrelationId: req.CorrelationID,
56
+		ReplyTo:       req.ReplyTo,
57
+		Expiration:    req.Expiration,
58
+		MessageId:     req.MessageID,
59
+		Timestamp:     req.Timestamp,
60
+		Type:          req.Type,
61
+		AppId:         req.AppID,
62
+		DeliveryMode:  amqp.Persistent, // 持久化消息
63
+	}
64
+
65
+	// 发送消息
66
+	err = channel.Publish(
67
+		req.ExchangeName,
68
+		req.RoutingKey,
69
+		false, // mandatory
70
+		false, // immediate
71
+		msg,
72
+	)
73
+	if err != nil {
74
+		return &types.QueryResult{
75
+			Success: false,
76
+			Error:   "Failed to publish message: " + err.Error(),
77
+			Time:    time.Now().Format(time.RFC3339),
78
+		}
79
+	}
80
+
81
+	return &types.QueryResult{
82
+		Success: true,
83
+		Message: "Message sent successfully",
84
+		Time:    time.Now().Format(time.RFC3339),
85
+		Data: map[string]interface{}{
86
+			"exchange":     req.ExchangeName,
87
+			"routing_key":  req.RoutingKey,
88
+			"channel":      req.ChannelName,
89
+			"message_size": len(messageBody),
90
+			"content_type": req.ContentType,
91
+			"timestamp":    req.Timestamp.Format(time.RFC3339),
92
+			"MessageID":    req.MessageID,
93
+		},
94
+	}
95
+}
96
+
97
+// SendBytesMessage 发送字节消息
98
+func SendBytesMessage(rabbitFactory *rabbitmq.RabbitMQFactory, req types.BytesMessageRequest, reqCtx *ctx.RequestContext) *types.QueryResult {
99
+	// 设置默认值
100
+	if req.ChannelName == "" {
101
+		req.ChannelName = "default"
102
+	}
103
+	if req.ExchangeName == "" {
104
+		req.ExchangeName = ""
105
+	}
106
+	if req.ContentType == "" {
107
+		req.ContentType = "application/octet-stream"
108
+	}
109
+
110
+	// 确保通道存在
111
+	channel, err := rabbitFactory.GetChannel(req.ChannelName)
112
+	if err != nil {
113
+		return &types.QueryResult{
114
+			Success: false,
115
+			Error:   "Failed to get channel: " + err.Error(),
116
+			Time:    time.Now().Format(time.RFC3339),
117
+		}
118
+	}
119
+
120
+	// 创建消息
121
+	msg := amqp.Publishing{
122
+		ContentType:  req.ContentType,
123
+		Body:         req.Data,
124
+		Headers:      convertHeaders(req.Headers),
125
+		DeliveryMode: amqp.Persistent,
126
+	}
127
+
128
+	// 发送消息
129
+	err = channel.Publish(
130
+		req.ExchangeName,
131
+		req.RoutingKey,
132
+		false,
133
+		false,
134
+		msg,
135
+	)
136
+	if err != nil {
137
+		return &types.QueryResult{
138
+			Success: false,
139
+			Error:   "Failed to publish bytes message: " + err.Error(),
140
+			Time:    time.Now().Format(time.RFC3339),
141
+		}
142
+	}
143
+
144
+	return &types.QueryResult{
145
+		Success: true,
146
+		Message: "Bytes message sent successfully",
147
+		Time:    time.Now().Format(time.RFC3339),
148
+		Data: map[string]interface{}{
149
+			"exchange":     req.ExchangeName,
150
+			"routing_key":  req.RoutingKey,
151
+			"channel":      req.ChannelName,
152
+			"data_size":    len(req.Data),
153
+			"content_type": req.ContentType,
154
+		},
155
+	}
156
+}
157
+
158
+// 转换headers类型
159
+func convertHeaders(headers map[string]string) amqp.Table {
160
+	table := make(amqp.Table)
161
+	for k, v := range headers {
162
+		table[k] = v
163
+	}
164
+	return table
165
+}

+ 94
- 0
functions/queue_functions.go Wyświetl plik

@@ -0,0 +1,94 @@
1
+package functions
2
+
3
+import (
4
+	"time"
5
+
6
+	"git.x2erp.com/qdy/go-base/ctx"
7
+	"git.x2erp.com/qdy/go-base/types"
8
+	"git.x2erp.com/qdy/go-db/factory/rabbitmq"
9
+)
10
+
11
+// CreateQueue 创建队列
12
+func CreateQueue(rabbitFactory *rabbitmq.RabbitMQFactory, req types.QueueRequest, reqCtx *ctx.RequestContext) *types.QueryResult {
13
+	// 设置默认值
14
+	if req.ChannelName == "" {
15
+		req.ChannelName = "default"
16
+	}
17
+
18
+	// 确保通道存在
19
+	_, err := rabbitFactory.CreateChannel(req.ChannelName)
20
+	if err != nil {
21
+		return &types.QueryResult{
22
+			Success: false,
23
+			Error:   "Failed to create channel: " + err.Error(),
24
+			Time:    time.Now().Format(time.RFC3339),
25
+		}
26
+	}
27
+
28
+	// 创建队列
29
+	err = rabbitFactory.AddQueue(
30
+		req.ChannelName,
31
+		req.QueueName,
32
+		req.Durable,
33
+		req.Exclusive,
34
+		req.AutoDelete,
35
+	)
36
+	if err != nil {
37
+		return &types.QueryResult{
38
+			Success: false,
39
+			Error:   "Failed to create queue: " + err.Error(),
40
+			Time:    time.Now().Format(time.RFC3339),
41
+		}
42
+	}
43
+
44
+	return &types.QueryResult{
45
+		Success: true,
46
+		Message: "Queue created successfully",
47
+		Time:    time.Now().Format(time.RFC3339),
48
+		Data: map[string]interface{}{
49
+			"queue_name":  req.QueueName,
50
+			"durable":     req.Durable,
51
+			"exclusive":   req.Exclusive,
52
+			"auto_delete": req.AutoDelete,
53
+			"channel":     req.ChannelName,
54
+		},
55
+	}
56
+}
57
+
58
+// BindQueue 绑定队列到交换机
59
+func BindQueue(rabbitFactory *rabbitmq.RabbitMQFactory, req types.QueueBindRequest, reqCtx *ctx.RequestContext) *types.QueryResult {
60
+	// 设置默认值
61
+	if req.ChannelName == "" {
62
+		req.ChannelName = "default"
63
+	}
64
+	if req.RoutingKey == "" {
65
+		req.RoutingKey = req.QueueName
66
+	}
67
+
68
+	// 绑定队列
69
+	err := rabbitFactory.BindQueue(
70
+		req.ChannelName,
71
+		req.QueueName,
72
+		req.ExchangeName,
73
+		req.RoutingKey,
74
+	)
75
+	if err != nil {
76
+		return &types.QueryResult{
77
+			Success: false,
78
+			Error:   "Failed to bind queue: " + err.Error(),
79
+			Time:    time.Now().Format(time.RFC3339),
80
+		}
81
+	}
82
+
83
+	return &types.QueryResult{
84
+		Success: true,
85
+		Message: "Queue bound to exchange successfully",
86
+		Time:    time.Now().Format(time.RFC3339),
87
+		Data: map[string]interface{}{
88
+			"queue":       req.QueueName,
89
+			"exchange":    req.ExchangeName,
90
+			"routing_key": req.RoutingKey,
91
+			"channel":     req.ChannelName,
92
+		},
93
+	}
94
+}

+ 75
- 0
functions/utility_functions.go Wyświetl plik

@@ -0,0 +1,75 @@
1
+package functions
2
+
3
+import (
4
+	"encoding/json"
5
+	"net/http"
6
+	"time"
7
+
8
+	"git.x2erp.com/qdy/go-base/ctx"
9
+	"git.x2erp.com/qdy/go-base/types"
10
+	"git.x2erp.com/qdy/go-db/factory/rabbitmq"
11
+)
12
+
13
+// HealthCheck RabbitMQ健康检查
14
+func HealthCheck(w http.ResponseWriter, r *http.Request, rabbitFactory *rabbitmq.RabbitMQFactory) {
15
+	w.Header().Set("Content-Type", "application/json")
16
+
17
+	// 尝试创建测试通道
18
+	_, err := rabbitFactory.CreateChannel("health_check")
19
+	if err != nil {
20
+		w.WriteHeader(http.StatusServiceUnavailable)
21
+		json.NewEncoder(w).Encode(map[string]interface{}{
22
+			"status": "down",
23
+			"error":  err.Error(),
24
+			"time":   time.Now().Format(time.RFC3339),
25
+		})
26
+		return
27
+	}
28
+
29
+	// 清理测试通道
30
+	rabbitFactory.CloseChannel("health_check")
31
+
32
+	w.WriteHeader(http.StatusOK)
33
+	json.NewEncoder(w).Encode(map[string]interface{}{
34
+		"status": "up",
35
+		"time":   time.Now().Format(time.RFC3339),
36
+	})
37
+}
38
+
39
+// GetQueueInfo 获取队列信息
40
+func GetQueueInfo(rabbitFactory *rabbitmq.RabbitMQFactory, req types.QueueInfoRequest, reqCtx *ctx.RequestContext) *types.QueryResult {
41
+	// 设置默认值
42
+	if req.ChannelName == "" {
43
+		req.ChannelName = "default"
44
+	}
45
+
46
+	// 获取通道
47
+	channel, err := rabbitFactory.GetChannel(req.ChannelName)
48
+	if err != nil {
49
+		return &types.QueryResult{
50
+			Success: false,
51
+			Error:   "Failed to get channel: " + err.Error(),
52
+			Time:    time.Now().Format(time.RFC3339),
53
+		}
54
+	}
55
+
56
+	// 获取队列信息
57
+	myQueue, err := channel.QueueInspect(req.QueueName)
58
+	if err != nil {
59
+		return &types.QueryResult{
60
+			Success: false,
61
+			Error:   "Failed to inspect queue: " + err.Error(),
62
+			Time:    time.Now().Format(time.RFC3339),
63
+		}
64
+	}
65
+
66
+	return &types.QueryResult{
67
+		Success: true,
68
+		Time:    time.Now().Format(time.RFC3339),
69
+		Data: map[string]interface{}{
70
+			"Consumers": myQueue.Consumers,
71
+			"Messages":  myQueue.Messages,
72
+			"Name":      myQueue.Name,
73
+		},
74
+	}
75
+}

+ 25
- 0
gct.sh Wyświetl plik

@@ -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 "完成!"

+ 70
- 0
go.mod Wyświetl plik

@@ -0,0 +1,70 @@
1
+module git.x2erp.com/qdy/go-svc-mqconsumer
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
+	github.com/streadway/amqp v1.1.0
9
+	go-micro.dev/v4 v4.11.0
10
+)
11
+
12
+require (
13
+	dario.cat/mergo v1.0.2 // indirect
14
+	github.com/Microsoft/go-winio v0.6.2 // indirect
15
+	github.com/ProtonMail/go-crypto v1.3.0 // indirect
16
+	github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect
17
+	github.com/bitly/go-simplejson v0.5.1 // indirect
18
+	github.com/cloudflare/circl v1.6.1 // indirect
19
+	github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
20
+	github.com/cyphar/filepath-securejoin v0.6.1 // indirect
21
+	github.com/emirpasic/gods v1.18.1 // indirect
22
+	github.com/fatih/color v1.9.0 // indirect
23
+	github.com/fsnotify/fsnotify v1.9.0 // indirect
24
+	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
25
+	github.com/go-git/go-billy/v5 v5.6.2 // indirect
26
+	github.com/go-git/go-git/v5 v5.16.4 // indirect
27
+	github.com/go-micro/plugins/v4/registry/consul v1.2.1 // indirect
28
+	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
29
+	github.com/golang/protobuf v1.5.4 // indirect
30
+	github.com/google/uuid v1.6.0 // indirect
31
+	github.com/hashicorp/consul/api v1.9.0 // indirect
32
+	github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
33
+	github.com/hashicorp/go-hclog v0.12.0 // indirect
34
+	github.com/hashicorp/go-immutable-radix v1.0.0 // indirect
35
+	github.com/hashicorp/go-rootcerts v1.0.2 // indirect
36
+	github.com/hashicorp/golang-lru v0.5.3 // indirect
37
+	github.com/hashicorp/serf v0.9.5 // indirect
38
+	github.com/imdario/mergo v0.3.16 // indirect
39
+	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
40
+	github.com/kevinburke/ssh_config v1.4.0 // indirect
41
+	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
42
+	github.com/mattn/go-colorable v0.1.8 // indirect
43
+	github.com/mattn/go-isatty v0.0.20 // indirect
44
+	github.com/miekg/dns v1.1.68 // indirect
45
+	github.com/mitchellh/go-homedir v1.1.0 // indirect
46
+	github.com/mitchellh/hashstructure v1.1.0 // indirect
47
+	github.com/mitchellh/mapstructure v1.3.3 // indirect
48
+	github.com/nxadm/tail v1.4.11 // indirect
49
+	github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
50
+	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
51
+	github.com/pjbgf/sha1cd v0.5.0 // indirect
52
+	github.com/pkg/errors v0.9.1 // indirect
53
+	github.com/russross/blackfriday/v2 v2.1.0 // indirect
54
+	github.com/sergi/go-diff v1.4.0 // indirect
55
+	github.com/skeema/knownhosts v1.3.2 // indirect
56
+	github.com/urfave/cli/v2 v2.27.7 // indirect
57
+	github.com/xanzy/ssh-agent v0.3.3 // indirect
58
+	github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect
59
+	golang.org/x/crypto v0.45.0 // indirect
60
+	golang.org/x/mod v0.30.0 // indirect
61
+	golang.org/x/net v0.47.0 // indirect
62
+	golang.org/x/sync v0.18.0 // indirect
63
+	golang.org/x/sys v0.38.0 // indirect
64
+	golang.org/x/text v0.31.0 // indirect
65
+	golang.org/x/tools v0.39.0 // indirect
66
+	google.golang.org/protobuf v1.36.10 // indirect
67
+	gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
68
+	gopkg.in/warnings.v0 v0.1.2 // indirect
69
+	gopkg.in/yaml.v2 v2.4.0 // indirect
70
+)

+ 264
- 0
go.sum Wyświetl plik

@@ -0,0 +1,264 @@
1
+dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
2
+dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
3
+git.x2erp.com/qdy/go-base v0.1.70 h1:P5IOfSN6CnXcBYIrs+4BULYemPyzcgrFMkI7TcCri5A=
4
+git.x2erp.com/qdy/go-base v0.1.70/go.mod h1:IYkjIY0xiqPKIOBojsR9miER2ceyly6+iwv4Zd64OT0=
5
+git.x2erp.com/qdy/go-db v0.1.70 h1:5VfvFxCmQ0+9OFEaPrTODYJ99wam+4lPjz4Y024SUdc=
6
+git.x2erp.com/qdy/go-db v0.1.70/go.mod h1:oApVuDJTvrJcsxmBunoUDAdC9GSFHR0MWrSZCfm89G0=
7
+github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
8
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
9
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
10
+github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
11
+github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
12
+github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
13
+github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
14
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
15
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
16
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
17
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
18
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
19
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
20
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
21
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
22
+github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
23
+github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
24
+github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
25
+github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
26
+github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
27
+github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
28
+github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
29
+github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
30
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
31
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
32
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
33
+github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
34
+github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
35
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
36
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
37
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
38
+github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
39
+github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
40
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
41
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
42
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
43
+github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
44
+github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
45
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
46
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
47
+github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
48
+github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
49
+github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
50
+github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
51
+github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y=
52
+github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
53
+github.com/go-micro/plugins/v4/registry/consul v1.2.1 h1:3wctYMtstwQLCjoJ1HA6mKGGFF1hcdKDv5MzHakB1jE=
54
+github.com/go-micro/plugins/v4/registry/consul v1.2.1/go.mod h1:wTat7/K9XQ+i64VbbcMYFcEwipYfSgJM51HcA/sgsM4=
55
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
56
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
57
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
58
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
59
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
60
+github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
61
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
62
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
63
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
64
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
65
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
66
+github.com/hashicorp/consul/api v1.9.0 h1:T6dKIWcaihG2c21YUi0BMAHbJanVXiYuz+mPgqxY3N4=
67
+github.com/hashicorp/consul/api v1.9.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
68
+github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU=
69
+github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
70
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
71
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
72
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
73
+github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
74
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
75
+github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM=
76
+github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
77
+github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
78
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
79
+github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
80
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
81
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
82
+github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
83
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
84
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
85
+github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
86
+github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
87
+github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs=
88
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
89
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
90
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
91
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
92
+github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
93
+github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
94
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
95
+github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
96
+github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
97
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
98
+github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
99
+github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g=
100
+github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
101
+github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM=
102
+github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
103
+github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
104
+github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
105
+github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
106
+github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
107
+github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
108
+github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
109
+github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
110
+github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
111
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
112
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
113
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
114
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
115
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
116
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
117
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
118
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
119
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
120
+github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
121
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
122
+github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
123
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
124
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
125
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
126
+github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
127
+github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
128
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
129
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
130
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
131
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
132
+github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
133
+github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
134
+github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
135
+github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
136
+github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
137
+github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
138
+github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
139
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
140
+github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
141
+github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=
142
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
143
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
144
+github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
145
+github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
146
+github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
147
+github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
148
+github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
149
+github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
150
+github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
151
+github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
152
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
153
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
154
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
155
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
156
+github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
157
+github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
158
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
159
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
160
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
161
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
162
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
163
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
164
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
165
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
166
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
167
+github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
168
+github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
169
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
170
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
171
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
172
+github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
173
+github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
174
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
175
+github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
176
+github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
177
+github.com/streadway/amqp v1.1.0 h1:py12iX8XSyI7aN/3dUT8DFIDJazNJsVJdxNVEpnQTZM=
178
+github.com/streadway/amqp v1.1.0/go.mod h1:WYSrTEYHOXHd0nwFeUXAe2G2hRnQT+deZJJf88uS9Bg=
179
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
180
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
181
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
182
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
183
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
184
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
185
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
186
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
187
+github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
188
+github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
189
+github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
190
+github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
191
+github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
192
+github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
193
+go-micro.dev/v4 v4.11.0 h1:DZ2xcr0pnZJDlp6MJiCLhw4tXRxLw9xrJlPT91kubr0=
194
+go-micro.dev/v4 v4.11.0/go.mod h1:eE/tD53n3KbVrzrWxKLxdkGw45Fg1qaNLWjpJMvIUF4=
195
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
196
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
197
+golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
198
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
199
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
200
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
201
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
202
+golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
203
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
204
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
205
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
206
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
207
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
208
+golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
209
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
210
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
211
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
212
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
213
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
214
+golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
215
+golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
216
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
217
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
218
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
219
+golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
220
+golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
221
+golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
222
+golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
223
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
224
+golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
225
+golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
226
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
227
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
228
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
229
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
230
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
231
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
232
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
233
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
234
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
235
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
236
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
237
+golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
238
+golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
239
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
240
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
241
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
242
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
243
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
244
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
245
+golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
246
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
247
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
248
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
249
+google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
250
+google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
251
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
252
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
253
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
254
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
255
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
256
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
257
+gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
258
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
259
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
260
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
261
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
262
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
263
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
264
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 77
- 0
handlers/http_handler.go Wyświetl plik

@@ -0,0 +1,77 @@
1
+package handlers
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/json"
6
+	"fmt"
7
+	"net/http"
8
+	"time"
9
+
10
+	"git.x2erp.com/qdy/go-base/types"
11
+)
12
+
13
+// HTTPHandler HTTP处理器实现MessageHandler接口
14
+type HTTPHandler struct {
15
+	client *http.Client
16
+}
17
+
18
+// NewHTTPHandler 创建HTTP处理器
19
+func NewHTTPHandler() *HTTPHandler {
20
+	return &HTTPHandler{
21
+		client: &http.Client{
22
+			Timeout: 30 * time.Second,
23
+			Transport: &http.Transport{
24
+				MaxIdleConns:        100,
25
+				MaxIdleConnsPerHost: 100,
26
+				IdleConnTimeout:     90 * time.Second,
27
+			},
28
+		},
29
+	}
30
+}
31
+
32
+// Process 处理消息,发送HTTP POST请求
33
+func (h *HTTPHandler) Process(queueRequest *types.QueueRequest, body []byte) types.QueryResult {
34
+	if queueRequest.Url == "" {
35
+		return types.QueryResult{
36
+			Success: false,
37
+			Message: "URL is empty",
38
+		}
39
+	}
40
+
41
+	// 准备请求体
42
+	requestBody := map[string]interface{}{
43
+		"queue":     queueRequest.QueueName,
44
+		"data":      string(body),
45
+		"timestamp": time.Now().Format(time.RFC3339),
46
+	}
47
+
48
+	jsonBody, err := json.Marshal(requestBody)
49
+	if err != nil {
50
+		return types.QueryResult{
51
+			Success: false,
52
+			Message: fmt.Sprintf("Failed to marshal request: %v", err),
53
+		}
54
+	}
55
+
56
+	// 发送HTTP POST请求
57
+	resp, err := h.client.Post(queueRequest.Url, "application/json", bytes.NewBuffer(jsonBody))
58
+	if err != nil {
59
+		return types.QueryResult{
60
+			Success: false,
61
+			Message: fmt.Sprintf("HTTP request failed: %v", err),
62
+		}
63
+	}
64
+	defer resp.Body.Close()
65
+
66
+	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
67
+		return types.QueryResult{
68
+			Success: true,
69
+			Message: "Message processed successfully",
70
+		}
71
+	}
72
+
73
+	return types.QueryResult{
74
+		Success: false,
75
+		Message: fmt.Sprintf("HTTP error: %s", resp.Status),
76
+	}
77
+}

+ 136
- 0
main.go Wyświetl plik

@@ -0,0 +1,136 @@
1
+package main
2
+
3
+import (
4
+	"fmt"
5
+	"log"
6
+	"net/http"
7
+	"os"
8
+	"os/signal"
9
+	"syscall"
10
+
11
+	"git.x2erp.com/qdy/go-base/config"
12
+	"git.x2erp.com/qdy/go-base/middleware"
13
+	"git.x2erp.com/qdy/go-base/myservice"
14
+	"git.x2erp.com/qdy/go-db/factory/rabbitmq"
15
+	"git.x2erp.com/qdy/go-db/myhandle"
16
+	"git.x2erp.com/qdy/go-svc-mqconsumer/functions"
17
+	"go-micro.dev/v4/web"
18
+)
19
+
20
+func main() {
21
+	cfg, err := config.GetConfig()
22
+	if err != nil {
23
+		log.Fatalf("Failed to create RabbitMQ factory: %v", err)
24
+		return
25
+	}
26
+
27
+	serviceConfig := cfg.GetService()
28
+
29
+	log.Printf("RabbitMQ Service Starting...")
30
+	log.Printf("Service Port: %d", serviceConfig.Port)
31
+	log.Printf("Service Name: %s", serviceConfig.ServiceName)
32
+
33
+	// 启动微服务
34
+	startRabbitMQService(cfg)
35
+}
36
+
37
+// 启动RabbitMQ微服务
38
+func startRabbitMQService(cfg config.IConfig) {
39
+
40
+	// 初始化RabbitMQ工厂
41
+	rabbitFactory, err := rabbitmq.NewRabbitMQFactory()
42
+	if err != nil {
43
+		log.Fatalf("Failed to create RabbitMQ factory: %v", err)
44
+	}
45
+	defer func() {
46
+		if err := rabbitFactory.Close(); err != nil {
47
+			log.Printf("RabbitMQ close error: %v", err)
48
+		}
49
+	}()
50
+
51
+	// 设置优雅关闭
52
+	setupGracefulShutdown(rabbitFactory)
53
+
54
+	// 创建默认通道
55
+	if err := createDefaultChannel(rabbitFactory); err != nil {
56
+		log.Fatalf("Failed to create default channel: %v", err)
57
+	}
58
+
59
+	webService := myservice.StartStandalone(cfg)
60
+
61
+	// 注册HTTP路由到webService
62
+	registerRabbitMQRoutes(webService, rabbitFactory)
63
+
64
+	// 等待服务运行
65
+	log.Printf("RabbitMQ Service started successfully")
66
+	log.Printf(" 	• Host: %s", cfg.GetRabbitMQ().Host)
67
+	log.Printf(" 	• Port: %d", cfg.GetRabbitMQ().Port)
68
+	log.Printf(" 	• Vhost: %s", cfg.GetRabbitMQ().Vhost)
69
+
70
+	// 保持主程序运行
71
+	select {}
72
+}
73
+
74
+// 创建默认通道
75
+func createDefaultChannel(rabbitFactory *rabbitmq.RabbitMQFactory) error {
76
+	_, err := rabbitFactory.CreateChannel("default")
77
+	if err != nil {
78
+		return fmt.Errorf("failed to create default channel: %v", err)
79
+	}
80
+	log.Println("Default RabbitMQ channel created successfully")
81
+	return nil
82
+}
83
+
84
+// 注册RabbitMQ相关路由
85
+func registerRabbitMQRoutes(webService web.Service, rabbitFactory *rabbitmq.RabbitMQFactory) {
86
+	// 创建交换机
87
+	webService.Handle("/api/rabbitmq/consumers/stop", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
88
+		myhandle.QueryHandlerJson(w, r, rabbitFactory, functions.CreateExchange)
89
+	})))
90
+
91
+	// 创建队列
92
+	webService.Handle("/api/rabbitmq/consumers/start", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
93
+		myhandle.QueryHandlerJson(w, r, rabbitFactory, functions.CreateQueue)
94
+	})))
95
+
96
+	// 绑定队列到交换机
97
+	webService.Handle("/api/rabbitmq/queue/bind", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
98
+		myhandle.QueryHandlerJson(w, r, rabbitFactory, functions.BindQueue)
99
+	})))
100
+
101
+	// 发送JSON消息
102
+	webService.Handle("/api/rabbitmq/message/send", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
103
+		myhandle.QueryHandlerJson(w, r, rabbitFactory, functions.SendMessage)
104
+	})))
105
+
106
+	// 发送原始消息(字节流)
107
+	webService.Handle("/api/rabbitmq/message/send/bytes", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
108
+		myhandle.QueryHandlerJson(w, r, rabbitFactory, functions.SendBytesMessage)
109
+	})))
110
+
111
+	// 健康检查
112
+	webService.Handle("/api/rabbitmq/health", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
113
+		functions.HealthCheck(w, r, rabbitFactory)
114
+	}))
115
+
116
+	// 获取队列信息
117
+	webService.Handle("/api/rabbitmq/queue/info", middleware.JWTAuthMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
118
+		myhandle.QueryHandlerJson(w, r, rabbitFactory, functions.GetQueueInfo)
119
+	})))
120
+}
121
+
122
+// 设置优雅关闭
123
+func setupGracefulShutdown(rabbitFactory *rabbitmq.RabbitMQFactory) {
124
+	signalCh := make(chan os.Signal, 1)
125
+	signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
126
+
127
+	go func() {
128
+		<-signalCh
129
+		log.Println("\nReceived shutdown signal, closing RabbitMQ connections...")
130
+		if err := rabbitFactory.Close(); err != nil {
131
+			log.Printf("Error closing RabbitMQ: %v", err)
132
+		}
133
+		log.Println("RabbitMQ connections closed gracefully")
134
+		os.Exit(0)
135
+	}()
136
+}

+ 174
- 0
service/rabbitmq_service.go Wyświetl plik

@@ -0,0 +1,174 @@
1
+package service
2
+
3
+import (
4
+	"encoding/json"
5
+	"fmt"
6
+	"log"
7
+	"net/http"
8
+	"sync"
9
+	"time"
10
+
11
+	"git.x2erp.com/qdy/go-base/config"
12
+	"git.x2erp.com/qdy/go-base/types"
13
+	"git.x2erp.com/qdy/go-db/factory/rabbitmq"
14
+	"git.x2erp.com/qdy/go-svc-mqconsumer/consumer"
15
+)
16
+
17
+// RabbitMQService RabbitMQ服务
18
+type RabbitMQService struct {
19
+	rabbitFactory   *rabbitmq.RabbitMQFactory
20
+	consumerManager *consumer.ConsumerManager
21
+	mu              sync.RWMutex
22
+	initialized     bool
23
+}
24
+
25
+// NewRabbitMQService 创建RabbitMQ服务
26
+func NewRabbitMQService() *RabbitMQService {
27
+	return &RabbitMQService{}
28
+}
29
+
30
+// Init 初始化服务
31
+func (s *RabbitMQService) Init() error {
32
+	s.mu.Lock()
33
+	defer s.mu.Unlock()
34
+
35
+	if s.initialized {
36
+		return nil
37
+	}
38
+
39
+	// 初始化RabbitMQ工厂
40
+	factory, err := rabbitmq.NewRabbitMQFactory()
41
+	if err != nil {
42
+		return fmt.Errorf("failed to create RabbitMQ factory: %v", err)
43
+	}
44
+
45
+	s.rabbitFactory = factory
46
+	s.consumerManager = consumer.NewConsumerManager(factory)
47
+	s.initialized = true
48
+
49
+	return nil
50
+}
51
+
52
+// StartConsumerHandler 启动消费者API处理器
53
+func (s *RabbitMQService) StartConsumerHandler(w http.ResponseWriter, r *http.Request) {
54
+	if r.Method != http.MethodPost {
55
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
56
+		return
57
+	}
58
+
59
+	var queueReq types.QueueRequest
60
+	if err := json.NewDecoder(r.Body).Decode(&queueReq); err != nil {
61
+		http.Error(w, fmt.Sprintf("Invalid request body: %v", err), http.StatusBadRequest)
62
+		return
63
+	}
64
+
65
+	if queueReq.QueueName == "" {
66
+		http.Error(w, "Queue name is required", http.StatusBadRequest)
67
+		return
68
+	}
69
+
70
+	if err := s.consumerManager.StartConsumer(queueReq); err != nil {
71
+		http.Error(w, fmt.Sprintf("Failed to start consumer: %v", err), http.StatusInternalServerError)
72
+		return
73
+	}
74
+
75
+	response := map[string]interface{}{
76
+		"success": true,
77
+		"message": fmt.Sprintf("Consumer started for queue: %s", queueReq.QueueName),
78
+	}
79
+
80
+	w.Header().Set("Content-Type", "application/json")
81
+	json.NewEncoder(w).Encode(response)
82
+}
83
+
84
+// StopConsumerHandler 停止消费者API处理器
85
+func (s *RabbitMQService) StopConsumerHandler(w http.ResponseWriter, r *http.Request) {
86
+	if r.Method != http.MethodPost {
87
+		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
88
+		return
89
+	}
90
+
91
+	var request struct {
92
+		ConsumerID string `json:"consumer_id"`
93
+		QueueName  string `json:"queue_name"`
94
+	}
95
+
96
+	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
97
+		http.Error(w, fmt.Sprintf("Invalid request body: %v", err), http.StatusBadRequest)
98
+		return
99
+	}
100
+
101
+	// 优先使用ConsumerID,如果为空则使用QueueName生成
102
+	consumerID := request.ConsumerID
103
+	if consumerID == "" && request.QueueName != "" {
104
+		consumerID = fmt.Sprintf("%s_consumer", request.QueueName)
105
+	}
106
+
107
+	if consumerID == "" {
108
+		http.Error(w, "consumer_id or queue_name is required", http.StatusBadRequest)
109
+		return
110
+	}
111
+
112
+	if err := s.consumerManager.StopConsumer(consumerID); err != nil {
113
+		http.Error(w, fmt.Sprintf("Failed to stop consumer: %v", err), http.StatusInternalServerError)
114
+		return
115
+	}
116
+
117
+	response := map[string]interface{}{
118
+		"success": true,
119
+		"message": fmt.Sprintf("Consumer stopped: %s", consumerID),
120
+	}
121
+
122
+	w.Header().Set("Content-Type", "application/json")
123
+	json.NewEncoder(w).Encode(response)
124
+}
125
+
126
+// HealthCheckHandler 健康检查
127
+func (s *RabbitMQService) HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
128
+	status := "healthy"
129
+	if s.rabbitFactory == nil {
130
+		status = "unhealthy"
131
+	}
132
+
133
+	response := map[string]interface{}{
134
+		"status":    status,
135
+		"service":   config.GetService().ServiceName,
136
+		"consumers": s.consumerManager.GetConsumerCount(),
137
+		"timestamp": time.Now().Format(time.RFC3339),
138
+	}
139
+
140
+	w.Header().Set("Content-Type", "application/json")
141
+	json.NewEncoder(w).Encode(response)
142
+}
143
+
144
+// StopAllConsumers 停止所有消费者
145
+func (s *RabbitMQService) StopAllConsumers() error {
146
+	if s.consumerManager != nil {
147
+		return s.consumerManager.StopAllConsumers()
148
+	}
149
+	return nil
150
+}
151
+
152
+// Close 关闭服务
153
+func (s *RabbitMQService) Close() error {
154
+	s.mu.Lock()
155
+	defer s.mu.Unlock()
156
+
157
+	// 停止所有消费者
158
+	if s.consumerManager != nil {
159
+		if err := s.consumerManager.StopAllConsumers(); err != nil {
160
+			log.Printf("Warning: failed to stop consumers: %v", err)
161
+		}
162
+	}
163
+
164
+	// 关闭RabbitMQ连接
165
+	if s.rabbitFactory != nil {
166
+		if err := s.rabbitFactory.Close(); err != nil {
167
+			return fmt.Errorf("failed to close RabbitMQ factory: %v", err)
168
+		}
169
+	}
170
+
171
+	s.initialized = false
172
+	log.Println("RabbitMQ service closed successfully")
173
+	return nil
174
+}

+ 141
- 0
test/my_create_exchange_test.go Wyświetl plik

@@ -0,0 +1,141 @@
1
+package test
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/json"
6
+	"fmt"
7
+	"io"
8
+	"net/http"
9
+	"testing"
10
+	"time"
11
+
12
+	"git.x2erp.com/qdy/go-base/types"
13
+)
14
+
15
+// type ExchangeRequest struct {
16
+// 	ChannelName  string            `json:"channel_name"`
17
+// 	ExchangeName string            `json:"exchange_name"`
18
+// 	ExchangeType string            `json:"exchange_type"`
19
+// 	Durable      bool              `json:"durable"`
20
+// 	Internal     bool              `json:"internal"`
21
+// 	AutoDelete   bool              `json:"auto_delete"`
22
+// 	NoWait       bool              `json:"no_wait"`
23
+// 	Arguments    map[string]string `json:"arguments"`
24
+// }
25
+
26
+func TestCreateexchange(t *testing.T) {
27
+	// 1. 准备请求数据
28
+	requestData := types.ExchangeRequest{
29
+		ChannelName:  "v_bdx_channel",
30
+		ExchangeName: "v_bdx_exchange",
31
+		ExchangeType: "topic", // direct, fanout, topic, headers
32
+		Durable:      true,
33
+		Internal:     false,
34
+		AutoDelete:   false,
35
+		NoWait:       false,
36
+		Arguments: map[string]string{
37
+			"x-delayed-type": "topic",
38
+		},
39
+	}
40
+
41
+	// 消息队列
42
+	queueRequest := types.QueueRequest{
43
+		QueueName:   "vbdx_queue_count",
44
+		ChannelName: requestData.ChannelName,
45
+	}
46
+
47
+	queueBindRequest := types.QueueBindRequest{
48
+		ChannelName:  requestData.ChannelName,
49
+		QueueName:    queueRequest.QueueName,
50
+		ExchangeName: requestData.ExchangeName,
51
+		RoutingKey:   "v-bdx.#",
52
+	}
53
+
54
+	// 2. 将数据转换为JSON
55
+	jsonData, err := json.Marshal(requestData)
56
+	if err != nil {
57
+		fmt.Printf("JSON编码失败: %v\n", err)
58
+		return
59
+	}
60
+
61
+	// 3. 发送HTTP POST请求
62
+	url := "http://localhost:9090/api/rabbitmq/exchange/create"
63
+
64
+	POST(jsonData, url)
65
+
66
+	//建立队列
67
+	CreateQueue(queueRequest)
68
+
69
+	//绑定
70
+	CreateQueueBind(queueBindRequest)
71
+}
72
+
73
+func CreateQueue(queueRequest types.QueueRequest) {
74
+
75
+	// 2. 将数据转换为JSON
76
+	jsonData, err := json.Marshal(queueRequest)
77
+	if err != nil {
78
+		fmt.Printf("JSON编码失败: %v\n", err)
79
+		return
80
+	}
81
+	url := "http://localhost:9090/api/rabbitmq/queue/create"
82
+
83
+	POST(jsonData, url)
84
+
85
+}
86
+
87
+func CreateQueueBind(queueBindRequest types.QueueBindRequest) {
88
+
89
+	// 2. 将数据转换为JSON
90
+	jsonData, err := json.Marshal(queueBindRequest)
91
+	if err != nil {
92
+		fmt.Printf("JSON编码失败: %v\n", err)
93
+		return
94
+	}
95
+	url := "http://localhost:9090/api/rabbitmq/queue/bind"
96
+
97
+	POST(jsonData, url)
98
+
99
+}
100
+
101
+func POST(jsonData []byte, url string) {
102
+
103
+	// 如果服务端需要认证,添加JWT token
104
+	token := "123" // 替换为实际的JWT token
105
+
106
+	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
107
+	if err != nil {
108
+		fmt.Printf("创建请求失败: %v\n", err)
109
+		return
110
+	}
111
+
112
+	// 添加请求头
113
+	req.Header.Set("Content-Type", "application/json")
114
+	if token != "" {
115
+		req.Header.Set("Authorization", "Bearer "+token)
116
+	}
117
+
118
+	// 4. 发送请求并获取响应
119
+	client := &http.Client{
120
+		Timeout: 10 * time.Second,
121
+	}
122
+
123
+	resp, err := client.Do(req)
124
+	if err != nil {
125
+		fmt.Printf("请求失败: %v\n", err)
126
+		return
127
+	}
128
+	defer resp.Body.Close()
129
+
130
+	// 5. 读取响应
131
+	body, err := io.ReadAll(resp.Body)
132
+	if err != nil {
133
+		fmt.Printf("读取响应失败: %v\n", err)
134
+		return
135
+	}
136
+
137
+	// 6. 输出结果
138
+	fmt.Printf("状态码: %d\n", resp.StatusCode)
139
+	fmt.Printf("响应头: %v\n", resp.Header)
140
+	fmt.Printf("响应体: %s\n", string(body))
141
+}

+ 98
- 0
test/my_send_message_test.go Wyświetl plik

@@ -0,0 +1,98 @@
1
+package test
2
+
3
+import (
4
+	"bytes"
5
+	"encoding/json"
6
+	"fmt"
7
+	"io"
8
+	"net/http"
9
+	"testing"
10
+	"time"
11
+
12
+	"git.x2erp.com/qdy/go-base/types"
13
+)
14
+
15
+func TestSendMessage(t *testing.T) {
16
+
17
+	queryRequest := getQueyRequest()
18
+
19
+	messageRequest := types.MessageRequest{
20
+		ChannelName:  "v_bdx_channel",
21
+		ExchangeName: "v_bdx_exchange",
22
+		RoutingKey:   "v-bdx.count",
23
+		Message:      queryRequest,
24
+		ContentType:  "json",
25
+	}
26
+
27
+	// 2. 将数据转换为JSON
28
+	jsonData, err := json.Marshal(messageRequest)
29
+	if err != nil {
30
+		fmt.Printf("JSON编码失败: %v\n", err)
31
+		return
32
+	}
33
+
34
+	// 3. 发送HTTP POST请求
35
+	url := "http://localhost:9090/api/rabbitmq/message/send"
36
+
37
+	POSTMessage(jsonData, url)
38
+
39
+}
40
+
41
+func getQueyRequest() types.QueryRequest {
42
+	sql := `SELECT * FROM (SELECT a.*, ROWNUM rn FROM (SELECT CLOTHING_ID, CLOTHING_NAME FROM X6_STOCK_DEV.A3_CLOTHING ORDER BY CLOTHING_ID) a WHERE ROWNUM <= :1) WHERE rn > :2
43
+	`
44
+
45
+	// // 3个参数
46
+	params := []interface{}{
47
+		10,
48
+		0,
49
+	}
50
+
51
+	return types.QueryRequest{
52
+		SQL:              sql,
53
+		PositionalParams: params,
54
+		WriterHeader:     false,
55
+	}
56
+}
57
+
58
+func POSTMessage(jsonData []byte, url string) {
59
+
60
+	// 如果服务端需要认证,添加JWT token
61
+	token := "123" // 替换为实际的JWT token
62
+
63
+	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
64
+	if err != nil {
65
+		fmt.Printf("创建请求失败: %v\n", err)
66
+		return
67
+	}
68
+
69
+	// 添加请求头
70
+	req.Header.Set("Content-Type", "application/json")
71
+	if token != "" {
72
+		req.Header.Set("Authorization", "Bearer "+token)
73
+	}
74
+
75
+	// 4. 发送请求并获取响应
76
+	client := &http.Client{
77
+		Timeout: 10 * time.Second,
78
+	}
79
+
80
+	resp, err := client.Do(req)
81
+	if err != nil {
82
+		fmt.Printf("请求失败: %v\n", err)
83
+		return
84
+	}
85
+	defer resp.Body.Close()
86
+
87
+	// 5. 读取响应
88
+	body, err := io.ReadAll(resp.Body)
89
+	if err != nil {
90
+		fmt.Printf("读取响应失败: %v\n", err)
91
+		return
92
+	}
93
+
94
+	// 6. 输出结果
95
+	fmt.Printf("状态码: %d\n", resp.StatusCode)
96
+	fmt.Printf("响应头: %v\n", resp.Header)
97
+	fmt.Printf("响应体: %s\n", string(body))
98
+}

+ 62
- 0
test/my_tag_test.go Wyświetl plik

@@ -0,0 +1,62 @@
1
+package test
2
+
3
+import (
4
+	"fmt"
5
+	"reflect"
6
+	"testing"
7
+)
8
+
9
+type DatabaseConfig struct {
10
+	Host     string `config:"host" type:"string" required:"true" desc:"数据库主机"`
11
+	Port     int    `config:"port" type:"int" required:"true" desc:"数据库端口"`
12
+	Database string `config:"database" type:"string" required:"true" desc:"数据库名称"`
13
+	PoolSize int    `config:"pool_size" type:"int" desc:"连接池大小"`
14
+}
15
+
16
+func ReadTags(config interface{}) map[string]map[string]string {
17
+	result := make(map[string]map[string]string)
18
+
19
+	t := reflect.TypeOf(config)
20
+	// 如果是指针,获取指向的类型
21
+	if t.Kind() == reflect.Ptr {
22
+		t = t.Elem()
23
+	}
24
+
25
+	for i := 0; i < t.NumField(); i++ {
26
+		field := t.Field(i)
27
+
28
+		// 读取config标签
29
+		configTag := field.Tag.Get("config")
30
+		if configTag == "" {
31
+			configTag = field.Name // 如果没有config标签,使用字段名
32
+		}
33
+
34
+		// 收集所有标签
35
+		tags := make(map[string]string)
36
+		tags["field_name"] = field.Name
37
+		tags["go_type"] = field.Type.String()
38
+		tags["config_key"] = configTag
39
+		tags["type"] = field.Tag.Get("type")
40
+		tags["required"] = field.Tag.Get("required")
41
+		tags["desc"] = field.Tag.Get("desc")
42
+		tags["default"] = field.Tag.Get("default")
43
+
44
+		result[field.Name] = tags
45
+	}
46
+
47
+	return result
48
+}
49
+
50
+func TestMain(t *testing.T) {
51
+	config := DatabaseConfig{}
52
+	tags := ReadTags(config)
53
+
54
+	for fieldName, tagMap := range tags {
55
+		fmt.Printf("字段: %s\n", fieldName)
56
+		fmt.Printf("  config标签: %s\n", tagMap["config_key"])
57
+		fmt.Printf("  类型: %s\n", tagMap["type"])
58
+		fmt.Printf("  必填: %s\n", tagMap["required"])
59
+		fmt.Printf("  描述: %s\n", tagMap["desc"])
60
+		fmt.Println()
61
+	}
62
+}

Ładowanie…
Anuluj
Zapisz