浏览代码

增加http参数配置

qdy 3 个月前
父节点
当前提交
1c249dc329
共有 5 个文件被更改,包括 376 次插入85 次删除
  1. 1
    0
      factory/db_factory.go
  2. 84
    59
      factory/doris_factory.go
  3. 276
    23
      factory/http_factory.go
  4. 5
    3
      go.mod
  5. 10
    0
      go.sum

+ 1
- 0
factory/db_factory.go 查看文件

@@ -107,6 +107,7 @@ func (f *DBFactory) Close() error {
107 107
 	if f.db != nil {
108 108
 		err := f.db.Close()
109 109
 		f.db = nil
110
+		fmt.Println("Database connection closed gracefully")
110 111
 		return err
111 112
 	}
112 113
 	return nil

+ 84
- 59
factory/doris_factory.go 查看文件

@@ -3,89 +3,94 @@ package factory
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"fmt"
6
-	"io"
7
-	"net/http"
8
-	"strings"
6
+	"sync"
9 7
 	"time"
10 8
 
11 9
 	"git.x2erp.com/qdy/go-base/config"
10
+	"github.com/go-resty/resty/v2"
12 11
 )
13 12
 
14 13
 // DorisFactory Doris HTTP客户端工厂
15 14
 type DorisFactory struct {
16
-	config     config.IConfig
17
-	httpClient *http.Client
15
+	client   *resty.Client // 线程安全
16
+	FEHost   string        // 只读
17
+	FEPort   int           // 只读
18
+	Username string        // 只读
19
+	Password string        // 只读
20
+	Timeout  time.Duration // 只读
21
+
22
+	isClosed bool         // 需要原子操作或锁保护
23
+	closedMu sync.RWMutex // 只保护 isClosed 字段
18 24
 }
19 25
 
20
-// NewDorisFactory 创建Doris HTTP客户端工厂
21
-func NewDorisFactory(httpFactory *HTTPFactory) (*DorisFactory, error) {
22
-	cfg := config.GetConfig()
23
-
24
-	if err := config.GetInitError(); err != nil {
25
-		return nil, fmt.Errorf("failed to load config: %v", err)
26
-	}
26
+var (
27
+	instance     *DorisFactory
28
+	instanceOnce sync.Once
29
+)
27 30
 
28
-	if !cfg.IsDorisConfigured() {
29
-		return nil, fmt.Errorf("doris configuration is incomplete")
31
+// GetDorisFactory 获取Doris工厂单例
32
+func GetDorisFactory(httpFactory *HTTPFactory) (*DorisFactory, error) {
33
+	var initErr error
34
+	instanceOnce.Do(func() {
35
+		cfg := config.GetConfig()
36
+
37
+		if err := config.GetInitError(); err != nil {
38
+			initErr = fmt.Errorf("failed to load config: %v", err)
39
+			return
40
+		}
41
+
42
+		if !cfg.IsDorisConfigured() {
43
+			initErr = fmt.Errorf("doris configuration is incomplete")
44
+			return
45
+		}
46
+
47
+		dorisConfig := cfg.GetDoris()
48
+		instance = &DorisFactory{
49
+			client:   httpFactory.CreateClient().client,
50
+			FEHost:   dorisConfig.FEHost,
51
+			FEPort:   dorisConfig.FEPort,
52
+			Username: dorisConfig.FEUsername,
53
+			Password: dorisConfig.FEPassword,
54
+			Timeout:  time.Duration(dorisConfig.StreamLoadTimeout) * time.Second,
55
+			isClosed: false,
56
+		}
57
+	})
58
+
59
+	if initErr != nil {
60
+		return nil, initErr
30 61
 	}
31 62
 
32
-	return &DorisFactory{
33
-		config:     cfg,
34
-		httpClient: httpFactory.CreateHTTPClient(),
35
-	}, nil
63
+	return instance, nil
36 64
 }
37 65
 
38
-// CreateDorisClient 创建Doris HTTP客户端
39
-func (f *DorisFactory) CreateDorisClient() *DorisClient {
40
-	dorisConfig := f.config.GetDoris()
41
-
42
-	return &DorisClient{
43
-		httpClient: f.httpClient,
44
-		FEHost:     dorisConfig.FEHost,
45
-		FEPort:     dorisConfig.FEPort,
46
-		Username:   dorisConfig.FEUsername,
47
-		Password:   dorisConfig.FEPassword,
48
-		Timeout:    time.Duration(dorisConfig.StreamLoadTimeout) * time.Second,
66
+// InsertCSV 插入CSV数据到Doris
67
+func (f *DorisFactory) InsertCSV(database, table, csvData string, skipHeader bool) error {
68
+	// 检查关闭状态
69
+	if f.IsClosed() {
70
+		return fmt.Errorf("doris client is closed")
49 71
 	}
50
-}
51 72
 
52
-// DorisClient Doris HTTP客户端
53
-type DorisClient struct {
54
-	httpClient *http.Client
55
-	FEHost     string
56
-	FEPort     int
57
-	Username   string
58
-	Password   string
59
-	Timeout    time.Duration
60
-}
61
-
62
-// InsertCSV 插入CSV数据到Doris 只认数据次序
63
-func (c *DorisClient) DorisInsertCSV(database, table, csvData string, skipHeader bool) error {
64
-	url := fmt.Sprintf("http://%s:%d/api/%s/%s/_stream_load", c.FEHost, c.FEPort, database, table)
65
-
66
-	req, err := http.NewRequest("PUT", url, strings.NewReader(csvData))
67
-	if err != nil {
68
-		return fmt.Errorf("创建请求失败: %v", err)
69
-	}
73
+	url := fmt.Sprintf("http://%s:%d/api/%s/%s/_stream_load", f.FEHost, f.FEPort, database, table)
70 74
 
71
-	req.SetBasicAuth(c.Username, c.Password)
72
-	req.Header.Set("Content-Type", "text/plain")
73
-	req.Header.Set("format", "csv")
74
-	req.Header.Set("column_separator", ",")
75
+	// 使用 resty 的 API
76
+	resp, err := f.client.R().
77
+		SetBasicAuth(f.Username, f.Password).
78
+		SetHeader("Content-Type", "text/plain").
79
+		SetHeader("format", "csv").
80
+		SetHeader("column_separator", ",").
81
+		SetBody(csvData).
82
+		Put(url)
75 83
 
76 84
 	if skipHeader {
77
-		req.Header.Set("skip_header", "1")
85
+		resp.Request.SetHeader("skip_header", "1")
78 86
 	}
79 87
 
80
-	resp, err := c.httpClient.Do(req)
81 88
 	if err != nil {
82 89
 		return fmt.Errorf("请求失败: %v", err)
83 90
 	}
84
-	defer resp.Body.Close()
85 91
 
86
-	body, _ := io.ReadAll(resp.Body)
87
-	if resp.StatusCode != 200 {
88
-		return fmt.Errorf("插入失败: %s", string(body))
92
+	if resp.StatusCode() != 200 {
93
+		return fmt.Errorf("插入失败: %s", string(resp.Body()))
89 94
 	}
90 95
 
91 96
 	// 解析Stream Load响应
@@ -93,7 +98,7 @@ func (c *DorisClient) DorisInsertCSV(database, table, csvData string, skipHeader
93 98
 		Status  string `json:"Status"`
94 99
 		Message string `json:"Message"`
95 100
 	}
96
-	if err := json.Unmarshal(body, &result); err != nil {
101
+	if err := json.Unmarshal(resp.Body(), &result); err != nil {
97 102
 		return fmt.Errorf("解析响应失败: %v", err)
98 103
 	}
99 104
 
@@ -103,3 +108,23 @@ func (c *DorisClient) DorisInsertCSV(database, table, csvData string, skipHeader
103 108
 
104 109
 	return nil
105 110
 }
111
+
112
+// Close 关闭Doris客户端
113
+func (f *DorisFactory) Close() error {
114
+	f.closedMu.Lock()
115
+	defer f.closedMu.Unlock()
116
+
117
+	if f.isClosed {
118
+		return nil
119
+	}
120
+
121
+	f.isClosed = true
122
+	return nil
123
+}
124
+
125
+// IsClosed 检查客户端是否已关闭
126
+func (f *DorisFactory) IsClosed() bool {
127
+	f.closedMu.RLock()
128
+	defer f.closedMu.RUnlock()
129
+	return f.isClosed
130
+}

+ 276
- 23
factory/http_factory.go 查看文件

@@ -1,48 +1,301 @@
1 1
 package factory
2 2
 
3 3
 import (
4
+	"encoding/base64"
4 5
 	"fmt"
5 6
 	"net/http"
7
+	"sync"
6 8
 	"time"
7 9
 
8 10
 	"git.x2erp.com/qdy/go-base/config"
11
+	"github.com/go-resty/resty/v2"
9 12
 )
10 13
 
14
+// HTTPClient 简化的HTTP客户端
15
+type HTTPClient struct {
16
+	client *resty.Client
17
+}
18
+
11 19
 // HTTPFactory HTTP客户端工厂
12 20
 type HTTPFactory struct {
13 21
 	config config.IConfig
14
-	client *http.Client
22
+	client *resty.Client
23
+}
24
+
25
+var (
26
+	httpFactoryInstance *HTTPFactory
27
+	httpFactoryOnce     sync.Once
28
+	httpFactoryInitErr  error
29
+)
30
+
31
+// GetHTTPFactory 获取HTTP工厂单例实例
32
+func GetHTTPFactory() (*HTTPFactory, error) {
33
+	httpFactoryOnce.Do(func() {
34
+		cfg := config.GetConfig()
35
+
36
+		if err := config.GetInitError(); err != nil {
37
+			httpFactoryInitErr = fmt.Errorf("failed to load config: %v", err)
38
+			return
39
+		}
40
+
41
+		httpConfig := cfg.GetHTTP()
42
+
43
+		// 创建共享的resty客户端(线程安全)
44
+		client := resty.New()
45
+		client.SetTimeout(time.Duration(httpConfig.Timeout) * time.Second)
46
+		client.SetTransport(&http.Transport{
47
+			MaxIdleConns:        httpConfig.MaxIdleConns,
48
+			MaxIdleConnsPerHost: httpConfig.MaxIdleConnsPerHost,
49
+			IdleConnTimeout:     time.Duration(httpConfig.IdleConnTimeout) * time.Second,
50
+			MaxConnsPerHost:     httpConfig.MaxConnsPerHost,
51
+			DisableCompression:  httpConfig.DisableCompression,
52
+			DisableKeepAlives:   httpConfig.DisableKeepAlives,
53
+		})
54
+
55
+		// 设置默认headers
56
+		client.SetHeaders(map[string]string{
57
+			"Content-Type": "application/json",
58
+			"Accept":       "application/json",
59
+		})
60
+
61
+		httpFactoryInstance = &HTTPFactory{
62
+			config: cfg,
63
+			client: client,
64
+		}
65
+	})
66
+
67
+	return httpFactoryInstance, httpFactoryInitErr
68
+}
69
+
70
+// CreateClient 创建HTTP客户端包装器(共享底层resty.Client)
71
+func (f *HTTPFactory) CreateClient() *HTTPClient {
72
+	return &HTTPClient{
73
+		client: f.client, // 共享同一个线程安全的客户端
74
+	}
75
+}
76
+
77
+// CreateClient 全局便捷函数:创建HTTP客户端
78
+func CreateClient() (*HTTPClient, error) {
79
+	factory, err := GetHTTPFactory()
80
+	if err != nil {
81
+		return nil, err
82
+	}
83
+	return factory.CreateClient(), nil
84
+}
85
+
86
+// GetInitError 获取初始化错误(如果有)
87
+func GetInitError() error {
88
+	return httpFactoryInitErr
89
+}
90
+
91
+// ==================== HTTPClient 方法 ====================
92
+
93
+// SetAuthToken 设置认证token(请求级别)
94
+func (c *HTTPClient) SetAuthToken(token string) *HTTPClient {
95
+	// 注意:这里设置的是客户端级别的token,会影响所有使用该客户端的请求
96
+	// 对于多用户场景,应该在请求级别设置
97
+	c.client.SetAuthToken(token)
98
+	return c
15 99
 }
16 100
 
17
-// NewHTTPFactory 创建HTTP客户端工厂
18
-func NewHTTPFactory() (*HTTPFactory, error) {
19
-	cfg := config.GetConfig()
101
+// SetBearerToken 设置Bearer Token认证头(请求级别)
102
+func (c *HTTPClient) SetBearerToken(token string) *HTTPClient {
103
+	if token != "" {
104
+		c.client.SetHeader("Authorization", "Bearer "+token)
105
+	} else {
106
+		c.client.Header.Del("Authorization")
107
+	}
108
+	return c
109
+}
20 110
 
21
-	if err := config.GetInitError(); err != nil {
22
-		return nil, fmt.Errorf("failed to load config: %v", err)
111
+// SetBasicAuth 设置Basic认证头(请求级别)
112
+func (c *HTTPClient) SetBasicAuth(username, password string) *HTTPClient {
113
+	if username != "" && password != "" {
114
+		auth := username + ":" + password
115
+		token := base64.StdEncoding.EncodeToString([]byte(auth))
116
+		c.client.SetHeader("Authorization", "Basic "+token)
117
+	} else {
118
+		c.client.Header.Del("Authorization")
23 119
 	}
120
+	return c
121
+}
122
+
123
+// SetJSONContentType 设置Content-Type为application/json
124
+func (c *HTTPClient) SetJSONContentType() *HTTPClient {
125
+	c.client.SetHeader("Content-Type", "application/json")
126
+	return c
127
+}
128
+
129
+// SetHeader 设置请求头
130
+func (c *HTTPClient) SetHeader(key, value string) *HTTPClient {
131
+	c.client.SetHeader(key, value)
132
+	return c
133
+}
134
+
135
+// SetHeaders 批量设置请求头
136
+func (c *HTTPClient) SetHeaders(headers map[string]string) *HTTPClient {
137
+	c.client.SetHeaders(headers)
138
+	return c
139
+}
24 140
 
25
-	client := &http.Client{
26
-		Timeout: 30 * time.Second,
27
-		Transport: &http.Transport{
28
-			MaxIdleConns:        100,
29
-			MaxIdleConnsPerHost: 10,
30
-			IdleConnTimeout:     90 * time.Second,
31
-		},
141
+// SetAuthHeaders 设置认证相关的headers
142
+func (c *HTTPClient) SetAuthHeaders(token string) *HTTPClient {
143
+	return c.SetJSONContentType().SetBearerToken(token)
144
+}
145
+
146
+// SetBasicAuthHeaders 设置Basic认证相关的headers
147
+func (c *HTTPClient) SetBasicAuthHeaders(username, password string) *HTTPClient {
148
+	return c.SetJSONContentType().SetBasicAuth(username, password)
149
+}
150
+
151
+// Post 发送POST请求(在请求级别设置认证信息,避免多用户相互影响)
152
+func (c *HTTPClient) Post(url string, data interface{}) (*resty.Response, error) {
153
+	return c.client.R().SetBody(data).Post(url)
154
+}
155
+
156
+// PostWithResult 发送POST请求并解析结果
157
+func (c *HTTPClient) PostWithResult(url string, data interface{}, result interface{}) (*resty.Response, error) {
158
+	return c.client.R().SetBody(data).SetResult(result).Post(url)
159
+}
160
+
161
+// PostWithAuth 发送带Bearer Token的POST请求(推荐用于多用户场景)
162
+func (c *HTTPClient) PostWithAuth(url string, data interface{}, token string, result interface{}) (*resty.Response, error) {
163
+	req := c.client.R().SetBody(data)
164
+	if token != "" {
165
+		req.SetHeader("Authorization", "Bearer "+token)
32 166
 	}
167
+	if result != nil {
168
+		req.SetResult(result)
169
+	}
170
+	return req.Post(url)
171
+}
33 172
 
34
-	return &HTTPFactory{
35
-		config: cfg,
36
-		client: client,
37
-	}, nil
173
+// Get 发送GET请求
174
+func (c *HTTPClient) Get(url string) (*resty.Response, error) {
175
+	return c.client.R().Get(url)
38 176
 }
39 177
 
40
-// CreateHTTPClient 创建HTTP客户端
41
-func (f *HTTPFactory) CreateHTTPClient() *http.Client {
42
-	return f.client
178
+// GetWithResult 发送GET请求并解析结果
179
+func (c *HTTPClient) GetWithResult(url string, result interface{}) (*resty.Response, error) {
180
+	return c.client.R().SetResult(result).Get(url)
43 181
 }
44 182
 
45
-// GetConfig 获取配置信息
46
-func (f *HTTPFactory) GetConfig() config.IConfig {
47
-	return f.config
183
+// GetWithAuth 发送带Bearer Token的GET请求(推荐用于多用户场景)
184
+func (c *HTTPClient) GetWithAuth(url string, token string, result interface{}) (*resty.Response, error) {
185
+	req := c.client.R()
186
+	if token != "" {
187
+		req.SetHeader("Authorization", "Bearer "+token)
188
+	}
189
+	if result != nil {
190
+		req.SetResult(result)
191
+	}
192
+	return req.Get(url)
193
+}
194
+
195
+// Put 发送PUT请求
196
+func (c *HTTPClient) Put(url string, data interface{}) (*resty.Response, error) {
197
+	return c.client.R().SetBody(data).Put(url)
198
+}
199
+
200
+// PutWithResult 发送PUT请求并解析结果
201
+func (c *HTTPClient) PutWithResult(url string, data interface{}, result interface{}) (*resty.Response, error) {
202
+	return c.client.R().SetBody(data).SetResult(result).Put(url)
203
+}
204
+
205
+// Delete 发送DELETE请求
206
+func (c *HTTPClient) Delete(url string) (*resty.Response, error) {
207
+	return c.client.R().Delete(url)
208
+}
209
+
210
+// DeleteWithResult 发送DELETE请求并解析结果
211
+func (c *HTTPClient) DeleteWithResult(url string, result interface{}) (*resty.Response, error) {
212
+	return c.client.R().SetResult(result).Delete(url)
213
+}
214
+
215
+// ==================== 便捷函数(修正版) ====================
216
+
217
+// PostJSON 便捷函数:直接发送POST请求
218
+func PostJSON(url string, data interface{}, result interface{}) (*resty.Response, error) {
219
+	client, err := CreateClient()
220
+	if err != nil {
221
+		return nil, err
222
+	}
223
+
224
+	if result != nil {
225
+		return client.PostWithResult(url, data, result)
226
+	}
227
+	return client.Post(url, data)
228
+}
229
+
230
+// PostJSONWithBearerToken 便捷函数:使用Bearer Token发送POST请求
231
+func PostJSONWithBearerToken(token, url string, data interface{}, result interface{}) (*resty.Response, error) {
232
+	client, err := CreateClient()
233
+	if err != nil {
234
+		return nil, err
235
+	}
236
+
237
+	return client.PostWithAuth(url, data, token, result)
238
+}
239
+
240
+// PostJSONWithBasicAuth 便捷函数:使用Basic认证发送POST请求
241
+func PostJSONWithBasicAuth(username, password, url string, data interface{}, result interface{}) (*resty.Response, error) {
242
+	client, err := CreateClient()
243
+	if err != nil {
244
+		return nil, err
245
+	}
246
+
247
+	// 为这个特定请求设置Basic Auth
248
+	req := client.client.R().SetBody(data)
249
+	if username != "" && password != "" {
250
+		auth := username + ":" + password
251
+		token := base64.StdEncoding.EncodeToString([]byte(auth))
252
+		req.SetHeader("Authorization", "Basic "+token)
253
+	}
254
+	if result != nil {
255
+		req.SetResult(result)
256
+	}
257
+	return req.Post(url)
258
+}
259
+
260
+// GetJSON 便捷函数:直接发送GET请求
261
+func GetJSON(url string, result interface{}) (*resty.Response, error) {
262
+	client, err := CreateClient()
263
+	if err != nil {
264
+		return nil, err
265
+	}
266
+
267
+	if result != nil {
268
+		return client.GetWithResult(url, result)
269
+	}
270
+	return client.Get(url)
271
+}
272
+
273
+// GetJSONWithBearerToken 便捷函数:使用Bearer Token发送GET请求
274
+func GetJSONWithBearerToken(token, url string, result interface{}) (*resty.Response, error) {
275
+	client, err := CreateClient()
276
+	if err != nil {
277
+		return nil, err
278
+	}
279
+
280
+	return client.GetWithAuth(url, token, result)
281
+}
282
+
283
+// GetJSONWithBasicAuth 便捷函数:使用Basic认证发送GET请求
284
+func GetJSONWithBasicAuth(username, password, url string, result interface{}) (*resty.Response, error) {
285
+	client, err := CreateClient()
286
+	if err != nil {
287
+		return nil, err
288
+	}
289
+
290
+	// 为这个特定请求设置Basic Auth
291
+	req := client.client.R()
292
+	if username != "" && password != "" {
293
+		auth := username + ":" + password
294
+		token := base64.StdEncoding.EncodeToString([]byte(auth))
295
+		req.SetHeader("Authorization", "Basic "+token)
296
+	}
297
+	if result != nil {
298
+		req.SetResult(result)
299
+	}
300
+	return req.Get(url)
48 301
 }

+ 5
- 3
go.mod 查看文件

@@ -13,17 +13,19 @@ require (
13 13
 require (
14 14
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
15 15
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
16
+	golang.org/x/net v0.43.0 // indirect
16 17
 	gopkg.in/yaml.v2 v2.4.0 // indirect
17 18
 )
18 19
 
19 20
 require (
20 21
 	filippo.io/edwards25519 v1.1.0 // indirect
21
-	git.x2erp.com/qdy/go-base v0.1.10
22
+	git.x2erp.com/qdy/go-base v0.1.11
22 23
 	github.com/go-redis/redis/v8 v8.11.5
24
+	github.com/go-resty/resty/v2 v2.17.0
23 25
 	github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
24 26
 	github.com/golang-sql/sqlexp v0.1.0 // indirect
25 27
 	github.com/google/uuid v1.6.0 // indirect
26 28
 	github.com/jmoiron/sqlx v1.4.0
27
-	golang.org/x/crypto v0.38.0 // indirect
28
-	golang.org/x/text v0.25.0 // indirect
29
+	golang.org/x/crypto v0.41.0 // indirect
30
+	golang.org/x/text v0.28.0 // indirect
29 31
 )

+ 10
- 0
go.sum 查看文件

@@ -4,6 +4,8 @@ git.x2erp.com/qdy/go-base v0.1.9 h1:SuyYSt3Gp7aXiUQRCBNwhrusJ53wlCnagiTYs5eITlY=
4 4
 git.x2erp.com/qdy/go-base v0.1.9/go.mod h1:Q+YLwpCoU8CVSnzATLdz2LAzVMlz/CEGzo8DePf7cug=
5 5
 git.x2erp.com/qdy/go-base v0.1.10 h1:qHvRNSBoQHZfYrXUpZ42r925OO7aT95tIZK/qh6Cd60=
6 6
 git.x2erp.com/qdy/go-base v0.1.10/go.mod h1:Q+YLwpCoU8CVSnzATLdz2LAzVMlz/CEGzo8DePf7cug=
7
+git.x2erp.com/qdy/go-base v0.1.11 h1:STHT6z+zaN8kMIiXfggUdPaP4vcz+kpXaxHkw5ziXzA=
8
+git.x2erp.com/qdy/go-base v0.1.11/go.mod h1:Q+YLwpCoU8CVSnzATLdz2LAzVMlz/CEGzo8DePf7cug=
7 9
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
8 10
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
9 11
 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
@@ -24,6 +26,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
24 26
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
25 27
 github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
26 28
 github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
29
+github.com/go-resty/resty/v2 v2.17.0 h1:pW9DeXcaL4Rrym4EZ8v7L19zZiIlWPg5YXAcVmt+gN0=
30
+github.com/go-resty/resty/v2 v2.17.0/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA=
27 31
 github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
28 32
 github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
29 33
 github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
@@ -54,12 +58,18 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
54 58
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
55 59
 golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
56 60
 golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
61
+golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
62
+golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
57 63
 golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
58 64
 golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
65
+golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
66
+golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
59 67
 golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
60 68
 golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
61 69
 golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
62 70
 golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
71
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
72
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
63 73
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
64 74
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
65 75
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

正在加载...
取消
保存