package facade import ( "context" "encoding/json" "errors" "eta/eta_chart_lib/utils" "fmt" "io" "net/http" "strings" "sync" "time" ) var ( once sync.Once miniFacadeMap = make(map[string]ChartCollect) FacadeClient = BaseMiniFacade{ client: DefaultClient(), } ) const ( bookMark = "bookMark" unBookMark = "unBookMark" ) func GetInstance(name string) ChartCollect { return miniFacadeMap[name] } type ChartCollect interface { Collect(data BaseRequest) (err error) UnCollect(data BaseRequest) (err error) IsCollect(data BaseRequest) bool } type BaseRequest struct { Auth string `json:"auth"` UniqueCode string `json:"uniqueCode"` } func (bm *BaseMiniFacade) GetData() interface{} { return bm.data } func (bm *BaseMiniFacade) Post(url string, data interface{}, auth string) (result string, err error) { resp, err := bm.client.Post(url, data, auth) if err != nil { return } respBody, respErr := io.ReadAll(resp.Body) if respErr != nil { utils.FileLog.Error("读取body失败,err:%v", err) return } result = string(respBody) return } func (bm *BaseMiniFacade) Deal(data BaseRequest) *BaseMiniFacade { bm.data = data return bm } func (bm *BaseMiniFacade) HandleAction(action string, handler ChartCollect) (actionMsg string, err error) { if handler == nil { return "", errors.New("不支持的操作类型") } switch action { case bookMark: err = handler.Collect(bm.data) actionMsg = "收藏" case unBookMark: err = handler.UnCollect(bm.data) actionMsg = "取消收藏" default: err = errors.New("不支持的操作") } return } func (bm *BaseMiniFacade) IsCollect(handler ChartCollect) bool { return handler.IsCollect(bm.data) } type BaseMiniFacade struct { client *HttpClient data BaseRequest //返回参数 } 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{}, auth string) (resp *http.Response, err error) { dataStr, err := json.Marshal(data) if err != nil { utils.FileLog.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", auth) if err != nil { utils.FileLog.Error("创建POST请求失败: %v", err.Error()) } resp, err = hc.DoWithRetry(req.Context(), req) if err == nil { code := resp.StatusCode if code != 200 { utils.FileLog.Error("请求错误应答,状态码:%d", code) errMsg := fmt.Sprintf("请求状态码异常,StatusCode:[%d]", code) respBody, respErr := io.ReadAll(resp.Body) if respErr != nil { utils.FileLog.Error("读取body失败,err:%v", err) err = errors.New(errMsg) return } utils.FileLog.Error("请求错误应答,body:%s", string(respBody)) errMsg = fmt.Sprintf("%s,body:%s", errMsg, string(respBody)) err = errors.New(errMsg) return } } else { utils.FileLog.Error("未知的应答错误,获取第三方授权信息失败", err.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 { utils.FileLog.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 { utils.FileLog.Error("创建POST请求失败: %v", err) } resp, err = hc.DoWithRetry(req.Context(), req) code := resp.StatusCode if code != 200 { utils.FileLog.Error("请求错误应答,状态码:%d", code) errMsg := fmt.Sprintf("请求状态码异常,StatusCode:[%d]", code) respBody, respErr := io.ReadAll(resp.Body) if respErr != nil { utils.FileLog.Error("读取body失败,err:%v", err) err = errors.New(errMsg) return } utils.FileLog.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 { utils.FileLog.Error("创建请求失败: %v", err) } resp, err = hc.DoWithRetry(req.Context(), req) return } func RegisterMiniFacade(name string, facade ChartCollect) { if facade == nil { panic("实例不存在,无法注册") } if _, ok := miniFacadeMap[name]; ok { utils.FileLog.Error("请勿重复注册小程序插件:" + name) } miniFacadeMap[name] = facade } func init() { }