package cav2 import ( "crypto/hmac" "crypto/sha1" "encoding/hex" "encoding/json" "errors" "fmt" "hospital-admin-api/config" "io" "net/http" "net/url" "sort" "strings" "time" ) type Client struct { BaseURL string AppID string AppSecret string HTTPClient *http.Client } func NewClient(baseURL, appID, appSecret string) *Client { return &Client{ BaseURL: strings.TrimRight(baseURL, "/"), AppID: appID, AppSecret: appSecret, HTTPClient: &http.Client{ Timeout: 15 * time.Second, }, } } func NewClientFromConfig() *Client { baseURL := config.C.CaOnlineV2.CaOnlineAPIURL appID := config.C.CaOnlineV2.CaOnlineAppID appSecret := config.C.CaOnlineV2.CaOnlineAppSecret if baseURL == "" { baseURL = config.C.CaOnline.CaOnlineApiUrl } if appID == "" { appID = config.C.CaOnline.CaOnlineAppId } if appSecret == "" { appSecret = config.C.CaOnline.CaOnlineAppSecret } return NewClient( baseURL, appID, appSecret, ) } func (c *Client) postForm(path string, form url.Values, out interface{}) error { if c == nil { return errors.New("ca v2 client is nil") } if c.BaseURL == "" || c.AppID == "" || c.AppSecret == "" { return errors.New("ca v2 client config is incomplete") } if c.HTTPClient == nil { c.HTTPClient = &http.Client{Timeout: 15 * time.Second} } payload := strings.NewReader(form.Encode()) req, err := http.NewRequest(http.MethodPost, c.BaseURL+path, payload) if err != nil { return err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("app_id", c.AppID) req.Header.Set("signature", c.signature(form)) resp, err := c.HTTPClient.Do(req) if err != nil { return err } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return fmt.Errorf("ca v2 request failed: http %d", resp.StatusCode) } respBody, err := io.ReadAll(resp.Body) if err != nil { return err } var envelope responseEnvelope if err := json.Unmarshal(respBody, &envelope); err != nil { return err } if envelope.ResultCode != 0 { if envelope.ResultMsg != "" { return errors.New(envelope.ResultMsg) } return fmt.Errorf("ca v2 request failed with code %d", envelope.ResultCode) } if out == nil { return nil } body := strings.TrimSpace(string(envelope.Body)) if body == "" || body == "null" { return nil } return json.Unmarshal(envelope.Body, out) } func (c *Client) postFormForString(path string, form url.Values) (string, error) { var raw json.RawMessage if err := c.postForm(path, form, &raw); err != nil { return "", err } body := strings.TrimSpace(string(raw)) if body == "" || body == "null" { return "", nil } var text string if err := json.Unmarshal(raw, &text); err == nil { return text, nil } var data map[string]string if err := json.Unmarshal(raw, &data); err != nil { return "", err } for _, key := range []string{"url", "authUrl", "faceUrl", "h5Url"} { if value := strings.TrimSpace(data[key]); value != "" { return value, nil } } return "", errors.New("ca v2 string body not found") } func (c *Client) signature(form url.Values) string { keys := make([]string, 0, len(form)) for key := range form { keys = append(keys, key) } sort.Strings(keys) values := make([]string, 0, len(keys)) for _, key := range keys { for _, value := range form[key] { values = append(values, value) } } h := hmac.New(sha1.New, []byte(c.AppSecret)) h.Write([]byte(strings.Join(values, "&"))) return hex.EncodeToString(h.Sum(nil)) } func setValue(form url.Values, key, value string) { if strings.TrimSpace(value) == "" { return } form.Set(key, value) }