package dx import ( "context" "fmt" "io" "net/http" "net/url" "strings" "time" ) // Config 网关鉴权与请求配置 type Config struct { BaseURL string // 默认 https://cmp-api.ctwing.cn:20164 AppKey string AppSecret string Timeout time.Duration } // Client API 客户端 type Client struct { baseURL string appKey string appSecret string http *http.Client } // New 创建客户端 func NewClient(cfg Config) *Client { base := cfg.BaseURL if base == "" { base = DefaultBaseURL } timeout := cfg.Timeout if timeout == 0 { timeout = 10 * time.Second } return &Client{ baseURL: strings.TrimSuffix(base, "/"), appKey: cfg.AppKey, appSecret: cfg.AppSecret, http: &http.Client{ Timeout: timeout, }, } } // DoGetRequest 通用GET请求(自动处理签名) // endpoint: API路径,如 "/api/v1/openbill/queryTraffic" // params: 查询参数,需调用方自行构建 func (c *Client) DoGetRequest(ctx context.Context, endpoint string, params url.Values) ([]byte, error) { req, err := c.doGetRequest(ctx, endpoint, params) if err != nil { return nil, err } return c.doRequest(req) } // doGetRequest 封装通用的 GET 请求构建逻辑 func (c *Client) doGetRequest(ctx context.Context, endpoint string, params url.Values) (*http.Request, error) { queryString := params.Encode() timestamp := GetTimestampGMT() signStr, err := BuildSign(queryString, "", c.appSecret, timestamp) if err != nil { return nil, err } reqURL := c.baseURL + endpoint + "?" + queryString req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil) if err != nil { return nil, err } req.Header.Set("AppKey", c.appKey) req.Header.Set("Sign", signStr) req.Header.Set("Timestamp", timestamp) return req, nil } // doRequest 执行HTTP请求 func (c *Client) doRequest(req *http.Request) ([]byte, error) { resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("http status %d: %s", resp.StatusCode, string(body)) } return body, nil }