package htfutures import ( "context" "encoding/json" "errors" "eta/eta_bridge/global" "fmt" "io" "net/http" "net/url" "strings" "time" ) const ( get = "GET" post = "POST" ) type HttpClient[T any] interface { Get(url string, head http.Header, params map[string]interface{}) (Response[T], error) Post(url string, head http.Header, data interface{}) (Response[T], error) PostFromData(url string, fromData url.Values) (Response[T], error) doRequest(req *http.Request) (Response[T], error) } type Response[T any] struct { Code int `json:"code"` Message string `json:"message"` Data T `json:"data"` TraceId string `json:"traceId"` } // DefaultHttpClient 结构体实现 SSOClient 接口 type DefaultHttpClient[T any] struct { client *http.Client retryStrategy RetryStrategy } func (c *DefaultHttpClient[T]) PostFromData(urlStr string, fromData url.Values) (Response[T], error) { dataStr, err := json.Marshal(fromData) if err != nil { global.FILE_LOG.Error("formData参数json序列化失败,err:" + err.Error()) } global.FILE_LOG.Info("post请求:" + urlStr + ",formData:" + string(dataStr)) return c.doPostForm(urlStr, fromData) } func (c *DefaultHttpClient[T]) Post(urlStr string, head http.Header, data interface{}) (response Response[T], err error) { dataStr, err := json.Marshal(data) if err != nil { global.FILE_LOG.Error("请求data json序列化失败,err:" + err.Error()) } global.FILE_LOG.Info("post请求:" + urlStr + ",data:" + string(dataStr)) body := io.NopCloser(strings.NewReader(string(dataStr))) req, err := http.NewRequest(post, urlStr, body) if err != nil { global.FILE_LOG.Error("创建http求求失败:" + err.Error()) return } if head != nil { req.Header = head var headStr []byte headStr, err = json.Marshal(head) if err != nil { global.FILE_LOG.Error("请求头json序列化失败,err:" + err.Error()) } global.FILE_LOG.Info("自定义请求头:" + string(headStr)) } return c.doRequest(req) } func (c *DefaultHttpClient[T]) Get(urlStr string, head http.Header, params map[string]interface{}) (response Response[T], err error) { if len(params) > 0 { queryParams := url.Values{} for key, value := range params { queryParams.Add(key, fmt.Sprintf("%v", value)) } urlStr = urlStr + "?" + queryParams.Encode() global.FILE_LOG.Info("带参get请求:" + urlStr) } req, err := http.NewRequest(get, urlStr, nil) if err != nil { global.FILE_LOG.Error("创建http求求失败:" + err.Error()) return } if head != nil { req.Header = head headStr, _ := json.Marshal(head) global.FILE_LOG.Info("自定义请求头:" + string(headStr)) } return c.doRequest(req) } func (c *DefaultHttpClient[T]) RetryStrategy(retryStrategy RetryStrategy) *DefaultHttpClient[T] { c.retryStrategy = retryStrategy return c } // retryRequest 尝试执行请求并根据重试策略重试 // retryRequest 尝试执行请求并根据重试策略重试 func (c *DefaultHttpClient[T]) retryRequest(requestFunc func() (*http.Response, error)) (result *http.Response, err error) { for i := 0; ; i++ { result, err = requestFunc() if err == nil || !c.retryStrategy.ShouldRetry(i, err) { return } global.FILE_LOG.Error("http请求失败,开始第" + string(rune(i+1)) + "次重试,最近一次错误:[" + err.Error() + "]") time.Sleep(c.retryStrategy.RetryDelay(i)) } } func (c *DefaultHttpClient[T]) doPostForm(urlStr string, values url.Values) (response Response[T], err error) { global.FILE_LOG.Info("发送请求:" + urlStr) //ctx 用Background resp, err := c.retryRequest(func() (*http.Response, error) { return c.client.PostForm(urlStr, values) }) if err != nil { global.FILE_LOG.Error("http请求失败:" + err.Error()) return } defer resp.Body.Close() repBody, err := io.ReadAll(resp.Body) if err != nil { global.FILE_LOG.Error("http请求失败,读取body失败:" + err.Error()) return } if resp.StatusCode != http.StatusOK { global.FILE_LOG.Error("http请求异常,请求状态为:" + resp.Status) } global.FILE_LOG.Info("http应答为:" + string(repBody)) err = json.Unmarshal(repBody, &response) if err != nil { global.FILE_LOG.Error("解析应答失败,反序列化失败:" + err.Error()) } return } // DoRequest 执行 HTTP 请求 func (c *DefaultHttpClient[T]) doRequest(req *http.Request) (response Response[T], err error) { global.FILE_LOG.Info("发送请求:") //ctx 用Background resp, err := c.retryRequest(func() (*http.Response, error) { return c.client.Do(req) }) if err != nil { global.FILE_LOG.Error("http请求失败:" + err.Error()) return } defer resp.Body.Close() repBody, err := io.ReadAll(resp.Body) if err != nil { global.FILE_LOG.Error("http请求失败,读取body失败:" + err.Error()) return } if resp.StatusCode != http.StatusOK { global.FILE_LOG.Error("http请求异常,请求状态为:" + resp.Status) return } global.FILE_LOG.Info("htt请求成功,body:" + string(repBody)) return } // RetryStrategy 接口定义 type RetryStrategy interface { ShouldRetry(attempt int, err error) bool RetryDelay(attempt int) time.Duration } // FixedRetryStrategy 实现 RetryStrategy 接口 type FixedRetryStrategy struct { maxAttempts int delay time.Duration } // NewFixedRetryStrategy 创建 FixedRetryStrategy 实例 func newFixedRetryStrategy(maxAttempts int, delay time.Duration) *FixedRetryStrategy { return &FixedRetryStrategy{ maxAttempts: maxAttempts, delay: delay, } } // ShouldRetry 判断是否应该重试 func (s *FixedRetryStrategy) ShouldRetry(attempt int, err error) bool { return attempt < s.maxAttempts && retryErr(err) } func (s *FixedRetryStrategy) Header() { } func retryErr(err error) bool { return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) } // RetryDelay 返回重试延迟时间 func (s *FixedRetryStrategy) RetryDelay(attempt int) time.Duration { return time.Duration(attempt) * s.delay } func CreateDefault[T any]() *DefaultHttpClient[T] { return &DefaultHttpClient[T]{ client: &http.Client{Timeout: 10 * time.Second}, retryStrategy: newFixedRetryStrategy(3, 3*time.Second), } }