client.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package qiyuesuosdk
  2. import (
  3. "bytes"
  4. "crypto/md5"
  5. "encoding/hex"
  6. "encoding/json"
  7. "fmt"
  8. "io"
  9. "mime/multipart"
  10. "net/http"
  11. "net/url"
  12. "strings"
  13. "time"
  14. "git.sxidc.com/student-physical-examination/contract_lock_sdk/utils"
  15. "github.com/google/uuid"
  16. )
  17. // Client 契约锁开放平台客户端;仅封装 HTTP/签名与契约锁 API,不含业务状态机。
  18. type Client struct {
  19. cfg Config
  20. signDefaults SignDefaults
  21. sdk *utils.SdkClient
  22. http *http.Client
  23. }
  24. // New 创建客户端。signDefaults 可为零值,签章时可按请求单独传入 ProcessID/Launcher。
  25. func New(cfg Config, signDefaults SignDefaults) *Client {
  26. address := strings.TrimRight(cfg.Address, "/")
  27. return &Client{
  28. cfg: cfg,
  29. signDefaults: signDefaults,
  30. sdk: utils.NewSdkClient(address, cfg.AppToken, cfg.AppSecret),
  31. http: &http.Client{Timeout: 60 * time.Second},
  32. }
  33. }
  34. func (c *Client) baseURL() string {
  35. return strings.TrimRight(c.cfg.Address, "/")
  36. }
  37. func (c *Client) authHeaders() map[string]string {
  38. nonce := uuid.NewString()
  39. timestamp := fmt.Sprintf("%d", time.Now().UnixMilli())
  40. raw := c.cfg.AppToken + c.cfg.AppSecret + timestamp + nonce
  41. sum := md5.Sum([]byte(raw))
  42. return map[string]string{
  43. "x-qys-accesstoken": c.cfg.AppToken,
  44. "x-qys-timestamp": timestamp,
  45. "x-qys-nonce": nonce,
  46. "x-qys-signature": hex.EncodeToString(sum[:]),
  47. }
  48. }
  49. func (c *Client) postJSON(path string, body any, out any) error {
  50. payload, err := json.Marshal(body)
  51. if err != nil {
  52. return err
  53. }
  54. req, err := http.NewRequest(http.MethodPost, c.baseURL()+path, bytes.NewReader(payload))
  55. if err != nil {
  56. return err
  57. }
  58. req.Header.Set("Content-Type", "application/json;charset=UTF-8")
  59. for k, v := range c.authHeaders() {
  60. req.Header.Set(k, v)
  61. }
  62. return c.doJSON(req, out)
  63. }
  64. func (c *Client) getQuery(path string, query url.Values, out any) error {
  65. u := c.baseURL() + path
  66. if len(query) > 0 {
  67. u += "?" + query.Encode()
  68. }
  69. req, err := http.NewRequest(http.MethodGet, u, nil)
  70. if err != nil {
  71. return err
  72. }
  73. for k, v := range c.authHeaders() {
  74. req.Header.Set(k, v)
  75. }
  76. return c.doJSON(req, out)
  77. }
  78. func (c *Client) doJSON(req *http.Request, out any) error {
  79. resp, err := c.http.Do(req)
  80. if err != nil {
  81. return err
  82. }
  83. defer resp.Body.Close()
  84. data, err := io.ReadAll(resp.Body)
  85. if err != nil {
  86. return err
  87. }
  88. if out == nil {
  89. return nil
  90. }
  91. return json.Unmarshal(data, out)
  92. }
  93. func (c *Client) postMultipart(path string, fields map[string]string, fileField string, fileBytes []byte, out any) error {
  94. var buf bytes.Buffer
  95. w := multipart.NewWriter(&buf)
  96. for k, v := range fields {
  97. if err := w.WriteField(k, v); err != nil {
  98. return err
  99. }
  100. }
  101. if len(fileBytes) > 0 && fileField != "" {
  102. part, err := w.CreateFormFile(fileField, "document.pdf")
  103. if err != nil {
  104. return err
  105. }
  106. if _, err = part.Write(fileBytes); err != nil {
  107. return err
  108. }
  109. }
  110. if err := w.Close(); err != nil {
  111. return err
  112. }
  113. req, err := http.NewRequest(http.MethodPost, c.baseURL()+path, &buf)
  114. if err != nil {
  115. return err
  116. }
  117. req.Header.Set("Content-Type", w.FormDataContentType())
  118. for k, v := range c.authHeaders() {
  119. req.Header.Set(k, v)
  120. }
  121. return c.doJSON(req, out)
  122. }
  123. func (c *Client) download(path string, query url.Values) ([]byte, error) {
  124. u := c.baseURL() + path
  125. if len(query) > 0 {
  126. u += "?" + query.Encode()
  127. }
  128. req, err := http.NewRequest(http.MethodGet, u, nil)
  129. if err != nil {
  130. return nil, err
  131. }
  132. for k, v := range c.authHeaders() {
  133. req.Header.Set(k, v)
  134. }
  135. resp, err := c.http.Do(req)
  136. if err != nil {
  137. return nil, err
  138. }
  139. defer resp.Body.Close()
  140. return io.ReadAll(resp.Body)
  141. }
  142. func (c *Client) serviceOK(resp *utils.SdkResponse) error {
  143. if resp == nil {
  144. return fmt.Errorf("qiyuesuo: empty sdk response")
  145. }
  146. if resp.Message == "SUCCESS" {
  147. return nil
  148. }
  149. return fmt.Errorf("qiyuesuo: %s", resp.Message)
  150. }
  151. func (c *Client) serviceString(resp *utils.SdkResponse) (string, error) {
  152. if err := c.serviceOK(resp); err != nil {
  153. return "", err
  154. }
  155. if resp.Result == nil {
  156. return "", nil
  157. }
  158. if s, ok := resp.Result.(string); ok {
  159. return s, nil
  160. }
  161. return "", fmt.Errorf("qiyuesuo: unexpected result type %T", resp.Result)
  162. }
  163. func (c *Client) resolveProcessID(id string) string {
  164. if id != "" {
  165. return id
  166. }
  167. return c.signDefaults.ProcessID
  168. }
  169. func (c *Client) resolveLauncher(name string) string {
  170. if name != "" {
  171. return name
  172. }
  173. return c.signDefaults.TenantName
  174. }