package mes import ( "bytes" "encoding/json" "fmt" "io" "net/http" "net/url" "strings" "time" ) // Client MES 内部 API HTTP 客户端。 // 自动携带请求头:X-API-TOKEN(项目间 token)、Content-Type: application/json。 // 任何非 2xx 状态返回包含响应体的错误信息。 type Client struct { baseURL string token string httpClient *http.Client } func New(baseURL, token string) *Client { return &Client{ baseURL: strings.TrimRight(baseURL, "/"), token: token, httpClient: &http.Client{Timeout: 10 * time.Second}, } } // Get 发送 GET 请求,query 为空则忽略。 func (c *Client) Get(path string, query url.Values) ([]byte, http.Header, error) { u := c.baseURL + path if len(query) > 0 { u += "?" + query.Encode() } return c.do(http.MethodGet, u, nil) } // Post 发送 POST 请求,body 序列化为 JSON(body 为 nil 则不发送请求体)。 func (c *Client) Post(path string, body any) ([]byte, http.Header, error) { var reader io.Reader if body != nil { b, err := json.Marshal(body) if err != nil { return nil, nil, fmt.Errorf("序列化请求体失败: %w", err) } reader = bytes.NewReader(b) } return c.do(http.MethodPost, c.baseURL+path, reader) } func (c *Client) do(method, u string, body io.Reader) ([]byte, http.Header, error) { req, err := http.NewRequest(method, u, body) if err != nil { return nil, nil, err } req.Header.Set("X-API-TOKEN", c.token) req.Header.Set("Content-Type", "application/json;charset=utf-8") resp, err := c.httpClient.Do(req) if err != nil { return nil, nil, err } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, resp.Header, err } if resp.StatusCode < 200 || resp.StatusCode > 299 { return respBody, resp.Header, fmt.Errorf("MES %s %s 返回 %d:%s", method, u, resp.StatusCode, truncate(string(respBody), 200)) } return respBody, resp.Header, nil } func truncate(s string, n int) string { s = strings.TrimSpace(s) if len(s) > n { return s[:n] + "..." } return s }