package sdk import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" ) // Client 调用 QMT HTTP API。 type Client struct { baseURL string token string accountType string http *http.Client } func New(baseURL, token, accountType string, timeout time.Duration) *Client { base := strings.TrimRight(baseURL, "/") return &Client{baseURL: base, token: token, accountType: accountType, http: &http.Client{Timeout: timeout}} } func (c *Client) Account(override string) string { if strings.TrimSpace(override) != "" { return override } return c.accountType } func (c *Client) get(ctx context.Context, path string, dest any) error { return c.do(ctx, http.MethodGet, path, nil, dest) } func (c *Client) post(ctx context.Context, path string, body any, dest any) error { if body == nil { body = map[string]any{} } return c.do(ctx, http.MethodPost, path, body, dest) } func (c *Client) do(ctx context.Context, method, path string, body any, dest any) error { var rdr io.Reader if body != nil && method != http.MethodGet { raw, err := json.Marshal(body) if err != nil { return fmt.Errorf("marshal request: %w", err) } rdr = bytes.NewReader(raw) } req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, rdr) if err != nil { return err } req.Header.Set("X-Token", c.token) req.Header.Set("Accept", "application/json") if rdr != nil { req.Header.Set("Content-Type", "application/json") } resp, err := c.http.Do(req) if err != nil { return err } defer resp.Body.Close() raw, err := io.ReadAll(resp.Body) if err != nil { return err } if resp.StatusCode >= 400 { apiErr := &APIError{StatusCode: resp.StatusCode, Message: strings.TrimSpace(string(raw))} var parsed APIError if json.Unmarshal(raw, &parsed) == nil { if parsed.StatusCode == 0 { parsed.StatusCode = resp.StatusCode } if parsed.Message != "" { apiErr = &parsed } } return apiErr } if dest == nil || len(raw) == 0 { return nil } if err := json.Unmarshal(raw, dest); err != nil { return fmt.Errorf("unmarshal %s: %w; body=%s", path, err, truncate(raw, 512)) } return nil } func truncate(b []byte, n int) string { if len(b) <= n { return string(b) } return string(b[:n]) + "..." } func ArrayJoin(items []string) string { parts := make([]string, 0, len(items)) for _, s := range items { s = strings.TrimSpace(s) if s != "" { parts = append(parts, s) } } return strings.Join(parts, ",") }