Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["client_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/service/main/push/model:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"auth.go",
"client.go",
"define.go",
],
importpath = "go-common/app/service/main/push/dao/oppo",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/log:go_default_library",
"//library/stat:go_default_library",
"//library/stat/prom:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,63 @@
package oppo
import (
"crypto/sha256"
"encoding/json"
"fmt"
"net/http"
"net/url"
"strconv"
"time"
)
// Auth oppo auth token.
type Auth struct {
Token string
Expire int64
}
type authResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
Token string `json:"auth_token"`
CreateTime int64 `json:"create_time"` // auth token的授权时间单位为毫秒
} `json:"data"`
}
// NewAuth get auth token.
func NewAuth(key, secret string) (a *Auth, err error) {
tm := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) // 用毫秒
params := url.Values{}
params.Add("app_key", key)
params.Add("timestamp", tm)
params.Add("sign", sign(key, secret, tm))
res, err := http.PostForm(_apiAuth, params)
if err != nil {
return
}
defer res.Body.Close()
dc := json.NewDecoder(res.Body)
resp := new(authResponse)
if err = dc.Decode(resp); err != nil {
return
}
if resp.Code == ResponseCodeSuccess {
a = &Auth{
Token: resp.Data.Token,
Expire: resp.Data.CreateTime/1000 + _authExpire,
}
return
}
err = fmt.Errorf("new access error, code(%d) description(%s)", resp.Code, resp.Message)
return
}
func sign(key, secret, timestamp string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(key+timestamp+secret)))
}
// IsExpired judge that whether privilige expired.
func (a *Auth) IsExpired() bool {
return a.Expire <= time.Now().Add(4*time.Hour).Unix() // 提前4小时过期for renew auth
}

View File

@@ -0,0 +1,184 @@
package oppo
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"go-common/library/log"
"go-common/library/stat"
"go-common/library/stat/prom"
)
// Client huawei push http client.
type Client struct {
Auth *Auth
HTTPClient *http.Client
Stats stat.Stat
Activity string
}
// NewClient new huawei push HTTP client.
func NewClient(a *Auth, activity string, timeout time.Duration) *Client {
return &Client{
Auth: a,
HTTPClient: &http.Client{Timeout: timeout},
Stats: prom.HTTPClient,
Activity: activity,
}
}
// Message saves push message content.
func (c *Client) Message(m *Message) (response *Response, err error) {
now := time.Now()
if c.Stats != nil {
defer func() {
c.Stats.Timing(_apiMessage, int64(time.Since(now)/time.Millisecond))
log.Info("oppo message stats timing: %v", int64(time.Since(now)/time.Millisecond))
if err != nil {
c.Stats.Incr(_apiMessage, "failed")
}
}()
}
params := url.Values{}
params.Add("auth_token", c.Auth.Token)
params.Add("title", m.Title)
params.Add("content", m.Content)
params.Add("click_action_type", strconv.Itoa(m.ActionType))
params.Add("click_action_activity", c.Activity)
params.Add("action_parameters", m.ActionParams)
params.Add("off_line_ttl", strconv.Itoa(m.OfflineTTL))
params.Add("call_back_url", m.CallbackURL)
req, err := http.NewRequest(http.MethodPost, _apiMessage, 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()
response = &Response{}
bs, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Error("ioutil.ReadAll() error(%v)", err)
return
} else if len(bs) == 0 {
return
}
if e := json.Unmarshal(bs, &response); e != nil {
if e != io.EOF {
log.Error("json decode body(%s) error(%v)", string(bs), e)
}
}
return
}
// Push push notification.
func (c *Client) Push(msgID string, tokens []string) (response *Response, err error) {
now := time.Now()
if c.Stats != nil {
defer func() {
c.Stats.Timing(_apiPushBroadcast, int64(time.Since(now)/time.Millisecond))
log.Info("oppo push stats timing: %v", int64(time.Since(now)/time.Millisecond))
if err != nil {
c.Stats.Incr(_apiPushBroadcast, "failed")
}
}()
}
params := url.Values{}
params.Add("auth_token", c.Auth.Token)
params.Add("message_id", msgID)
params.Add("target_type", _pushTypeToken)
params.Add("registration_ids", strings.Join(tokens, ";"))
req, err := http.NewRequest(http.MethodPost, _apiPushBroadcast, 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()
response = &Response{}
bs, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Error("ioutil.ReadAll() error(%v)", err)
return
} else if len(bs) == 0 {
return
}
if e := json.Unmarshal(bs, &response); e != nil {
if e != io.EOF {
log.Error("json decode body(%s) error(%v)", string(bs), e)
}
}
return
}
// PushOne push single notification.
func (c *Client) PushOne(m *Message, token string) (response *Response, err error) {
now := time.Now()
if c.Stats != nil {
defer func() {
c.Stats.Timing(_apiPushUnicast, int64(time.Since(now)/time.Millisecond))
log.Info("oppo pushOne stats timing: %v", int64(time.Since(now)/time.Millisecond))
if err != nil {
c.Stats.Incr(_apiPushUnicast, "failed")
}
}()
}
m.ActionActivity = c.Activity
params := url.Values{}
msg, _ := json.Marshal(&struct {
TargetType string `json:"target_type"`
Token string `json:"registration_id"`
Notification *Message `json:"notification"`
}{
TargetType: _pushTypeToken,
Token: token,
Notification: m,
})
params.Add("auth_token", c.Auth.Token)
params.Add("message", string(msg))
req, err := http.NewRequest(http.MethodPost, _apiPushUnicast, 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()
response = &Response{}
bs, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Error("ioutil.ReadAll() error(%v)", err)
return
} else if len(bs) == 0 {
return
}
if e := json.Unmarshal(bs, &response); e != nil {
if e != io.EOF {
log.Error("json decode body(%s) error(%v)", string(bs), e)
}
}
return
}

View File

@@ -0,0 +1,79 @@
package oppo
import (
"encoding/json"
"testing"
"time"
"go-common/app/service/main/push/model"
"github.com/smartystreets/goconvey/convey"
)
var (
auth = &Auth{Token: "7826d9a0-f192-4402-8a2c-b0dffbdd94da", Expire: 1519336387}
cli = NewClient(auth, "com.bilibili.oppo.push.internal", time.Hour)
)
func init() {
auth, _ = NewAuth("UTlf5g2bAOSQA9aCqYiFQh3X", "Ppq0B4xk73augxMJbEBSyu9m")
}
func Test_AuthExpire(t *testing.T) {
convey.Convey("auth expire", t, func() {
a := Auth{Expire: time.Now().Add(-8 * time.Hour).Unix()}
if !a.IsExpired() {
t.Errorf("access should be expire")
}
if auth.IsExpired() {
t.Error("access should not be expire")
}
})
}
func Test_Message(t *testing.T) {
convey.Convey("message", t, func() {
params, _ := json.Marshal(map[string]string{
"task_id": "123",
"scheme": model.Scheme(1, "2", model.PlatformAndroid, model.UnknownBuild),
})
m := &Message{
Title: "this is title",
Content: "this is content",
ActionType: ActionTypeInner,
ActionParams: string(params),
OfflineTTL: 3600,
}
res, err := cli.Message(m)
convey.So(err, convey.ShouldBeNil)
t.Logf("message result(%+v)", res)
})
}
func Test_Pushs(t *testing.T) {
convey.Convey("pushs", t, func() {
res, err := cli.Push("5a72f82ba250c94f9f51540d", []string{"token1", "token2"})
convey.So(err, convey.ShouldBeNil)
t.Logf("push result(%+v)", res)
})
}
func Test_PushOne(t *testing.T) {
convey.Convey("push one", t, func() {
params, _ := json.Marshal(map[string]string{
"task_id": "123",
"scheme": model.Scheme(1, "2", model.PlatformAndroid, model.UnknownBuild),
})
m := &Message{
Title: "this is title",
Content: "this is content",
ActionType: ActionTypeInner,
ActionParams: string(params),
OfflineTTL: 3600,
// CallbackURL: oppo.CallbackURL(1, 123),
}
res, err := cli.PushOne(m, "") // baab653406d187af12daa9980c87f4e5
convey.So(err, convey.ShouldBeNil)
t.Logf("pushOne result(%+v)", res)
})
}

View File

@@ -0,0 +1,72 @@
package oppo
import "fmt"
const (
_host = "https://api.push.oppomobile.com"
_apiAuth = _host + "/server/v1/auth"
_apiMessage = _host + "/server/v1/message/notification/save_message_content" // 保存通知栏消息内容体
_apiPushUnicast = _host + "/server/v1/message/notification/unicast" // 单条推送
_apiPushBroadcast = _host + "/server/v1/message/notification/broadcast" // 批量推送
// _apiStatistics = _host + "/server/v1/message/statistics" // 推送统计
_callbackURL = "https://api.bilibili.com/x/push/callback/oppo"
_authExpire = 24 * 60 * 60 // auth token 过期秒数
// _pushTypeAll = "1" // 推送全部设备
_pushTypeToken = "2" // 按token推
// ResponseCodeServiceUnavalable service unavalable
ResponseCodeServiceUnavalable = -1
// ResponseCodeSuccess http normal response code
ResponseCodeSuccess = 0
// ResponseCodeInvalidToken invalid token response code
ResponseCodeInvalidToken = 10000
// ResponseCodeUnsubscribeToken unsubscribe token
ResponseCodeUnsubscribeToken = 10001
// ResponseCodeRepeatToken repeat token
ResponseCodeRepeatToken = 10004
// ActionTypeInner 打开应⽤内⻚activity的intentaction
ActionTypeInner = 1
)
// Message message content.
type Message struct {
Title string `json:"title"`
Content string `json:"content"`
ActionType int `json:"click_action_type"` // 0:启动应⽤; 1:打开应⽤内⻚(activity的intentaction); 2:打开⽹⻚; 4:打开应⽤内⻚(activity); [⾮必填默认值为0]
ActionActivity string `json:"click_action_activity"` // 应⽤内⻚地址【click_action_type 为1或4时必填⻓度500】
ActionURL string `json:"click_action_url"` // ⽹⻚地址【click_action_type为2 必填⻓度500】
ActionParams string `json:"action_parameters"` // 传递给应⽤的参数,json格式
OfflineTTL int `json:"off_line_ttl"` // 离线消息的存活时间 (默认3600s) (单位:秒), 【off_line值为true时必填最 ⻓10天】
CallbackURL string `json:"call_back_url"` // 应⽤接收消息到达回执的回调(仅⽀持registrationId或aliasName 两种推送⽅式)
}
// Response push response.
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data struct {
MsgID string `json:"message_id,omitempty"`
TaskID string `json:"task_id,omitempty"`
TokenInvalid []string `json:"10000"`
TokenUnsubscribe []string `json:"10001"`
TokenRepeat []string `json:"10004"`
} `json:"data"`
}
// Callback oppo callback.
type Callback struct {
MsgID string `json:"messageId"`
TaskID string `json:"taskId"`
Tokens string `json:"registrationIds"` // regId1, regid2
EventType string `json:"eventType"` // push_arrive
}
// CallbackURL gets callback URL.
func CallbackURL(app int64, task string) string {
return fmt.Sprintf("%s?app=%d&task=%s", _callbackURL, app, task)
}