client.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package huawei
  2. // http://developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush.html?page=hmssdk_huaweipush_api_reference_s2
  3. import (
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io/ioutil"
  8. "net/http"
  9. "net/url"
  10. "strconv"
  11. "strings"
  12. "time"
  13. "go-common/library/log"
  14. "go-common/library/stat"
  15. "go-common/library/stat/prom"
  16. )
  17. const (
  18. _pushURL = "https://api.push.hicloud.com/pushsend.do"
  19. _nspSvc = "openpush.message.api.send"
  20. _ver = "1" // current SDK version
  21. // ResponseCodeSuccess success code
  22. ResponseCodeSuccess = "80000000"
  23. // ResponseCodeSomeTokenInvalid some tokens failed
  24. ResponseCodeSomeTokenInvalid = "80100000"
  25. // ResponseCodeAllTokenInvalid all tokens failed
  26. ResponseCodeAllTokenInvalid = "80100002"
  27. // ResponseCodeAllTokenInvalidNew .
  28. ResponseCodeAllTokenInvalidNew = "80300007"
  29. )
  30. var (
  31. // ErrLimit .
  32. ErrLimit = errors.New("触发华为系统级流控")
  33. )
  34. // Client huawei push http client.
  35. type Client struct {
  36. Access *Access
  37. HTTPClient *http.Client
  38. Stats stat.Stat
  39. SDKCtx string
  40. Package string
  41. }
  42. // ver huawei push service version.
  43. type ver struct {
  44. Ver string `json:"ver"`
  45. AppID string `json:"appId"`
  46. }
  47. // NewClient new huawei push HTTP client.
  48. func NewClient(pkg string, a *Access, timeout time.Duration) *Client {
  49. ctx, _ := json.Marshal(ver{Ver: _ver, AppID: a.AppID})
  50. return &Client{
  51. Access: a,
  52. HTTPClient: &http.Client{Timeout: timeout},
  53. Stats: prom.HTTPClient,
  54. SDKCtx: string(ctx),
  55. Package: pkg,
  56. }
  57. }
  58. /*
  59. Push push notifications.
  60. access_token: 必选,使用OAuth2进行鉴权时的ACCESSTOKEN
  61. nsp_ts: 必选,服务请求时间戳,自GMT 时间 1970-1-1 0:0:0至今的秒数。如果传入的时间与服务器时间相 差5分钟以上,服务器可能会拒绝请求。
  62. nsp_svc: 必选, 本接口固定为openpush.message.api.send
  63. device_token_list: 以半角逗号分隔的华为PUSHTOKEN列表,单次最多只是1000个
  64. expire_time: 格式ISO 8601[6]:2013-06-03T17:30,采用本地时间精确到分钟
  65. payload: 描述投递消息的JSON结构体,描述PUSH消息的:类型、内容、显示、点击动作、报表统计和扩展信 息。具体参考下面的详细说明。
  66. */
  67. func (c *Client) Push(payload *Message, tokens []string, expire time.Time) (response *Response, err error) {
  68. now := time.Now()
  69. if c.Stats != nil {
  70. defer func() {
  71. c.Stats.Timing(_pushURL, int64(time.Since(now)/time.Millisecond))
  72. log.Info("huawei stats timing: %v", int64(time.Since(now)/time.Millisecond))
  73. if err != nil {
  74. c.Stats.Incr(_pushURL, "failed")
  75. }
  76. }()
  77. }
  78. pl, _ := payload.SetPkg(c.Package).JSON()
  79. reqURL := _pushURL + "?nsp_ctx=" + url.QueryEscape(c.SDKCtx)
  80. tokenStr, _ := json.Marshal(tokens)
  81. params := url.Values{}
  82. params.Add("access_token", c.Access.Token)
  83. params.Add("nsp_ts", strconv.FormatInt(now.Unix(), 10))
  84. params.Add("nsp_svc", _nspSvc)
  85. params.Add("device_token_list", string(tokenStr))
  86. params.Add("expire_time", expire.Format("2006-01-02T15:04"))
  87. params.Add("payload", pl)
  88. req, err := http.NewRequest(http.MethodPost, reqURL, strings.NewReader(params.Encode()))
  89. if err != nil {
  90. log.Error("http.NewRequest() error(%v)", err)
  91. return
  92. }
  93. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  94. req.Header.Set("Connection", "Keep-Alive")
  95. res, err := c.HTTPClient.Do(req)
  96. if err != nil {
  97. log.Error("HTTPClient.Do() error(%v)", err)
  98. return
  99. }
  100. defer res.Body.Close()
  101. if res.StatusCode == http.StatusServiceUnavailable {
  102. return nil, ErrLimit
  103. }
  104. if res.StatusCode != http.StatusOK {
  105. err = fmt.Errorf("huawei Push http code(%d)", res.StatusCode)
  106. return
  107. }
  108. response = &Response{}
  109. nspStatus := res.Header.Get("NSP_STATUS")
  110. if nspStatus != "" {
  111. log.Error("push huawei system error, NSP_STATUS(%s)", nspStatus)
  112. response.Code = nspStatus
  113. response.Msg = "NSP_STATUS error"
  114. return
  115. }
  116. bs, err := ioutil.ReadAll(res.Body)
  117. if err != nil {
  118. log.Error("ioutil.ReadAll() error(%v)", err)
  119. return
  120. }
  121. if err = json.Unmarshal(bs, &response); err != nil {
  122. log.Error("json decode body(%s) error(%v)", string(bs), err)
  123. }
  124. return
  125. }