Files
big-qmt/go-client/sdk/client.go
2026-08-25 18:59:18 +08:00

142 lines
3.1 KiB
Go

package sdk
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
type Client struct {
baseURL string
token string
accountType string
http *http.Client
}
func New(baseURL, token string, timeout time.Duration) *Client {
if timeout <= 0 {
timeout = 15 * time.Second
}
return &Client{
baseURL: strings.TrimRight(baseURL, "/"),
token: token,
accountType: "stock",
http: &http.Client{Timeout: timeout},
}
}
func (c *Client) SetAccountType(accountType string) *Client {
if strings.TrimSpace(accountType) != "" {
c.accountType = accountType
}
return c
}
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 (c *Client) getField(ctx context.Context, path, key string) (any, error) {
var out map[string]any
if err := c.get(ctx, path, &out); err != nil {
return nil, err
}
return out[key], nil
}
func (c *Client) postField(ctx context.Context, path string, body any, key string) (any, error) {
var out map[string]any
if err := c.post(ctx, path, body, &out); err != nil {
return nil, err
}
if msg, ok := out["error"].(string); ok && msg != "" {
return nil, &BusinessError{Message: msg}
}
if key == "" {
return out, nil
}
if v, ok := out[key]; ok {
return v, nil
}
return out, nil
}
func csvJoin(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, ",")
}
func truncate(b []byte, n int) string {
if len(b) <= n {
return string(b)
}
return string(b[:n]) + "..."
}