package client import ( "context" "encoding/json" "errors" logger "eta/eta_mini_ht_api/common/component/log" "fmt" "io" "net/http" "strings" "time" ) type HttpClient struct { *http.Client maxRetries int retryDelayFunc RetryDelayFunc } // NewClient 构造函数,其中 delayFunc 参数是可选的 func NewClient(timeout time.Duration, maxRetries int, delayFunc ...RetryDelayFunc) *HttpClient { var df RetryDelayFunc if len(delayFunc) > 0 { df = delayFunc[0] } else { df = defaultRetryDelayFunc } return &HttpClient{ Client: &http.Client{Timeout: timeout}, maxRetries: maxRetries, retryDelayFunc: df, } } func DefaultClient() *HttpClient { return NewClient(time.Second*10, 3) } func defaultRetryDelayFunc(attempt int) time.Duration { delay := time.Duration(attempt) * time.Second if attempt > 0 { delay *= 2 } return delay } type RetryDelayFunc func(attempt int) time.Duration func retryErr(err error) bool { return errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) } // DoWithRetry 发送带有重试机制的HTTP请求,允许用户自定义重试延迟逻辑 func (hc *HttpClient) DoWithRetry(ctx context.Context, req *http.Request) (resp *http.Response, err error) { attempt := 0 for { resp, err = hc.Do(req.WithContext(ctx)) if err != nil && retryErr(err) { if attempt >= hc.maxRetries { return nil, fmt.Errorf("请求失败: %w", err) } attempt++ delay := hc.retryDelayFunc(attempt) time.Sleep(delay) continue } return } } func (hc *HttpClient) Post(url string, data interface{}) (resp *http.Response, err error) { dataStr, err := json.Marshal(data) if err != nil { logger.Error("请求data json序列化失败,err:" + err.Error()) } body := io.NopCloser(strings.NewReader(string(dataStr))) req, err := http.NewRequest(http.MethodPost, url, body) req.Header.Set("Content-Type", "application/json") if err != nil { logger.Error("创建POST请求失败: %v", err) } resp, err = hc.DoWithRetry(req.Context(), req) if err == nil { code := resp.StatusCode if code != 200 { logger.Error("请求错误应答,状态码:%d", code) errMsg := fmt.Sprintf("请求状态码异常,StatusCode:[%d]", code) respBody, respErr := io.ReadAll(resp.Body) if respErr != nil { logger.Error("读取body失败,err:%v", err) err = errors.New(errMsg) return } logger.Error("请求错误应答,body:%s", string(respBody)) errMsg = fmt.Sprintf("%s,body:%s", errMsg, string(respBody)) err = errors.New(errMsg) return } } else { logger.Error("未知的应答错误,获取第三方授权信息失败") } return } func (hc *HttpClient) PostWithAuth(url string, data interface{}, token string) (resp *http.Response, err error) { dataStr, err := json.Marshal(data) if err != nil { logger.Error("请求data json序列化失败,err:" + err.Error()) } body := io.NopCloser(strings.NewReader(string(dataStr))) req, err := http.NewRequest(http.MethodPost, url, body) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", token) if err != nil { logger.Error("创建POST请求失败: %v", err) } resp, err = hc.DoWithRetry(req.Context(), req) code := resp.StatusCode if code != 200 { logger.Error("请求错误应答,状态码:%d", code) errMsg := fmt.Sprintf("请求状态码异常,StatusCode:[%d]", code) respBody, respErr := io.ReadAll(resp.Body) if respErr != nil { logger.Error("读取body失败,err:%v", err) err = errors.New(errMsg) return } logger.Error("请求错误应答,body:%s", string(respBody)) errMsg = fmt.Sprintf("%s,body:%s", errMsg, string(respBody)) err = errors.New(errMsg) return } return } func (hc *HttpClient) Get(url string) (resp *http.Response, err error) { req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil) if err != nil { logger.Error("创建请求失败: %v", err) } resp, err = hc.DoWithRetry(req.Context(), req) return }