bilibili-backup/app/service/main/push/dao/huawei/client.go

135 lines
4.0 KiB
Go
Raw Normal View History

2019-04-22 10:59:20 +08:00
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
}