package huawei // http://developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush.html?page=hmssdk_huaweipush_api_reference_s2 import ( "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "net/url" "strconv" "strings" "time" "go-common/library/log" "go-common/library/stat" "go-common/library/stat/prom" ) const ( _pushURL = "https://api.push.hicloud.com/pushsend.do" _nspSvc = "openpush.message.api.send" _ver = "1" // current SDK version // ResponseCodeSuccess success code ResponseCodeSuccess = "80000000" // ResponseCodeSomeTokenInvalid some tokens failed ResponseCodeSomeTokenInvalid = "80100000" // ResponseCodeAllTokenInvalid all tokens failed ResponseCodeAllTokenInvalid = "80100002" // ResponseCodeAllTokenInvalidNew . ResponseCodeAllTokenInvalidNew = "80300007" ) var ( // ErrLimit . ErrLimit = errors.New("触发华为系统级流控") ) // Client huawei push http client. type Client struct { Access *Access HTTPClient *http.Client Stats stat.Stat SDKCtx string Package string } // ver huawei push service version. type ver struct { Ver string `json:"ver"` AppID string `json:"appId"` } // NewClient new huawei push HTTP client. func NewClient(pkg string, a *Access, timeout time.Duration) *Client { ctx, _ := json.Marshal(ver{Ver: _ver, AppID: a.AppID}) return &Client{ Access: a, HTTPClient: &http.Client{Timeout: timeout}, Stats: prom.HTTPClient, SDKCtx: string(ctx), Package: pkg, } } /* Push push notifications. access_token: 必选,使用OAuth2进行鉴权时的ACCESSTOKEN nsp_ts: 必选,服务请求时间戳,自GMT 时间 1970-1-1 0:0:0至今的秒数。如果传入的时间与服务器时间相 差5分钟以上,服务器可能会拒绝请求。 nsp_svc: 必选, 本接口固定为openpush.message.api.send device_token_list: 以半角逗号分隔的华为PUSHTOKEN列表,单次最多只是1000个 expire_time: 格式ISO 8601[6]:2013-06-03T17:30,采用本地时间精确到分钟 payload: 描述投递消息的JSON结构体,描述PUSH消息的:类型、内容、显示、点击动作、报表统计和扩展信 息。具体参考下面的详细说明。 */ func (c *Client) Push(payload *Message, tokens []string, expire time.Time) (response *Response, err error) { now := time.Now() if c.Stats != nil { defer func() { c.Stats.Timing(_pushURL, int64(time.Since(now)/time.Millisecond)) log.Info("huawei stats timing: %v", int64(time.Since(now)/time.Millisecond)) if err != nil { c.Stats.Incr(_pushURL, "failed") } }() } pl, _ := payload.SetPkg(c.Package).JSON() reqURL := _pushURL + "?nsp_ctx=" + url.QueryEscape(c.SDKCtx) tokenStr, _ := json.Marshal(tokens) params := url.Values{} params.Add("access_token", c.Access.Token) params.Add("nsp_ts", strconv.FormatInt(now.Unix(), 10)) params.Add("nsp_svc", _nspSvc) params.Add("device_token_list", string(tokenStr)) params.Add("expire_time", expire.Format("2006-01-02T15:04")) params.Add("payload", pl) req, err := http.NewRequest(http.MethodPost, reqURL, strings.NewReader(params.Encode())) if err != nil { log.Error("http.NewRequest() error(%v)", err) return } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Connection", "Keep-Alive") res, err := c.HTTPClient.Do(req) if err != nil { log.Error("HTTPClient.Do() error(%v)", err) return } defer res.Body.Close() if res.StatusCode == http.StatusServiceUnavailable { return nil, ErrLimit } if res.StatusCode != http.StatusOK { err = fmt.Errorf("huawei Push http code(%d)", res.StatusCode) return } response = &Response{} nspStatus := res.Header.Get("NSP_STATUS") if nspStatus != "" { log.Error("push huawei system error, NSP_STATUS(%s)", nspStatus) response.Code = nspStatus response.Msg = "NSP_STATUS error" return } bs, err := ioutil.ReadAll(res.Body) if err != nil { log.Error("ioutil.ReadAll() error(%v)", err) return } if err = json.Unmarshal(bs, &response); err != nil { log.Error("json decode body(%s) error(%v)", string(bs), err) } return }