package cav2 import ( "crypto/hmac" "crypto/sha1" "encoding/hex" "encoding/json" "errors" "fmt" "hospital-admin-api/config" "hospital-admin-api/global" "io" "net/http" "net/url" "sort" "strings" "time" "github.com/sirupsen/logrus" ) 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) signature := c.signature(form) req.Header.Set("signature", signature) c.logUpstreamRequest(path, form, signature) resp, err := c.HTTPClient.Do(req) if err != nil { c.logUpstreamError(path, form, "request_error", err.Error()) return err } defer func() { _ = resp.Body.Close() }() respBody, err := io.ReadAll(resp.Body) if err != nil { c.logUpstreamError(path, form, "read_body_error", err.Error()) return err } c.logUpstreamResponse(path, form, resp.StatusCode, string(respBody)) if resp.StatusCode != http.StatusOK { return fmt.Errorf("ca v2 request failed: http %d", resp.StatusCode) } var envelope responseEnvelope if err := json.Unmarshal(respBody, &envelope); err != nil { c.logUpstreamError(path, form, "unmarshal_error", err.Error()) return err } if envelope.ResultCode != 0 { if envelope.ResultMsg != "" { c.logUpstreamError(path, form, "upstream_business_error", 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) } func (c *Client) logUpstreamRequest(path string, form url.Values, signature string) { if global.Logger == nil { return } global.Logger.WithFields(logrus.Fields{ "module": "ca_v2", "upstream_path": path, "request_form": form.Encode(), "request_params": form, "signature": signature, }).Info("ca v2 upstream request") } func (c *Client) logUpstreamResponse(path string, form url.Values, statusCode int, body string) { if global.Logger == nil { return } global.Logger.WithFields(logrus.Fields{ "module": "ca_v2", "upstream_path": path, "upstream_status": statusCode, "request_form": form.Encode(), "response_body": body, }).Info("ca v2 upstream response") } func (c *Client) logUpstreamError(path string, form url.Values, stage string, message string) { if global.Logger == nil { return } global.Logger.WithFields(logrus.Fields{ "module": "ca_v2", "stage": stage, "upstream_path": path, "request_form": form.Encode(), }).Error(message) }