作者 yangfu

v0.0.3 feature multi channel support

  1 +## 推送调用说明
  2 +
  3 +### 服务端调用
  4 +
  5 +服务端 project_key 是 slave_key
  6 +
  7 +存在子项目:
  8 +
  9 +主项目键值 ability
  10 +
  11 +子项目键值 worth
  12 +
  13 +那么价值服务端调用推送接口,传入的project_key是worth
  14 +
  15 +
  16 +
  17 +不存在子项目:
  18 +
  19 +主项目键值 mmm.suplus.orders
  20 +
  21 +子项目键值 -
  22 +
  23 +那么订单服务端(消息中心)调用推送接口,传入的project_key是mmm.suplus.orders
  24 +
  25 +### 客户端调用
  26 +
  27 +app更新设备信息 project_key 是 master_key
  28 +
  29 +例如:
  30 +
  31 +同上,因为客户端主项目里面包含子项目,所以更新设备信息的时候传入主项目的编码,即为 ability,如果一个app只有一个项目 如:mmm.suplus.orders
@@ -4,14 +4,18 @@ log_level = "${LOG_LEVEL||debug}" @@ -4,14 +4,18 @@ log_level = "${LOG_LEVEL||debug}"
4 aliyun_logs_access ="${aliyun_logs_access||F:/log/app.log}" 4 aliyun_logs_access ="${aliyun_logs_access||F:/log/app.log}"
5 5
6 6
7 -#阿里云基础配置  
8 -AccessKeyID ="LTAI4FhiZ3UktC6N1u3H5GFC"  
9 -AccessKeySecret ="UyspWwdni55CYQ02hUCint4qY2jNYO" 7 +#个人阿里云基础配置
  8 +#AccessKeyID ="LTAI4FhiZ3UktC6N1u3H5GFC"
  9 +#AccessKeySecret ="UyspWwdni55CYQ02hUCint4qY2jNYO"
  10 +#OssEndPoint ="oss-cn-shanghai.aliyuncs.com"
  11 +#BuckName ="mmm-vod-dev-public"
10 cname ="https://media.goexample.live/" 12 cname ="https://media.goexample.live/"
11 13
12 -OssEndPoint ="oss-cn-shanghai.aliyuncs.com"  
13 -BuckName ="mmm-vod-dev-public"  
14 - 14 +#公司
  15 +AccessKeyID ="LTAI4Fz1LUBW2fXp6QWaJHRS"
  16 +AccessKeySecret ="aLZXwK8pgrs10Ws03qcN7NsrSXFVsg"
  17 +OssEndPoint ="oss-cn-shenzhen.aliyuncs.com"
  18 +BuckName ="timeless-world"
15 19
16 #友盟推送 20 #友盟推送
17 UMENG_API_HOST = "http://msg.umeng.com" 21 UMENG_API_HOST = "http://msg.umeng.com"
1 [dev_online] 1 [dev_online]
2 -#Ali could  
3 -AccessKeyID ="LTAI4Fz1LUBW2fXp6QWaJHRS"  
4 -AccessKeySecret ="aLZXwK8pgrs10Ws03qcN7NsrSXFVsg"  
5 2
6 #日志 3 #日志
7 log_level = "${LOG_LEVEL||debug}" 4 log_level = "${LOG_LEVEL||debug}"
8 aliyun_logs_access ="${aliyun_logs_access||F:/log/app.log}" 5 aliyun_logs_access ="${aliyun_logs_access||F:/log/app.log}"
9 6
  7 +#AccessKeyID ="LTAI4FhiZ3UktC6N1u3H5GFC"
  8 +#AccessKeySecret ="UyspWwdni55CYQ02hUCint4qY2jNYO"
  9 +#OssEndPoint ="oss-cn-shanghai.aliyuncs.com"
  10 +#BuckName ="mmm-vod-dev-public"
  11 +
10 #阿里云 12 #阿里云
  13 +AccessKeyID ="LTAI4Fz1LUBW2fXp6QWaJHRS"
  14 +AccessKeySecret ="aLZXwK8pgrs10Ws03qcN7NsrSXFVsg"
11 cname ="https://media.fjmaimaimai.com/" 15 cname ="https://media.fjmaimaimai.com/"
  16 +OssEndPoint ="oss-cn-shenzhen.aliyuncs.com"
  17 +BuckName ="timeless-world"
12 18
13 #数据库相关 19 #数据库相关
14 MYSQL_USER = "${MYSQL_USER||root}" 20 MYSQL_USER = "${MYSQL_USER||root}"
@@ -9,6 +9,8 @@ AccessKeySecret ="aLZXwK8pgrs10Ws03qcN7NsrSXFVsg" @@ -9,6 +9,8 @@ AccessKeySecret ="aLZXwK8pgrs10Ws03qcN7NsrSXFVsg"
9 9
10 #阿里云 10 #阿里云
11 cname ="https://media.fjmaimaimai.com/" 11 cname ="https://media.fjmaimaimai.com/"
  12 +OssEndPoint ="oss-cn-shenzhen.aliyuncs.com"
  13 +BuckName ="timeless-world"
12 14
13 #数据库相关 15 #数据库相关
14 MYSQL_USER = "${MYSQL_USER||root}" 16 MYSQL_USER = "${MYSQL_USER||root}"
@@ -9,6 +9,8 @@ AccessKeySecret ="aLZXwK8pgrs10Ws03qcN7NsrSXFVsg" @@ -9,6 +9,8 @@ AccessKeySecret ="aLZXwK8pgrs10Ws03qcN7NsrSXFVsg"
9 9
10 #阿里云 10 #阿里云
11 cname ="https://media.fjmaimaimai.com/" 11 cname ="https://media.fjmaimaimai.com/"
  12 +OssEndPoint ="oss-cn-shenzhen.aliyuncs.com"
  13 +BuckName ="timeless-world"
12 14
13 #数据库相关 15 #数据库相关
14 MYSQL_USER = "${MYSQL_USER||root1}" 16 MYSQL_USER = "${MYSQL_USER||root1}"
@@ -85,5 +85,5 @@ func CreateStsAuth(header *protocol.RequestHeader, files []string) (rsp interfac @@ -85,5 +85,5 @@ func CreateStsAuth(header *protocol.RequestHeader, files []string) (rsp interfac
85 FileName: fileBase, 85 FileName: fileBase,
86 }) 86 })
87 } 87 }
88 - return map[string]interface{}{"access": access, "files": listPath}, nil 88 + return map[string]interface{}{"certificate": access, "files": listPath}, nil
89 } 89 }
@@ -5,7 +5,7 @@ import ( @@ -5,7 +5,7 @@ import (
5 "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log" 5 "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log"
6 protocol "openapi/pkg/domain" 6 protocol "openapi/pkg/domain"
7 "openapi/pkg/infrastructure/push" 7 "openapi/pkg/infrastructure/push"
8 - "openapi/pkg/infrastructure/push/getui" 8 + getui "openapi/pkg/infrastructure/push/getuiV2"
9 "openapi/pkg/infrastructure/repository" 9 "openapi/pkg/infrastructure/repository"
10 "openapi/pkg/infrastructure/utils" 10 "openapi/pkg/infrastructure/utils"
11 ) 11 )
@@ -47,6 +47,9 @@ func Notification(header *protocol.RequestHeader, request *protocol.PushInfoRequ @@ -47,6 +47,9 @@ func Notification(header *protocol.RequestHeader, request *protocol.PushInfoRequ
47 err = nil 47 err = nil
48 return 48 return
49 } 49 }
  50 + if extInfo, ok := appInfo.GetExtInfo(); ok {
  51 + requestOriginal.Ext["intent"] = extInfo.Intent
  52 + }
50 if len(deviceList) == 0 { 53 if len(deviceList) == 0 {
51 err = protocol.NewSuccessWithMessage(fmt.Sprintf("接收人:%v 未查询到注册的设备信息!", request.Receivers)) 54 err = protocol.NewSuccessWithMessage(fmt.Sprintf("接收人:%v 未查询到注册的设备信息!", request.Receivers))
52 return 55 return
@@ -81,13 +84,16 @@ func NotificationOriginal(header *protocol.RequestHeader, request *protocol.Push @@ -81,13 +84,16 @@ func NotificationOriginal(header *protocol.RequestHeader, request *protocol.Push
81 84
82 push.Title(request.Title), 85 push.Title(request.Title),
83 push.Content(request.Content), 86 push.Content(request.Content),
84 - //push.TransmissionContent(utils.JsonAssertString(request.Ext)), 87 + push.Extra(request.Ext),
85 } 88 }
86 ) 89 )
87 rsp = &protocol.PushInfoResponse{} 90 rsp = &protocol.PushInfoResponse{}
88 if v, ok := request.Ext["transData"]; ok { 91 if v, ok := request.Ext["transData"]; ok {
89 options = append(options, push.TransmissionContent(utils.JsonAssertString(v))) 92 options = append(options, push.TransmissionContent(utils.JsonAssertString(v)))
90 } 93 }
  94 + if v, ok := request.Ext["intent"]; ok {
  95 + options = append(options, push.Intent(v.(string)))
  96 + }
91 clientIds = request.ClientIdList 97 clientIds = request.ClientIdList
92 switch len(clientIds) { 98 switch len(clientIds) {
93 case 0: 99 case 0:
@@ -142,7 +148,7 @@ func UpdateDevice(header *protocol.RequestHeader, request *protocol.UpdateDevice @@ -142,7 +148,7 @@ func UpdateDevice(header *protocol.RequestHeader, request *protocol.UpdateDevice
142 log.Error(err) 148 log.Error(err)
143 return 149 return
144 } 150 }
145 - if err = rep.UpdateDevice(request.Muid, request.ClientId, request.DeviceToken, request.ProjectKey); err != nil { 151 + if err = rep.UpdateDevice(request.Muid, request.ClientId, request.DeviceToken, request.ProjectKey, request.Phone); err != nil {
146 log.Error(err) 152 log.Error(err)
147 } 153 }
148 err = protocol.NewSuccessWithMessage("更新成功") 154 err = protocol.NewSuccessWithMessage("更新成功")
  1 +package constant
  2 +
  3 +var (
  4 + // 主项目 键值
  5 + DefaultProjectKey = "mmm.ability"
  6 + // 子项目 键值
  7 + DefaultSlaveProjectKey = "mmm.ability.worth"
  8 +)
@@ -5,6 +5,7 @@ var ( @@ -5,6 +5,7 @@ var (
5 AccessKeyID string = "LTAI4FhiZ3UktC6N1u3H5GFC" 5 AccessKeyID string = "LTAI4FhiZ3UktC6N1u3H5GFC"
6 AccessKeySecret string = "UyspWwdni55CYQ02hUCint4qY2jNYO" 6 AccessKeySecret string = "UyspWwdni55CYQ02hUCint4qY2jNYO"
7 CName string = "https://media.goexample.live/" 7 CName string = "https://media.goexample.live/"
  8 + RoleArn string = "acs:ram::1777936936207896:role/ossclientsts" //"acs:ram::1373671070046453:role/role-oss-sts"
8 ) 9 )
9 10
10 func init() { 11 func init() {
1 package domain 1 package domain
2 2
  3 +import (
  4 + "encoding/json"
  5 + "openapi/pkg/infrastructure/log"
  6 +)
  7 +
3 /*PushInfo 推送信息*/ 8 /*PushInfo 推送信息*/
4 type PushInfoOriginalRequest struct { 9 type PushInfoOriginalRequest struct {
5 Type int `json:"msgType"` 10 Type int `json:"msgType"`
@@ -35,6 +40,7 @@ type UpdateDeviceRequest struct { @@ -35,6 +40,7 @@ type UpdateDeviceRequest struct {
35 Muid int64 `json:"muid" valid:"Required;"` //企业平台中的用户 UID 40 Muid int64 `json:"muid" valid:"Required;"` //企业平台中的用户 UID
36 ClientId string `json:"clientId" valid:"Required"` 41 ClientId string `json:"clientId" valid:"Required"`
37 DeviceToken string `json:"deviceToken"` 42 DeviceToken string `json:"deviceToken"`
  43 + Phone string `json:"phone"`
38 } 44 }
39 type UpdateDeviceResponse struct { 45 type UpdateDeviceResponse struct {
40 } 46 }
@@ -54,4 +60,21 @@ type AppInfo struct { @@ -54,4 +60,21 @@ type AppInfo struct {
54 AppMasterSecret string 60 AppMasterSecret string
55 AppId string 61 AppId string
56 ProjectId int //项目编号 62 ProjectId int //项目编号
  63 + ExtInfo string
  64 +}
  65 +
  66 +type ExtInfo struct {
  67 + Intent string `json:"intent"`
  68 +}
  69 +
  70 +func (t *AppInfo) GetExtInfo() (*ExtInfo, bool) {
  71 + extInfo := &ExtInfo{}
  72 + if len(t.ExtInfo) == 0 {
  73 + return nil, false
  74 + }
  75 + if err := json.Unmarshal([]byte(t.ExtInfo), extInfo); err != nil {
  76 + log.Error(err.Error())
  77 + return nil, false
  78 + }
  79 + return extInfo, true
57 } 80 }
1 package aliyun 1 package aliyun
2 2
3 import ( 3 import (
4 - "fmt"  
5 "github.com/aliyun/alibaba-cloud-sdk-go/services/sts" 4 "github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
6 "openapi/pkg/constant" 5 "openapi/pkg/constant"
7 "openapi/pkg/infrastructure/log" 6 "openapi/pkg/infrastructure/log"
8 ) 7 )
9 8
  9 +/*
  10 + 设置参数。 指定角色的ARN。格式:acs:ram::$accountID:role/$roleName/$RoleSessionName 。 mmm-go@1373671070046453.onaliyun.com
  11 + 配置用户 - ram角色权限-oss授权
  12 + $accountID 用户的阿里云账号
  13 + $RoleArn 点到ram角色 具体的权限详情 格式 格式:acs:ram::$accountID:role/$roleName/$RoleSessionName
  14 +
  15 + 1.使用授权账号(mmm-go@1373671070046453.onaliyun.com)登录
  16 + 2.RAM访问控制->RAM角色管理->添加权限->为权限设置 oss访问权限
  17 + 3.查看角色详情-》ARN( acs:ram::1373671070046453:role/role-oss-sts )需要配置给程序使用
  18 + 4.RAM访问控制->用户(mmm-go@1373671070046453.onaliyun.com)->给用户设置角色
  19 +*/
  20 +
  21 +/*
  22 + 1.前端sts上传需要设置bucket跨域,bucket->权限管理->跨域设置
  23 +*/
10 func DefaultSts() (interface{}, error) { 24 func DefaultSts() (interface{}, error) {
11 //构建一个阿里云客户端, 用于发起请求。 25 //构建一个阿里云客户端, 用于发起请求。
12 //构建阿里云客户端时,需要设置AccessKey ID和AccessKey Secret。 26 //构建阿里云客户端时,需要设置AccessKey ID和AccessKey Secret。
@@ -18,12 +32,7 @@ func DefaultSts() (interface{}, error) { @@ -18,12 +32,7 @@ func DefaultSts() (interface{}, error) {
18 //构建请求对象。 32 //构建请求对象。
19 request := sts.CreateAssumeRoleRequest() 33 request := sts.CreateAssumeRoleRequest()
20 request.Scheme = "https" 34 request.Scheme = "https"
21 -  
22 - //设置参数。 指定角色的ARN。格式:acs:ram::$accountID:role/$roleName/$RoleSessionName 。 mmm-go@1373671070046453.onaliyun.com  
23 - // 配置用户 - ram角色权限-oss授权  
24 - // $accountID 用户的阿里云账号  
25 - // $RoleArn 点到ram角色 具体的权限详情 格式 格式:acs:ram::$accountID:role/$roleName/$RoleSessionName  
26 - request.RoleArn = fmt.Sprintf("acs:ram::1373671070046453:role/role-oss-sts") 35 + request.RoleArn = constant.RoleArn
27 // 会话名称 36 // 会话名称
28 request.RoleSessionName = "role-oss-sts-session" 37 request.RoleSessionName = "role-oss-sts-session"
29 38
@@ -38,5 +47,7 @@ func DefaultSts() (interface{}, error) { @@ -38,5 +47,7 @@ func DefaultSts() (interface{}, error) {
38 "expiration": response.Credentials.Expiration, 47 "expiration": response.Credentials.Expiration,
39 "accessKeyId": response.Credentials.AccessKeyId, 48 "accessKeyId": response.Credentials.AccessKeyId,
40 "securityToken": response.Credentials.SecurityToken, 49 "securityToken": response.Credentials.SecurityToken,
  50 + "bucket": constant.BuckName,
  51 + "endpoint": constant.OssEndPoint,
41 }, nil 52 }, nil
42 } 53 }
@@ -6,7 +6,7 @@ import ( @@ -6,7 +6,7 @@ import (
6 _ "github.com/go-sql-driver/mysql" 6 _ "github.com/go-sql-driver/mysql"
7 "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log" 7 "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log"
8 "openapi/pkg/constant" 8 "openapi/pkg/constant"
9 - _ "openapi/pkg/infrastructure/bgorm/model" 9 + _ "openapi/pkg/infrastructure/bgorm/models"
10 ) 10 )
11 11
12 func init() { 12 func init() {
@@ -15,6 +15,7 @@ type PushAppInfo struct { @@ -15,6 +15,7 @@ type PushAppInfo struct {
15 AppMasterSecret string `orm:"column(app_master_secret);size(255);null" description:"推送服务端密钥"` 15 AppMasterSecret string `orm:"column(app_master_secret);size(255);null" description:"推送服务端密钥"`
16 AppId string `orm:"column(app_id);size(255);null" description:"推送应用编号"` 16 AppId string `orm:"column(app_id);size(255);null" description:"推送应用编号"`
17 ProjectId int `orm:"column(project_id);size(255);null" description:"项目编号"` 17 ProjectId int `orm:"column(project_id);size(255);null" description:"项目编号"`
  18 + ExtInfo string `orm:"column(ext_info);size(1024);null" description:"扩展信息"`
18 } 19 }
19 20
20 func (t *PushAppInfo) TableName() string { 21 func (t *PushAppInfo) TableName() string {
@@ -9,6 +9,7 @@ import ( @@ -9,6 +9,7 @@ import (
9 type PushDeviceInfo struct { 9 type PushDeviceInfo struct {
10 Id int `orm:"column(id);auto"` 10 Id int `orm:"column(id);auto"`
11 Uid int64 `orm:"column(uid);null" description:"企业平台用户id (muid)"` 11 Uid int64 `orm:"column(uid);null" description:"企业平台用户id (muid)"`
  12 + Phone string `orm:"column(phone);size(100);null" description:"设备手机号"`
12 ClientId string `orm:"column(client_id);size(100);null" description:"设备识别码 推送标识"` 13 ClientId string `orm:"column(client_id);size(100);null" description:"设备识别码 推送标识"`
13 DeviceToken string `orm:"column(device_token);size(100);null" description:"设备识别码 推送标识"` 14 DeviceToken string `orm:"column(device_token);size(100);null" description:"设备识别码 推送标识"`
14 CreateAt time.Time `orm:"column(create_at);type(timestamp);null" description:"创建时间"` 15 CreateAt time.Time `orm:"column(create_at);type(timestamp);null" description:"创建时间"`
@@ -51,6 +51,7 @@ type Message struct { @@ -51,6 +51,7 @@ type Message struct {
51 AppKey string `json:"appkey"` 51 AppKey string `json:"appkey"`
52 IsOffline bool `json:"is_offline"` 52 IsOffline bool `json:"is_offline"`
53 MsgType string `json:"msgtype"` 53 MsgType string `json:"msgtype"`
  54 + OfflineExpireTime int `json:"offline_expire_time"` //offline_expire_time
54 } 55 }
55 56
56 //透传 57 //透传
@@ -59,6 +60,7 @@ type Transmission struct { @@ -59,6 +60,7 @@ type Transmission struct {
59 TransmissionContent string `json:"transmission_content,omitempty"` //透传内容 60 TransmissionContent string `json:"transmission_content,omitempty"` //透传内容
60 DurationBegin string `json:"duration_begin,omitempty"` 61 DurationBegin string `json:"duration_begin,omitempty"`
61 DurationEnd string `json:"duration_end,omitempty"` 62 DurationEnd string `json:"duration_end,omitempty"`
  63 + Notify interface{} `json:"notify,omitempty"`
62 } 64 }
63 65
64 func (o *Transmission) SetTransmissionType(t bool) { 66 func (o *Transmission) SetTransmissionType(t bool) {
@@ -71,6 +73,27 @@ func (o *Transmission) SetDuration(begin, end string) { @@ -71,6 +73,27 @@ func (o *Transmission) SetDuration(begin, end string) {
71 o.DurationBegin = begin 73 o.DurationBegin = begin
72 o.DurationEnd = end 74 o.DurationEnd = end
73 } 75 }
  76 +func (o *Transmission) SetNotify(options *push.Options) {
  77 + mapNotify := make(map[string]interface{})
  78 + mapNotify["title"] = options.Title
  79 + mapNotify["content"] = options.Content
  80 + // 安卓设备生成
  81 + mapNotify["intent"] = options.Intent
  82 + mapNotify["type"] = 1
  83 + //mapNotify["extKVList"] = []ExtKVList{
  84 + // //{Constrains: "HW", Key: "/message/android/notification/badge/add_num", Value: 1},
  85 + // //{Constrains: "HW", Key: "/message/android/notification/badge/class", Value: `"com.getui.demo.GetuiSdkDemoActivity"`},
  86 + // // 小米厂家支持
  87 + // {Constrains: "XM", Key: "channel", Value: "\"2882303761518034255\""},//Default 2882303761518034255
  88 + //}
  89 + o.Notify = mapNotify
  90 +}
  91 +
  92 +type ExtKVList struct {
  93 + Constrains string `json:"constrains"`
  94 + Key string `json:"key"`
  95 + Value interface{} `json:"value"`
  96 +}
74 97
75 func NewTemplate(options *push.Options) *Template { 98 func NewTemplate(options *push.Options) *Template {
76 return &Template{ 99 return &Template{
@@ -87,6 +110,7 @@ func NewTransmission(options *push.Options) *Transmission { @@ -87,6 +110,7 @@ func NewTransmission(options *push.Options) *Transmission {
87 } 110 }
88 t.SetTransmissionType(false) 111 t.SetTransmissionType(false)
89 t.SetTransmissionContent(options.TransmissionContent) 112 t.SetTransmissionContent(options.TransmissionContent)
  113 + t.SetNotify(options)
90 return t 114 return t
91 } 115 }
92 func NewMessage(options *push.Options) *Message { 116 func NewMessage(options *push.Options) *Message {
@@ -94,6 +118,7 @@ func NewMessage(options *push.Options) *Message { @@ -94,6 +118,7 @@ func NewMessage(options *push.Options) *Message {
94 AppKey: options.AppKey, 118 AppKey: options.AppKey,
95 IsOffline: true, 119 IsOffline: true,
96 MsgType: resolveMsgType(options.MsgType), 120 MsgType: resolveMsgType(options.MsgType),
  121 + OfflineExpireTime: 100000000,
97 } 122 }
98 } 123 }
99 func resolveMsgType(msgType int) string { 124 func resolveMsgType(msgType int) string {
@@ -168,6 +193,10 @@ func NewAps(options *push.Options) (v *Aps) { @@ -168,6 +193,10 @@ func NewAps(options *push.Options) (v *Aps) {
168 ContentAvailable: 0, 193 ContentAvailable: 0,
169 Sound: "default", 194 Sound: "default",
170 } 195 }
  196 + // 声音
  197 + if value, ok := options.GetExt("sound"); ok {
  198 + v.Sound = value.(string)
  199 + }
171 return 200 return
172 } 201 }
173 func NewAlert(options *push.Options) (v *Alert) { 202 func NewAlert(options *push.Options) (v *Alert) {
@@ -186,7 +215,7 @@ type Aps struct { @@ -186,7 +215,7 @@ type Aps struct {
186 Alert *Alert `json:"alert"` 215 Alert *Alert `json:"alert"`
187 AutoBadge string `json:"autoBadge"` //用于计算应用上面未读数字 216 AutoBadge string `json:"autoBadge"` //用于计算应用上面未读数字
188 ContentAvailable int `json:"content-available,omitempty"` //推送直接带有透传数据 0:有通知栏消息 1:无通知栏消息 217 ContentAvailable int `json:"content-available,omitempty"` //推送直接带有透传数据 0:有通知栏消息 1:无通知栏消息
189 - Sound string `json:"sound"` 218 + Sound string `json:"sound"` // 推送声音 storein_voice.mp3
190 } 219 }
191 type Alert struct { 220 type Alert struct {
192 Title string `json:"title"` 221 Title string `json:"title"`
  1 +package getuiV2
  2 +
  3 +import (
  4 + "crypto/sha256"
  5 + "fmt"
  6 + "github.com/astaxie/beego/httplib"
  7 + "gitlab.fjmaimaimai.com/mmm-go/gocomm/pkg/log"
  8 + "openapi/pkg/infrastructure/push"
  9 + "openapi/pkg/infrastructure/utils"
  10 + "strings"
  11 + "sync"
  12 + "time"
  13 +)
  14 +
  15 +const (
  16 + host = "https://restapi.getui.com"
  17 + pushSingle = "push/single/cid"
  18 + saveListBody = "push/list/message"
  19 + pushList = "push/list/cid"
  20 + authSign = "auth"
  21 +)
  22 +
  23 +var (
  24 + authtoken = ""
  25 + expire time.Time
  26 + authMux sync.RWMutex
  27 + expireSpan = time.Second * 600 //token 10分钟过期
  28 +)
  29 +
  30 +const (
  31 + error_not_auth = "not_auth"
  32 +)
  33 +
  34 +//GetuiNotification 个推消息推送
  35 +type GetuiNotification struct {
  36 + Options *push.Options
  37 + Request *httplib.BeegoHTTPRequest
  38 + retry int
  39 +}
  40 +
  41 +func (notify *GetuiNotification) Init(options ...push.Option) error {
  42 + notify.Options = &push.Options{}
  43 + for _, o := range options {
  44 + o(notify.Options)
  45 + }
  46 + notify.retry = 3
  47 + return nil
  48 +}
  49 +func (notify *GetuiNotification) Send(option map[string]interface{}) (rsp map[string]interface{}, err error) {
  50 + retry := 1
  51 + for {
  52 + switch notify.Options.PushType {
  53 + case push.PushToSingle:
  54 + rsp, err = notify.pushToSingle(option)
  55 + case push.PushToList:
  56 + rsp, err = notify.pushToList(option)
  57 + default:
  58 + rsp, err = notify.pushToSingle(option)
  59 + }
  60 + if err == nil {
  61 + break
  62 + }
  63 + //重试
  64 + if err != nil && retry > notify.retry {
  65 + return
  66 + }
  67 + log.Error(fmt.Sprintf("【个推】 重试:%v 失败:%v", retry, err))
  68 + retry++
  69 + if retry > notify.retry {
  70 + break
  71 + }
  72 + }
  73 + return
  74 +}
  75 +
  76 +//pushToSingle 单推
  77 +func (notify *GetuiNotification) pushToSingle(option map[string]interface{}) (rsp map[string]interface{}, err error) {
  78 + var token string
  79 + rsp = make(map[string]interface{})
  80 + if token, err = notify.GetAuthToken(); err != nil {
  81 + return
  82 + }
  83 +
  84 + var (
  85 + result *MessageBase
  86 + url = notify.Url(notify.Options.AppId, pushSingle)
  87 + m = notify.Message(pushSingle)
  88 + )
  89 + notify.Request = httplib.Post(url)
  90 + notify.Request.Header("token", token)
  91 + notify.Request.JSONBody(m)
  92 + if err = notify.Request.ToJSON(&result); err != nil {
  93 + return
  94 + }
  95 + rsp = result.Data
  96 + notify.print(url, m, result, result)
  97 + if err = handleResult(url, result); err != nil {
  98 + return
  99 + }
  100 + return
  101 +}
  102 +
  103 +//pushToList 群推
  104 +//步骤1.获取token
  105 +//步骤2.save_list_body保存消息共同体
  106 +//步骤3.push_list
  107 +func (notify *GetuiNotification) pushToList(option map[string]interface{}) (rsp map[string]interface{}, err error) {
  108 + var (
  109 + token string
  110 + taskId string
  111 + )
  112 + rsp = make(map[string]interface{})
  113 + if token, err = notify.GetAuthToken(); err != nil {
  114 + return
  115 + }
  116 + if taskId, err = notify.saveListBody(token, option); err != nil {
  117 + return
  118 + }
  119 + var (
  120 + result *MessageBase
  121 + url = notify.Url(notify.Options.AppId, pushList)
  122 + m = NewMapData()
  123 + )
  124 + m.AddFiled("audience.cid", notify.Options.ClientIds)
  125 + m.AddFiled("taskid", taskId)
  126 + m.AddFiled("is_async", true) //是否异步发送
  127 + notify.Request = httplib.Post(url)
  128 + notify.Request.Header("token", token)
  129 + notify.Request.JSONBody(m.Data)
  130 + if err = notify.Request.ToJSON(&result); err != nil {
  131 + return
  132 + }
  133 + rsp = result.Data
  134 + notify.print(url, m, result, result)
  135 + if err = handleResult(url, result); err != nil {
  136 + return
  137 + }
  138 + return
  139 +}
  140 +
  141 +//saveListBody 保存消息共同体
  142 +func (notify *GetuiNotification) saveListBody(token string, option map[string]interface{}) (taskId string, err error) {
  143 + var (
  144 + result *MessageBase
  145 + url = notify.Url(notify.Options.AppId, saveListBody)
  146 + m = notify.Message(saveListBody)
  147 + )
  148 + notify.Request = httplib.Post(url)
  149 + notify.Request.Header("token", token)
  150 + notify.Request.JSONBody(m)
  151 + delete(m, "audience")
  152 + if err = notify.Request.ToJSON(&result); err != nil {
  153 + return
  154 + }
  155 + notify.print(url, m, result, result)
  156 + if err = handleResult(url, result); err != nil {
  157 + return
  158 + }
  159 + if id, ok := result.Data["taskid"]; ok {
  160 + taskId = id.(string)
  161 + return
  162 + }
  163 + return "", fmt.Errorf("error task id")
  164 +}
  165 +
  166 +//Message 组装消息体
  167 +func (notify *GetuiNotification) Message(method string) map[string]interface{} {
  168 + msg := NewPushMessage(notify.Options)
  169 + return msg
  170 +}
  171 +
  172 +//Url 组装请求地址
  173 +func (notify *GetuiNotification) Url(param string, method string) string {
  174 + return fmt.Sprintf("%v/v2/%v/%v", host, param, method)
  175 +}
  176 +
  177 +//GetAuthToken 获取token
  178 +func (notify *GetuiNotification) GetAuthToken() (token string, err error) {
  179 + if authtoken != "" && expire.Unix() > time.Now().Unix() {
  180 + token = authtoken
  181 + return
  182 + }
  183 +
  184 + authMux.Lock()
  185 + defer authMux.Unlock()
  186 + url := notify.Url(notify.Options.AppId, authSign)
  187 + notify.Request = httplib.Post(strings.TrimSpace(url))
  188 + req := &AuthSignRequest{
  189 + Timestamp: fmt.Sprintf("%v", time.Now().Unix()*1000), //"1589797286000",//
  190 + AppKey: notify.Options.AppKey,
  191 + }
  192 + req.Sign = sign(req.AppKey, req.Timestamp, notify.Options.AppMasterSecret)
  193 + if _, err = notify.Request.JSONBody(req); err != nil {
  194 + return
  195 + }
  196 +
  197 + var rsp *AuthSignResponse
  198 + err = notify.Request.ToJSON(&rsp)
  199 + notify.print(url, req, rsp, rsp.MessageBase)
  200 + if err != nil {
  201 + return
  202 + }
  203 + if err = handleResult(url, rsp.MessageBase); err != nil {
  204 + return
  205 + }
  206 + authtoken = rsp.Data["token"].(string)
  207 + token = rsp.Data["token"].(string)
  208 + expire = time.Now().Add(rsp.GetExpireTime(expireSpan))
  209 + log.Info(fmt.Sprintf("【个推】token:%v expire:%v", token, expire))
  210 + return
  211 +}
  212 +
  213 +//打印日志 debug_module=true print debug log
  214 +func (notify *GetuiNotification) print(url string, v interface{}, rsp interface{}, result *MessageBase) {
  215 + if !notify.Options.DebugModule {
  216 + return
  217 + }
  218 + log.Error(fmt.Sprintf("【个推】 url:%v \n request:%v \n response:%v 结果:%v", url, utils.JsonAssertString(v), utils.JsonAssertString(rsp), result.Msg))
  219 +}
  220 +
  221 +//处理结果
  222 +func handleResult(url string, result *MessageBase) (err error) {
  223 + if result.Code == 0 {
  224 + return
  225 + }
  226 +
  227 + switch result.Code {
  228 + case 0:
  229 + break
  230 + default:
  231 + setToken("")
  232 + break
  233 + }
  234 + err = fmt.Errorf("error:%v %v", result.Code, result.Msg)
  235 + return err
  236 +}
  237 +func sign(appKey, timestamp, masterSecret string) string {
  238 + sha := sha256.New()
  239 + sha.Write([]byte(appKey + timestamp + masterSecret))
  240 + return fmt.Sprintf("%x", sha.Sum(nil))
  241 +}
  242 +
  243 +func setToken(token string) {
  244 + //authMux.Lock()
  245 + //defer authMux.Unlock()
  246 + authtoken = token
  247 +}
  1 +package getuiV2
  2 +
  3 +import (
  4 + "openapi/pkg/infrastructure/push"
  5 + "openapi/pkg/infrastructure/utils"
  6 + "testing"
  7 +)
  8 +
  9 +func TestGetui(t *testing.T) {
  10 + var param = make(map[string]interface{})
  11 + param["A"] = "A1"
  12 + param["B"] = 2
  13 + param["transData"] = struct{ Id int }{Id: 10}
  14 + notification := &GetuiNotification{}
  15 + err := notification.Init(
  16 + push.DebugModule(true),
  17 +
  18 + push.AppId("TkpBI4awmg9fBUx3NWKXS6"),
  19 + push.AppKey("5AjJeDOSOZ5ojQpXJFjhg9"),
  20 + push.AppMasterSecret("9VnM8MaA6n84Y5VnOIaSvA"),
  21 + //单推
  22 + push.PushType(push.PushToSingle),
  23 + push.ClientId("b5fff5f6b0af551da5f381fa47991828"),
  24 + //群推
  25 + //push.PushType(push.PushToList),
  26 + //push.ClientIds([]string{"b5fff5f6b0af551da5f381fa47991828"}),
  27 +
  28 + push.MsgType(push.SystemTransmission), //push.SystemNotification
  29 + push.Title("测试 hello"),
  30 + push.Content("hello content"),
  31 +
  32 + push.TransmissionContent(utils.JsonAssertString(param["transData"])),
  33 + push.Extra(param),
  34 + )
  35 + if err != nil {
  36 + t.Fatal(err)
  37 + }
  38 + _, err = notification.Send(param)
  39 + if err != nil {
  40 + t.Fatal(err)
  41 + }
  42 +}
  43 +
  44 +func TestGetuiPrd(t *testing.T) {
  45 + var param = make(map[string]interface{})
  46 + param["A"] = "A1"
  47 + param["B"] = 2
  48 + param["transData"] = struct {
  49 + Id int `json:"id"`
  50 + }{Id: 1}
  51 + notification := &GetuiNotification{}
  52 + err := notification.Init(
  53 + push.DebugModule(true),
  54 +
  55 + push.AppId("WgrbaaStTk7JElrXOCgUg6"),
  56 + push.AppKey("FG5lbqVrHa5rS9NVfxNP7"),
  57 + push.AppMasterSecret("FW3jMNLJrRARYKv2iqA5H5"),
  58 + //单推
  59 + //push.PushType(push.PushToSingle),
  60 + //push.ClientId("502f4fd7ba5df15ac6b3d5c561efd9ca"),
  61 + //群推
  62 + push.PushType(push.PushToList),
  63 + push.ClientIds([]string{"502f4fd7ba5df15ac6b3d5c561efd9ca"}),
  64 +
  65 + push.MsgType(push.SystemTransmission),
  66 + push.Title("hello"),
  67 + push.Content("hello content"),
  68 +
  69 + push.TransmissionContent(utils.JsonAssertString(param["transData"])),
  70 + push.Extra(param),
  71 + )
  72 + if err != nil {
  73 + t.Fatal(err)
  74 + }
  75 + _, err = notification.Send(param)
  76 + if err != nil {
  77 + t.Fatal(err)
  78 + }
  79 +}
  1 +package getuiV2
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/mmm-go/gocomm/identity/uid"
  5 + "openapi/pkg/infrastructure/push"
  6 + "time"
  7 +)
  8 +
  9 +//1.消息模板
  10 +type NotificationTemplate struct {
  11 + *Template
  12 + Notification *Notification `json:"notification"`
  13 +}
  14 +
  15 +//1.新建通知模板
  16 +func NewNotificationTemplate(options *push.Options) *NotificationTemplate {
  17 + return &NotificationTemplate{
  18 + Template: NewTemplate(options),
  19 + Notification: &Notification{
  20 + Style: (&Style{}).SetStyle0(options),
  21 + Transmission: NewTransmission(options),
  22 + },
  23 + }
  24 +}
  25 +
  26 +//2.透传模板
  27 +type TransmissionTemplate struct {
  28 + *Template
  29 + Transmission *Transmission `json:"transmission"`
  30 + PushInfo *PushInfo `json:"push_info"`
  31 +}
  32 +
  33 +//2.新建透传模板
  34 +func NewTransmissionTemplate(options *push.Options) *TransmissionTemplate {
  35 + return &TransmissionTemplate{
  36 + Template: NewTemplate(options),
  37 + Transmission: NewTransmission(options),
  38 + PushInfo: NewPushInfo(options),
  39 + }
  40 +}
  41 +
  42 +type Template struct {
  43 + ClientId string `json:"cid,omitempty"`
  44 + RequestId string `json:"requestid,omitempty"`
  45 + Message *Message `json:"message"`
  46 +}
  47 +type Notification struct {
  48 + Style *Style `json:"style"`
  49 + *Transmission
  50 +}
  51 +type Message struct {
  52 + AppKey string `json:"appkey"`
  53 + IsOffline bool `json:"is_offline"`
  54 + MsgType string `json:"msgtype"`
  55 + OfflineExpireTime int `json:"offline_expire_time"` //offline_expire_time
  56 +}
  57 +
  58 +//透传
  59 +type Transmission struct {
  60 + TransmissionType bool `json:"transmission_type"` //收到消息是否立即启动应用,true为立即启动,false则广播等待启动,默认是否
  61 + TransmissionContent string `json:"transmission_content,omitempty"` //透传内容
  62 + DurationBegin string `json:"duration_begin,omitempty"`
  63 + DurationEnd string `json:"duration_end,omitempty"`
  64 + Notify interface{} `json:"notify,omitempty"`
  65 +}
  66 +
  67 +func (o *Transmission) SetTransmissionType(t bool) {
  68 + o.TransmissionType = t
  69 +}
  70 +func (o *Transmission) SetTransmissionContent(s string) {
  71 + o.TransmissionContent = s
  72 +}
  73 +func (o *Transmission) SetDuration(begin, end string) {
  74 + o.DurationBegin = begin
  75 + o.DurationEnd = end
  76 +}
  77 +func (o *Transmission) SetNotify(options *push.Options) {
  78 + mapNotify := make(map[string]interface{})
  79 + mapNotify["title"] = options.Title
  80 + mapNotify["content"] = options.Content
  81 + // 安卓设备生成
  82 + mapNotify["intent"] = options.Intent
  83 + mapNotify["type"] = 1
  84 + //mapNotify["extKVList"] = []ExtKVList{
  85 + // //{Constrains: "HW", Key: "/message/android/notification/badge/add_num", Value: 1},
  86 + // //{Constrains: "HW", Key: "/message/android/notification/badge/class", Value: `"com.getui.demo.GetuiSdkDemoActivity"`},
  87 + // // 小米厂家支持
  88 + // {Constrains: "XM", Key: "channel", Value: "\"2882303761518034255\""},//Default 2882303761518034255
  89 + //}
  90 + o.Notify = mapNotify
  91 +}
  92 +
  93 +type ExtKVList struct {
  94 + Constrains string `json:"constrains"`
  95 + Key string `json:"key"`
  96 + Value interface{} `json:"value"`
  97 +}
  98 +
  99 +func NewTemplate(options *push.Options) *Template {
  100 + return &Template{
  101 + Message: NewMessage(options),
  102 + ClientId: options.ClientId,
  103 + RequestId: genRequestId(),
  104 + }
  105 +}
  106 +func NewTransmission(options *push.Options) *Transmission {
  107 + t := &Transmission{}
  108 + if len(options.TransmissionContent) == 0 {
  109 + //t.SetTransmissionType(false)
  110 + return t
  111 + }
  112 + t.SetTransmissionType(false)
  113 + t.SetTransmissionContent(options.TransmissionContent)
  114 + t.SetNotify(options)
  115 + return t
  116 +}
  117 +func NewMessage(options *push.Options) *Message {
  118 + return &Message{
  119 + AppKey: options.AppKey,
  120 + IsOffline: true,
  121 + MsgType: resolveMsgType(options.MsgType),
  122 + OfflineExpireTime: 100000000,
  123 + }
  124 +}
  125 +func resolveMsgType(msgType int) string {
  126 + /*
  127 + 消息应用类型,
  128 + 可选项:notification、link、notypopload、startactivity, transmission
  129 + */
  130 + switch msgType {
  131 + case push.SystemNotification:
  132 + return "notification"
  133 + case push.SystemTransmission:
  134 + return "transmission"
  135 + }
  136 + return "notification"
  137 +}
  138 +func genRequestId() string {
  139 + return uid.NewV1().StringNoDash()
  140 +}
  141 +
  142 +//样式 0:系统样式 1:个推样式 4:纯图样式 6:展开通知样式
  143 +type Style struct {
  144 + Type int `json:"type"` //样式类型
  145 + Text string `json:"text"` //通知内容
  146 + Title string `json:"title"` //通知标题
  147 + Logo string `json:"logo,omitempty"` //通知的图标名称,包含后缀名(需要在客户端开发时嵌入),如“push.png”
  148 + //IsRing bool `json:"is_ring"` //收到通知是否响铃:true响铃,false不响铃。默认响铃
  149 + //IsVibrate bool `json:"is_vibrate"` //收到通知是否振动:true振动,false不振动。默认振动
  150 + NotifyId int `json:"notify_id"` //需要被覆盖的消息已经增加了notifyId字段,用于实现下发消息的覆盖。新的消息使用相同的notifyId下发。
  151 +}
  152 +
  153 +//设置默认样式 0
  154 +func (s *Style) SetStyle0(options *push.Options) *Style {
  155 + s.Type = 0
  156 + s.Title = options.Title
  157 + s.Text = options.Content
  158 + s.Logo = "push.png" //TODO:设置Logo地址
  159 + s.NotifyId = 1
  160 + return s
  161 +}
  162 +
  163 +//认证请求/应答
  164 +type AuthSignRequest struct {
  165 + Sign string `json:"sign"`
  166 + Timestamp string `json:"timestamp"`
  167 + AppKey string `json:"appkey"`
  168 +}
  169 +type AuthSignResponse struct {
  170 + *Result
  171 + *MessageBase
  172 + //ExpireTime string `json:"expire_time"`
  173 + //AuthToken string `json:"token"`
  174 +}
  175 +
  176 +func (auth AuthSignResponse) GetExpireTime(defaultEx time.Duration) time.Duration {
  177 + //v,e := strconv.Atoi(auth.ExpireTime)
  178 + //if e!=nil || v==0{
  179 + return defaultEx
  180 + //}
  181 + //return time.Duration(v)*time.Second
  182 +}
  183 +
  184 +type MessageBase struct {
  185 + Code int `json:"code"`
  186 + Msg string `json:"msg"`
  187 + Data map[string]interface{} `json:"data"`
  188 +}
  189 +
  190 +//应答结果
  191 +type Result struct {
  192 + Result string `json:"result"`
  193 + TaskId string `json:"taskid"`
  194 + Status string `json:"status"`
  195 + Desc string `json:"desc"`
  196 +}
  197 +
  198 +//透传附加的推送信息
  199 +func NewPushInfo(options *push.Options) (v *PushInfo) {
  200 + v = &PushInfo{
  201 + Aps: NewAps(options),
  202 + Payload: options.TransmissionContent,
  203 + }
  204 + return
  205 +}
  206 +func NewAps(options *push.Options) (v *Aps) {
  207 + v = &Aps{
  208 + Alert: NewAlert(options),
  209 + AutoBadge: "+1",
  210 + ContentAvailable: 0,
  211 + Sound: "default",
  212 + }
  213 + // 声音
  214 + if value, ok := options.GetExt("sound"); ok {
  215 + v.Sound = value.(string)
  216 + }
  217 + return
  218 +}
  219 +func NewAlert(options *push.Options) (v *Alert) {
  220 + v = &Alert{
  221 + //Title: options.Title, //TODO:去掉这个ios通知栏只有内容行,没有标题行,如果后期需要显示这个 需要ext里面扩展字段用来控制是否显示标题
  222 + Body: options.Content,
  223 + }
  224 + return
  225 +}
  226 +
  227 +type PushInfo struct {
  228 + Aps *Aps `json:"aps"`
  229 + Payload string `json:"payload,omitempty"`
  230 +}
  231 +type Aps struct {
  232 + Alert *Alert `json:"alert"`
  233 + AutoBadge string `json:"autoBadge"` //用于计算应用上面未读数字
  234 + ContentAvailable int `json:"content-available,omitempty"` //推送直接带有透传数据 0:有通知栏消息 1:无通知栏消息
  235 + Sound string `json:"sound"` // 推送声音 storein_voice.mp3
  236 +}
  237 +type Alert struct {
  238 + Title string `json:"title"`
  239 + Body string `json:"body"`
  240 +}
  1 +package getuiV2
  2 +
  3 +import (
  4 + "openapi/pkg/infrastructure/push"
  5 + "reflect"
  6 + "strings"
  7 +)
  8 +
  9 +const (
  10 + splitChar = "."
  11 + contentTemplate = "点击查看详情"
  12 +)
  13 +
  14 +type MapData struct {
  15 + Data map[string]interface{}
  16 +}
  17 +
  18 +func NewMapData() *MapData {
  19 + return &MapData{
  20 + Data: make(map[string]interface{}),
  21 + }
  22 +}
  23 +func (m *MapData) AddFiled(field string, value interface{}) *MapData {
  24 + fields := strings.Split(field, splitChar)
  25 + var cur map[string]interface{}
  26 + cur = m.Data
  27 + for index, f := range fields {
  28 + if index != (len(fields) - 1) {
  29 + if _, ok := cur[f]; !ok {
  30 + cur[f] = make(map[string]interface{})
  31 + }
  32 + cur = cur[f].(map[string]interface{})
  33 + continue
  34 + }
  35 + if _, ok := cur[f]; !ok {
  36 + cur[f] = value
  37 + }
  38 + }
  39 + return m
  40 +}
  41 +func (m *MapData) GetFiledMap(field string) map[string]interface{} {
  42 + fields := strings.Split(field, splitChar)
  43 + cur := m.Data
  44 + for _, f := range fields {
  45 + if _, ok := cur[f]; !ok {
  46 + cur[f] = make(map[string]interface{})
  47 + }
  48 + cur = cur[f].(map[string]interface{})
  49 + }
  50 + return cur
  51 +}
  52 +func (m *MapData) SetFieldMap(fieldMap map[string]interface{}, field string, value interface{}) *MapData {
  53 + if value == nil {
  54 + return m
  55 + }
  56 + v := reflect.ValueOf(value)
  57 + if !v.IsValid() {
  58 + return m
  59 + }
  60 + if v.IsZero() {
  61 + return m
  62 + }
  63 + fieldMap[field] = value
  64 + return m
  65 +}
  66 +
  67 +// 推送消息
  68 +func NewPushMessage(option *push.Options) map[string]interface{} {
  69 + m := NewMapData()
  70 + // request_id
  71 + m.AddFiled("request_id", genRequestId())
  72 +
  73 + // setting
  74 + m.AddFiled("settings.ttl", 3600*24)
  75 +
  76 + // audience
  77 + m.AddFiled("audience.cid", []string{option.ClientId})
  78 + if len(option.ClientIds) > 0 {
  79 + m.AddFiled("audience.cid", option.ClientIds)
  80 + }
  81 +
  82 + // push_message
  83 + if option.MsgType == push.Notification {
  84 + pushMessageNotification(m, option)
  85 + } else if option.MsgType == push.SystemTransmission {
  86 + pushMessageTransmission(m, option)
  87 + }
  88 +
  89 + // push_channel
  90 + channelAndroid(m, option)
  91 + channelIOS(m, option)
  92 +
  93 + return m.Data
  94 +}
  95 +
  96 +/*push_message*/
  97 +func pushMessageNotification(m *MapData, option *push.Options) {
  98 + notification := m.GetFiledMap("push_message.notification")
  99 +
  100 + m.SetFieldMap(notification, "title", option.Title)
  101 + m.SetFieldMap(notification, "body", option.Content)
  102 +
  103 + m.SetFieldMap(notification, "click_type", "payload")
  104 + m.SetFieldMap(notification, "payload", option.TransmissionContent)
  105 + if len(option.Intent) > 0 {
  106 + m.SetFieldMap(notification, "click_type", "intent")
  107 + m.SetFieldMap(notification, "intent", option.Intent)
  108 + }
  109 +}
  110 +func pushMessageTransmission(m *MapData, option *push.Options) {
  111 + m.AddFiled("push_message.transmission", option.TransmissionContent)
  112 +}
  113 +
  114 +/*channel*/
  115 +/*
  116 + "android":{
  117 + "ups":{
  118 + "notification":{
  119 + "title":"请填写android标题",
  120 + "body":"请填写android内容",
  121 + "click_type":"url",
  122 + "url":"https://xxx"
  123 + }
  124 + }
  125 + },
  126 +*/
  127 +func channelAndroid(m *MapData, option *push.Options) {
  128 + notification := m.GetFiledMap("push_channel.android.ups.notification")
  129 + m.SetFieldMap(notification, "title", option.Title).
  130 + SetFieldMap(notification, "body", contentTemplate) //TODO:配置控制body是否展示
  131 + if len(option.Intent) > 0 {
  132 + m.SetFieldMap(notification, "intent", option.FormatTranDataToIntent()).
  133 + SetFieldMap(notification, "click_type", "intent")
  134 + } else {
  135 + m.SetFieldMap(notification, "click_type", "payload")
  136 + m.SetFieldMap(notification, "payload", option.TransmissionContent)
  137 + }
  138 +}
  139 +
  140 +/*
  141 + "ios":{
  142 + "type":"notify",
  143 + "payload":"自定义消息",
  144 + "aps":{
  145 + "alert":{
  146 + "title":"请填写ios标题",
  147 + "body":"请填写ios内容"
  148 + },
  149 + "content-available":0
  150 + },
  151 + "auto_badge":"+1"
  152 + }
  153 +*/
  154 +func channelIOS(m *MapData, option *push.Options) {
  155 + m.AddFiled("push_channel.ios.type", "notify")
  156 + m.AddFiled("push_channel.ios.payload", option.TransmissionContent)
  157 +
  158 + alert := m.GetFiledMap("push_channel.ios.aps.alert")
  159 + //TODO:去掉这个ios通知栏只有内容行,没有标题行,如果后期需要显示这个 需要ext里面扩展字段用来控制是否显示标题
  160 + //m.SetFieldMap(alert, "title", option.Title)
  161 + m.SetFieldMap(alert, "body", option.Content)
  162 + m.AddFiled("push_channel.ios.aps.content-available", 0)
  163 +
  164 + if v, ok := option.GetExt("sound"); ok && len(v.(string)) > 0 {
  165 + m.AddFiled("push_channel.ios.aps.sound", v)
  166 + }
  167 + m.AddFiled("push_channel.ios.auto_badge", "+1")
  168 +}
  1 +package getuiV2
  2 +
  3 +import (
  4 + "gitlab.fjmaimaimai.com/mmm-go/gocomm/common"
  5 + "testing"
  6 +)
  7 +
  8 +func TestNewMapData(t *testing.T) {
  9 + m := NewMapData()
  10 + m.AddFiled("user.id", 1)
  11 + m.AddFiled("user.name", "tip")
  12 + m.AddFiled("user.sex", true)
  13 +
  14 + m.AddFiled("address.lon", 59.2156461)
  15 + m.AddFiled("address.lat", 23.1245648)
  16 + m.AddFiled("phone", "18860183050")
  17 +
  18 + notification := m.GetFiledMap("notification")
  19 + notification["title"] = "xxx"
  20 + notification["body"] = "body"
  21 + m.SetFieldMap(notification, "url", "http://")
  22 + m.SetFieldMap(notification, "options", nil)
  23 +
  24 + t.Log(common.AssertJson(m.Data))
  25 +}
1 package push 1 package push
2 2
  3 +import (
  4 + "bytes"
  5 + "fmt"
  6 + "strings"
  7 +)
  8 +
3 type Options struct { 9 type Options struct {
4 AppId string 10 AppId string
5 AppKey string 11 AppKey string
@@ -13,9 +19,12 @@ type Options struct { @@ -13,9 +19,12 @@ type Options struct {
13 19
14 Title string 20 Title string
15 Content string 21 Content string
16 - Extra interface{} //扩展数据 22 + Extra interface{} //扩展数据 map[string]interface{} key:"sound" storein_voice.mp3
17 TransmissionContent string //透传内容 23 TransmissionContent string //透传内容
18 24
  25 + //多厂家支持
  26 + Intent string
  27 +
19 DebugModule bool 28 DebugModule bool
20 } 29 }
21 type Option func(o *Options) 30 type Option func(o *Options)
@@ -95,12 +104,48 @@ func TransmissionContent(content string) Option { @@ -95,12 +104,48 @@ func TransmissionContent(content string) Option {
95 o.TransmissionContent = content 104 o.TransmissionContent = content
96 } 105 }
97 } 106 }
  107 +func Intent(intent string) Option {
  108 + return func(o *Options) {
  109 + o.Intent = intent
  110 + }
  111 +}
98 func DebugModule(module bool) Option { 112 func DebugModule(module bool) Option {
99 return func(o *Options) { 113 return func(o *Options) {
100 o.DebugModule = module 114 o.DebugModule = module
101 } 115 }
102 } 116 }
103 117
  118 +func (o *Options) GetExt(key string) (value interface{}, ok bool) {
  119 + var mapExt map[string]interface{}
  120 + if mapExt, ok = o.Extra.(map[string]interface{}); !ok {
  121 + return
  122 + }
  123 + if value, ok = mapExt[key]; ok {
  124 + return
  125 + }
  126 + return
  127 +}
  128 +
  129 +func (o *Options) FormatTranDataToIntent() string {
  130 + tran, ok := o.GetExt("transData")
  131 + if !ok {
  132 + return o.Intent
  133 + }
  134 + var tranMap map[string]interface{}
  135 + tranMap, ok = tran.(map[string]interface{})
  136 + if !ok {
  137 + return o.Intent
  138 + }
  139 + var params = bytes.NewBuffer(nil)
  140 + for k, v := range tranMap {
  141 + params.WriteString(fmt.Sprintf("S.%s=%v;", k, v))
  142 + }
  143 + if idx := strings.Index(o.Intent, "end"); idx > 0 {
  144 + return o.Intent[0:idx] + params.String() + "end"
  145 + }
  146 + return o.Intent
  147 +}
  148 +
104 const ( 149 const (
105 Message = iota + 1 150 Message = iota + 1
106 Notification 151 Notification
@@ -4,7 +4,7 @@ import ( @@ -4,7 +4,7 @@ import (
4 "fmt" 4 "fmt"
5 "github.com/astaxie/beego/orm" 5 "github.com/astaxie/beego/orm"
6 "openapi/pkg/domain" 6 "openapi/pkg/domain"
7 - "openapi/pkg/infrastructure/bgorm/model" 7 + "openapi/pkg/infrastructure/bgorm/models"
8 . "openapi/pkg/infrastructure/utils" 8 . "openapi/pkg/infrastructure/utils"
9 ) 9 )
10 10
@@ -3,7 +3,7 @@ package repository @@ -3,7 +3,7 @@ package repository
3 import ( 3 import (
4 "github.com/astaxie/beego/orm" 4 "github.com/astaxie/beego/orm"
5 "openapi/pkg/domain" 5 "openapi/pkg/domain"
6 - "openapi/pkg/infrastructure/bgorm/model" 6 + "openapi/pkg/infrastructure/bgorm/models"
7 "sync" 7 "sync"
8 ) 8 )
9 9
@@ -44,6 +44,7 @@ func (repository *AppInfoRepository) transformBgormModelToDomainModel(model *mod @@ -44,6 +44,7 @@ func (repository *AppInfoRepository) transformBgormModelToDomainModel(model *mod
44 AppMasterSecret: model.AppMasterSecret, 44 AppMasterSecret: model.AppMasterSecret,
45 AppId: model.AppId, 45 AppId: model.AppId,
46 ProjectId: model.ProjectId, 46 ProjectId: model.ProjectId,
  47 + ExtInfo: model.ExtInfo,
47 }, nil 48 }, nil
48 } 49 }
49 50
@@ -3,7 +3,7 @@ package repository @@ -3,7 +3,7 @@ package repository
3 import ( 3 import (
4 "github.com/astaxie/beego/orm" 4 "github.com/astaxie/beego/orm"
5 "openapi/pkg/domain" 5 "openapi/pkg/domain"
6 - "openapi/pkg/infrastructure/bgorm/model" 6 + "openapi/pkg/infrastructure/bgorm/models"
7 "openapi/pkg/infrastructure/utils" 7 "openapi/pkg/infrastructure/utils"
8 "strings" 8 "strings"
9 "time" 9 "time"
@@ -18,10 +18,12 @@ func (repository *PushDeviceRepository) Save(device *domain.UpdateDeviceRequest) @@ -18,10 +18,12 @@ func (repository *PushDeviceRepository) Save(device *domain.UpdateDeviceRequest)
18 m := &models.PushDeviceInfo{ 18 m := &models.PushDeviceInfo{
19 Uid: device.Muid, 19 Uid: device.Muid,
20 ClientId: strings.TrimSpace(device.ClientId), 20 ClientId: strings.TrimSpace(device.ClientId),
  21 + Phone: device.Phone,
21 DeviceToken: strings.TrimSpace(device.DeviceToken), 22 DeviceToken: strings.TrimSpace(device.DeviceToken),
22 CreateAt: time.Now(), 23 CreateAt: time.Now(),
23 UpdateAt: time.Now(), 24 UpdateAt: time.Now(),
24 ProjectMasterKey: device.ProjectKey, 25 ProjectMasterKey: device.ProjectKey,
  26 + IsActive: 1,
25 } 27 }
26 _, err := o.Insert(m) 28 _, err := o.Insert(m)
27 return err 29 return err
@@ -59,17 +61,17 @@ func (repository *PushDeviceRepository) Find(queryOptions map[string]interface{} @@ -59,17 +61,17 @@ func (repository *PushDeviceRepository) Find(queryOptions map[string]interface{}
59 return 61 return
60 } 62 }
61 63
62 -func (repository *PushDeviceRepository) UpdateDevice(uid int64, clientId, deviceToken string, projectKey string) error { 64 +func (repository *PushDeviceRepository) UpdateDevice(uid int64, clientId, deviceToken string, projectKey string, phone string) error {
63 o := orm.NewOrm() 65 o := orm.NewOrm()
64 o.Begin() 66 o.Begin()
65 - //更新其他绑定这个client_id的设备 is_active=0 67 + //更新(这个项目)其他绑定这个client_id的设备 is_active=0
66 _, err := o.Raw("UPDATE push_device_info SET update_at=now(),is_active=0 where client_id=? and is_active=1 and project_master_key=?", clientId, projectKey).Exec() 68 _, err := o.Raw("UPDATE push_device_info SET update_at=now(),is_active=0 where client_id=? and is_active=1 and project_master_key=?", clientId, projectKey).Exec()
67 if err != nil { 69 if err != nil {
68 o.Rollback() 70 o.Rollback()
69 return err 71 return err
70 } 72 }
71 73
72 - _, err = o.Raw("UPDATE push_device_info SET client_id=?,device_token = ?,update_at=now(),is_active=1 where uid=? and project_master_key=?", clientId, deviceToken, uid, projectKey).Exec() 74 + _, err = o.Raw("UPDATE push_device_info SET client_id=?,device_token = ?,update_at=now(),is_active=1,phone=? where uid=? and project_master_key=?", clientId, deviceToken, phone, uid, projectKey).Exec()
73 if err != nil { 75 if err != nil {
74 o.Rollback() 76 o.Rollback()
75 return err 77 return err
@@ -32,8 +32,8 @@ func (this *PushController) PushInfo() { @@ -32,8 +32,8 @@ func (this *PushController) PushInfo() {
32 msg = m 32 msg = m
33 return 33 return
34 } 34 }
35 - if len(request.ProjectKey) == 0 {  
36 - request.ProjectKey = "worth" //默认是价值项目 35 + if len(request.ProjectKey) == 0 || request.ProjectKey == "worth" {
  36 + request.ProjectKey = "mmm.ability.worth" //默认是价值项目
37 } 37 }
38 header := controllers.GetRequestHeader(this.Ctx) 38 header := controllers.GetRequestHeader(this.Ctx)
39 msg = protocol.NewReturnResponse(push.Notification(header, request)) 39 msg = protocol.NewReturnResponse(push.Notification(header, request))
@@ -78,7 +78,7 @@ func (this *PushController) UpdateDevice() { @@ -78,7 +78,7 @@ func (this *PushController) UpdateDevice() {
78 return 78 return
79 } 79 }
80 if request.ProjectKey == "" { 80 if request.ProjectKey == "" {
81 - request.ProjectKey = "ability" //默认能力展示项目 81 + request.ProjectKey = "mmm.ability" //默认能力展示项目
82 } 82 }
83 header := controllers.GetRequestHeader(this.Ctx) 83 header := controllers.GetRequestHeader(this.Ctx)
84 msg = protocol.NewReturnResponse(push.UpdateDevice(header, request)) 84 msg = protocol.NewReturnResponse(push.UpdateDevice(header, request))