Skip to content

Commit e6471c9

Browse files
committed
init
0 parents  commit e6471c9

9 files changed

+456
-0
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea

Diff for: LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 geek-go
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Diff for: README.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# hwpush
2+
华为push
3+
4+
由于官方没有推出Go版本的推送SDK,故自己实现了。支持:
5+
6+
- 按cid单推
7+
- 按cid群推
8+
9+
全推不支持,官方接口没有提供全推。
10+
11+
12+
## 使用
13+
14+
安装:
15+
``` bash
16+
go get https://github.com/geek-go/hwpush
17+
```
18+
19+
SDK 测试(使用前先打开`hwpush_test.go`配置appid等参数):
20+
``` bash
21+
# 测试单推
22+
go test -v -run="^TestHwPush_SendByCid$"
23+
24+
# 测试群推
25+
go test -v -run="^TestHwPush_SendByCids$"
26+
```
27+
28+
其它例子参照 `hwpush_test.go` 的调用。
29+
30+
> 测试用例里针对SDK进行了一些封装,大家可以参考快速实现。
31+
32+
## 如何参与该项目
33+
34+
如果需要增加个推其它接口的实现,请参考`push_send`实现。规范:
35+
36+
- 每个接口对应一个文件
37+
- 每个文件均包含接口请求结构体、接口响应结构体、接口调用的实现
38+
- 增加测试用例
39+
- 注意避免过度封装,以免使用者困惑

Diff for: const.go

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package huawei
2+
3+
const (
4+
TOKEN_URL = "https://login.cloud.huawei.com/oauth2/v2/token"
5+
PUSH_URL = "https://api.push.hicloud.com/pushsend.do"
6+
)
7+
8+
//流控:https://club.huawei.com/thread-16930815-1-2.html
9+
//Push SDK-服务端常见问题 https://club.huawei.com/thread-10204980-1-1.html

Diff for: httpclient.go

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package huawei
2+
3+
import (
4+
"io/ioutil"
5+
"net/http"
6+
"net/url"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
const (
12+
NSP_STATUS_6 = 6
13+
NSP_STATUS_102 = 102
14+
NSP_STATUS_105 = 105
15+
NSP_STATUS_111 = 111
16+
NSP_STATUS_112 = 112
17+
NSP_STATUS_113 = 113
18+
NSP_STATUS_114 = 114
19+
NSP_STATUS_199 = 199
20+
NSP_STATUS_403 = 403
21+
)
22+
23+
var NSP_STATUS_MSG = map[int]string{
24+
NSP_STATUS_6: "session过期",
25+
NSP_STATUS_102: "无效的SESSION_KEY",
26+
NSP_STATUS_105: "参数错误",
27+
NSP_STATUS_111: "系统、服务处理忙",
28+
NSP_STATUS_112: "找不到对应服务",
29+
NSP_STATUS_113: "请求服务失败",
30+
NSP_STATUS_114: "服务不可达、无路由",
31+
NSP_STATUS_199: "未知错误",
32+
NSP_STATUS_403: "无权限",
33+
}
34+
35+
//对方业务异常
36+
type BizErr struct {
37+
Errno int `json:"errno"`
38+
Errmsg string `json:"errmsg"`
39+
}
40+
41+
func NewBizErr(errno int, errmsg string) *BizErr {
42+
return &BizErr{
43+
Errno: errno,
44+
Errmsg: errmsg,
45+
}
46+
}
47+
48+
//post请求
49+
func SendFormPost(url string, data url.Values) ([]byte, error, *BizErr) {
50+
body := ioutil.NopCloser(strings.NewReader(data.Encode()))
51+
response, err := http.Post(url, "application/x-www-form-urlencoded", body)
52+
if err != nil {
53+
54+
return []byte(""), err, nil
55+
}
56+
defer response.Body.Close()
57+
58+
//HTTP协议错误码处理
59+
if response.StatusCode == 500 {
60+
return []byte(""), nil, NewBizErr(response.StatusCode, "推送服务器系统错误")
61+
}
62+
63+
if response.StatusCode == 503 {
64+
return []byte(""), nil, NewBizErr(response.StatusCode, "流量控制错误,发送流量过高")
65+
}
66+
67+
//系统级错误码(通过扩展HTTP协议头 NSP_STATUS)
68+
NSP_STATUS := response.Header.Get("NSP_STATUS")
69+
NSP_STATUS_INT, _ := strconv.Atoi(NSP_STATUS)
70+
if NSP_STATUS_INT != 0 {
71+
return []byte(""), nil, NewBizErr(NSP_STATUS_INT, NSP_STATUS_MSG[NSP_STATUS_INT])
72+
}
73+
74+
result, err := ioutil.ReadAll(response.Body)
75+
if err != nil {
76+
return []byte(""), err, nil
77+
}
78+
return result, err, nil
79+
}

Diff for: hwpush_test.go

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package huawei
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"testing"
9+
)
10+
11+
var Cfg = &HwpushConfig{
12+
AppId: "",
13+
AppSecret: "",
14+
Package: "",
15+
}
16+
17+
//测试单推
18+
func TestHwPush_SendByCid(t *testing.T) {
19+
igetui, err := NewHuawei(Cfg)
20+
if err != nil {
21+
t.Error(err)
22+
os.Exit(1)
23+
}
24+
25+
cid := "xxx"
26+
payLoad := Payload{"这是测试title", "这是测试内容", "1", ""}
27+
err = igetui.SendByCid(cid, &payLoad)
28+
if err != nil {
29+
t.Error(err)
30+
} else {
31+
t.Log("ok")
32+
}
33+
}
34+
35+
//测试群推
36+
func TestHwPush_SendByCids(t *testing.T) {
37+
igetui, err := NewHuawei(Cfg)
38+
if err != nil {
39+
t.Error(err)
40+
os.Exit(1)
41+
}
42+
43+
cids := []string{"xxx"}
44+
payLoad := Payload{"这是测试title", "这是测试内容", "1", ""}
45+
err = igetui.SendByCids(cids, &payLoad)
46+
if err != nil {
47+
t.Error(err)
48+
} else {
49+
t.Log("ok")
50+
}
51+
}
52+
53+
type HwpushConfig struct {
54+
AppId string `toml:"app_id"`
55+
AppSecret string `toml:"app_secret"`
56+
Package string `toml:"package"`
57+
}
58+
59+
//消息payload,根据业务自定义
60+
type Payload struct {
61+
PushTitle string `json:"push_title"`
62+
PushBody string `json:"push_body"`
63+
IsShowNotify string `json:"is_show_notify"`
64+
Ext string `json:"ext"`
65+
}
66+
67+
type HuaweiPush struct {
68+
Config *HwpushConfig
69+
}
70+
71+
//存储每个cid失败的原因
72+
type PushSendResultMsg struct {
73+
Success int64 `json:"success"`
74+
Failure int64 `json:"failure"`
75+
IllegalTokens []string `json:"illegal_tokens"`
76+
}
77+
78+
//获取实例
79+
func NewHuawei(config *HwpushConfig) (*HuaweiPush, error) {
80+
81+
if config.AppId == "" || config.Package == "" || config.AppSecret == "" {
82+
return nil, errors.New("请检查配置")
83+
}
84+
85+
hwpush := &HuaweiPush{
86+
Config: config,
87+
}
88+
89+
return hwpush, nil
90+
}
91+
92+
//根据用户cid推送
93+
func (h *HuaweiPush) SendByCid(cid string, payload *Payload) error {
94+
cids := []string{cid}
95+
return h.SendByCids(cids, payload)
96+
}
97+
98+
//组装消息体
99+
func NewMessage(appPkgName string, payload *Payload) *Message {
100+
101+
msgType := 1 //默认透传
102+
if payload.IsShowNotify == "1" { //通知栏
103+
msgType = 3
104+
}
105+
106+
payload_str, _ := json.Marshal(payload)
107+
108+
return &Message{
109+
Hps: Hps{
110+
Msg: Msg{
111+
Type: msgType, //1, 透传异步消息; 3, 系统通知栏异步消息。注意:2和4以后为保留后续扩展使用
112+
Body: Body{
113+
Content: payload.PushBody,
114+
Title: payload.PushTitle,
115+
},
116+
Action: Action{
117+
Type: 3, //1, 自定义行为; 2, 打开URL; 3, 打开App;
118+
Param: Param{
119+
AppPkgName: appPkgName,
120+
},
121+
},
122+
},
123+
Ext: Ext{
124+
Customize: []string{string(payload_str)},
125+
},
126+
},
127+
}
128+
}
129+
130+
//根据用户cids批量推送
131+
func (h *HuaweiPush) SendByCids(cids []string, payload *Payload) error {
132+
133+
//nspCtx
134+
vers := &Vers{
135+
Ver: "1",
136+
AppID: h.Config.AppId,
137+
}
138+
nspCtx, _ := json.Marshal(vers)
139+
140+
//hps
141+
hps, err := json.Marshal(NewMessage(h.Config.Package, payload))
142+
if err != nil {
143+
return err
144+
}
145+
146+
deviceToken, err := json.Marshal(cids)
147+
if err != nil {
148+
return err
149+
}
150+
151+
pushSendParam := &PushSendParam{
152+
DeviceToken: string(deviceToken),
153+
NspCtx: string(nspCtx),
154+
Payload: string(hps),
155+
}
156+
157+
pushSendResult, err, bizErr := PushSend(h.Config.AppId, h.Config.AppSecret, pushSendParam)
158+
if err != nil {
159+
return err
160+
}
161+
162+
fmt.Println(pushSendResult, bizErr)
163+
164+
return nil
165+
}

Diff for: message.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package huawei
2+
3+
type Message struct {
4+
Hps Hps `json:"hps"`
5+
}
6+
7+
//华为Push消息总结构体
8+
type Hps struct {
9+
Msg Msg `json:"msg"`
10+
Ext Ext `json:"ext,omitempty"`
11+
}
12+
13+
//PUSH消息定义。包括:消息类型type、消息内容body、消息动作action
14+
type Msg struct {
15+
Type int `json:"type"` //1, 透传异步消息; 3, 系统通知栏异步消息。注意:2和4以后为保留后续扩展使用
16+
Body Body `json:"body"`
17+
Action Action `json:"action,omitempty"`
18+
}
19+
20+
//消息内容。注意:对于透传类的消息可以是字符串,不必是JSON Object。
21+
type Body struct {
22+
Content string `json:"content"` //消息内容体
23+
Title string `json:"title"` //消息标题
24+
}
25+
26+
//消息点击动作
27+
type Action struct {
28+
Type int `json:"type,omitempty"` //1 自定义行为:行为由参数intent定义;2 打开URL:URL地址由参数url定义;3 打开APP:默认值,打开App的首页。注意:富媒体消息开放API不支持。
29+
Param Param `json:"param,omitempty"`
30+
}
31+
32+
//关于消息点击动作的参数
33+
type Param struct {
34+
Intent string `json:"intent,omitempty"` //Action的type为1的时候表示自定义行为。
35+
Url string `json:"url,omitempty"` //Action的type为2的时候表示打开URL地址
36+
AppPkgName string `json:"appPkgName"` //需要拉起的应用包名,必须和注册推送的包名一致。
37+
}
38+
39+
//扩展信息,含BI消息统计,特定展示风格,消息折叠
40+
type Ext struct {
41+
BadgeAddNum string `json:"badgeAddNum,omitempty"` //设置应用角标数值,取值范围1-99。
42+
BadgeClass string `json:"badgeClass,omitempty"` //桌面图标对应的应用入口Activity类。
43+
BiTag string `json:"biTag,omitempty"` //设置消息标签,如果带了这个标签,会在回执中推送给CP用于检测某种类型消息的到达率和状态。
44+
Customize []string `json:"customize,omitempty"` //用于触发onEvent点击事件,扩展样例:[{"season":"Spring"},{"weather":"raining"}] 。说明:这个字段类型必须是JSON Array,里面是key-value的一组扩展信息。
45+
}

0 commit comments

Comments
 (0)