订阅消息
订阅消息是⼩程序能⼒中的重要组成,当⽤户⾃主订阅之后,可以向⽤户以服务通知 的⽅式发送消息的能⼒,当⽤户点击订阅消息卡片可以跳转到⼩程序的⻚⾯,这样就 可以实现服务的闭环和更优的体验,提⾼活跃度和⽤户粘性。
获取订阅消息授权
要获取订阅消息授权,⾸先要调⽤接⼝wx.requestSubscribeMessage
,这个接⼝会调 起⼩程序订阅消息界⾯,返回⽤户订阅消息的操作结果。注意这个接⼝只能在⼩程序 端使⽤tap
点击或⽀付完成后触发。如果是使⽤⻚⾯加载或其他非⽤户点击类的事件来 调⽤这个接⼝,就会报requestSubscribeMessage:fail can only be invoked by user TAP gesture
的错误。
要调⽤wx.requestSubscribeMessage
,需要我们⾸先要有订阅消息的模板ID,⼀次性 模板 id 和永久模板 id 不可同时使⽤,基础库2.8.4之后⼀次性可以调起3个模板ID(不 能多于3个)。
使⽤开发者⼯具新建⼀个⻚⾯,如subscribe,然后在subscribe.wxml
⾥输入以下代 码,我们通过点击tap来触发事件处理函数
<button bindtap="subscribeMessage">订阅订阅消息</button>
然后再在subscribe.js
⾥输入以下代码,我们在事件处理函数subscribeMessage
⾥调⽤ wx.requestSubscribeMessage
接⼝:
subscribeMessage() {
wx.requestSubscribeMessage({
tmplIds: [
"qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",//模板
"RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
"EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
],
success(res) {
console.log("订阅消息API调⽤成功:",res)
},
fail(res) {
console.log("订阅消息API调⽤失败:",res)
}
})
}
建议⼤家在⼿机上进⾏真机调试这个接⼝,点击订阅消息button,就能弹出授权弹窗。
-
当⽤户点击“允许”就会累积⼀次授权,如果点击N次允许就能累积N次授权,这个授权是⻓期的,没
有时间限制,你可以在⼀天内发完N次授权,也可以在未来分批次发完;也就是说这个虽然是⼀次
性订阅消息,但是只要⽤户授权了N次,我们在短时间就可以发N次,⽽不是只能发⼀次;累积了
多少次就可以发送多少次。发送⼀次就会消耗⼀次,累积的授权次数被消耗完之后,还继续发,就
会报错"errcode
":"43101
","errmsg
":"user refuse to accept the msghint...
"。 -
当⽤户勾选了订阅⾯板中的“总是保持以上选择,不再询问”时,且允许或拒绝之后,订阅消息的授
权弹窗则永远不会再弹出,订阅消息也会被添加到⽤户的⼩程序设置⻚,我们可以通过wx.getSetting
接⼝可获取⽤户对相关模板消息的订阅状态。 -
如果⽤户勾选了总是允许,那由于⽤户点击按钮都不会弹出授权弹窗,⽤户点击了授权按钮仍然会 累积授权,起到⼀个静默收集授权次数的效果。
注意该接⼝调⽤成功之后返回的对象,[TEMPLATE_ID]是动态的键,即模板id,值包 括’accept’、‘reject’、‘ban’。'accept’表⽰⽤户同意订阅该条id对应的模板消 息,'reject’表⽰⽤户拒绝订阅该条id对应的模板消息,'ban’表⽰已被后台封禁,如下 所⽰(以下值仅为案例):
{errMsg: "requestSubscribeMessage:ok", RCg8DiM_y1erbOXR9DzW_jKsqSSJ9KF0h8lbKKmoFU: "accept", qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44:
"reject", EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0: "accept"}
订阅消息的累积次数决定了我们是否可以给⽤户发送订阅消息,也决定了可以发送⼏ 次,因此记录⽤户给某个模板ID授权了多少次这个也就显得很重要了,比如我们可以 结合接⼝返回的res对象和inc原⼦⾃增在数据库⾥记录订阅次数,当发送⼀次也会消耗 ⼀次,再⽤inc⾃减:
subscribeMessage() {
const tmplIds= [
"qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
"RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
"EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
];
wx.requestSubscribeMessage({
tmplIds:tmplIds,
success(res) {
console.log("订阅消息API调⽤成功:",res)
tmplIds.map(function(item,index){
if(res[item] === "accept"){
console.log("该模板ID⽤户同意了",item)
//可以使⽤原⼦⾃增指令inc往数据库⾥某个记录授权次数的字段+1
}
})
},
fail(res) {
console.log("订阅消息API调⽤失败:",res)
}
})
},
wx.requestSubscribeMessage
的参数tmplIds
是数组可以容纳3个模板ID,当⽤户点击授 权弹窗,三个模板ID都是默认勾选的,只要⽤户点击允许,就会同时给三个模板ID累 积次数;如果⽤户取消勾选了其中⼀个模板ID,并点击总是允许,那另外两个勾选的 模板ID将不会再有授权弹窗。
发送订阅消息⽅式说明
当我们在⼩程序端累积了某个模板ID的授权次数之后,就可以通过云函数来调⽤ subscribeMessage.send
接⼝发送订阅消息了。⽽这个云函数我们可以在⼩程序端调 ⽤,也可以使⽤云函数来调⽤云函数,还能使⽤定时触发器来调⽤云函数。
- ⼩程序端发送订阅消息,有些业务需要在⽤户在⼩程序内完成了某个操作之后,就需要向⽤户发送
订阅消息,⽐如打卡、签到、⽀付、发表成功等,这些业务都依赖于⽤户的操作,当操作完成之后 就可以在回调函数⾥调⽤发送订阅消息的云函数; - 还有⼀种就是,如果你是⼩程序的管理员,订阅消息的管理界⾯也在⼩程序⾥,当管理员在⼩程序
端点击定点或群发订阅消息时,也可以调⽤云函数来发送订阅消息; - 使⽤定时触发器发送订阅消息,这时订阅消息就可以周期性、定时发送,不再需要⽤户/管理员点 击就可以结合业务场景发送。
云函数调⽤subscribeMessage.send
接⼝的⽅式有两种,⼀种是HTTPS
调⽤,还有⼀种 就是云调⽤,建议使⽤云调⽤。调⽤subscribeMessage.send
接⼝时有很多细节需要注 意,尤其是data
格式,必须符合格式要求。
订阅消息的data必须与模板消息⼀⼀对应
比如我们申请到⼀个订阅课程开课提醒的模板,它的格式如下
订阅消息的data必须与模板消息⼀⼀对应 比如我们申请到⼀个订阅课程开课提醒的模板,它的格式如下
姓名{{phrase1.DATA}}
课程标题{{thing2.DATA}}
课程内容{{thing3.DATA}}
时间{{date5.DATA}}
课程进度{{character_string6.DATA}}
与之相应的data的写法如下phrase1
、thing2
、thing3
、date5
、character_string6
,这 些需要⼀⼀对应,参数不能多也不能少,参数后⾯的数字比如date5
不能改成date6
, 否则会报"openapi.subscribeMessage.send:fail argument invalid! hint:的错误,也就是模板⾥有什么参数,你就只能按部就班写什么参数
data: {
"phrase1": {
"value": '李东'
},
"thing2": {
"value": '零基础云开发技术训练营第7课'
},
"thing3": {
"value": '列表渲染与条件渲染'
},
"date5": {
"value": '2019年10⽉20⽇ 20:00'
},
"character_string6": {
"value": 3
}
}
订阅消息参数值的内容格式必须要符合要求
在技术⽂档⾥,有⼀个关于订阅消息参数值的内容格式要求,
这个在写订阅消息内容 的时候需要严格的⼀⼀对应,否则会出现格式错误。
比如:
- 可能已有的模板消息的格式和你想要的不⼀致,⽐如你希望发送的消息是⽤户的昵称,⽽不是姓名
{{phrase1.DATA}},
因为姓名只能是中⽂,且必须5个字以内,那你就没法擅⾃改动,只能去申 请或复⽤其他的模板ID; - 每个格式对字符串的⻓度和类型都有严格的要求,⽐如thing,要求必须是20个以内的字符,不能超
过20个字符;有些只能是数字或字⺟,就不能是其他格式
使⽤云调⽤发送订阅消息
新建⼀个云函数比如subscribeMessage
,然后再在config.json
的添加 subscribeMessage.send
权限,使⽤云函数增量上传更新这个配置⽂件
{
"permissions": {
"openapi": [
"subscribeMessage.send"
]
}
}
然后再在index.js
⾥输入以下代码,注意这⾥的openid
,是⽤户⾃⼰的,这种适⽤于⽤ 户在⼩程序端完成某个业务操作之后,就给⽤户⾃⼰发订阅消息;当然这⾥的openid
可以是其他累积了授权次数的⽤户的,也就是当我们在⼩程序端调⽤该云函数就能给 其他⼈发订阅消息了,这主要适⽤于管理员
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
exports.main = async (event, context) => {
const { OPENID } = cloud.getWXContext()
try {
const result = await cloud.openapi.subscribeMessage.send({
touser: "oUL-m5FuRmuVmxvbYOGuXbuEDsn8",
page: 'index',
templateId: "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
data: {
"phrase1": {
"value": '⼩明'
},
"thing2": {
"value": '零基础云开发技术训练营第7课'
},
"thing3": {
"value": '列表渲染与条件渲染'
},
"date5": {
"value": '2019年10⽉20⽇ 20:00'
},
"character_string6": {
"value": 3
}
}
})
return result
} catch (err) {
console.log(err)
return err
}
}
由于subscribeMessage.send
的参数templateId
和touser
都是字符串,因此执⾏⼀次 subscribeMessage.send
只能给⼀个⽤户发送⼀条订阅消息,那要给更多⽤户比如1000 ⼈以内(云函数⼀次可以获取到1000条数据)发订阅消息,则需要结合数据库的查询 数据库内所有有授权次数的⽤户然后循环执⾏来发消息,并在发完之后使⽤inc⾃减来 减去授权次数。
特别注意的是,不要把查询数据库的语句放到循环⾥⾯,也就是我们可以⼀次性取出1000条需要发订阅消息的⽤户,然后再结合map和Promise.all⽅法给这 1000个⽤户发送订阅消息,然后再⼀次性给所有这1000条数据进⾏原⼦⾃增,不能⼀条⼀条处理,否则会造成数据库性能的极⼤浪费以及超出最⼤连接数, ⽽且也会导致云函数在最⾼60s的⽣命周期⾥也发送不了⼏百条订阅消息。
使⽤定时触发器发订阅消息
但是当要发送订阅消息的⽤户有⼏⼗万⼏百万,那应该怎么处理呢?如果全部让云函 数来执⾏,即使将云函数的执⾏超时时间修改为60s,也应该会超时,这时候我们可以 结合定时器来发送订阅消息。 使⽤定时触发器来发送订阅消息,也就是在⼩程序的云 开发服务端,⽤定时触发器调⽤订阅消息的云调⽤接⼝ openapi.subscribeMessage.send
。当我们每天要给数⼗万⼈定时发送订阅消息时,这 时候定时触发器就不仅仅需要比如每天早上9点触发,⽽且还需要在9点之后能够每隔 ⼀段时间比如40s,就来执⾏⼀次云函数以便给数⼗万⽤户发送订阅消息。 这时候Cron表达式可以这样写,意思是每天早上9点到11点每隔40s执⾏⼀次云函数:
0/40 * 9-11 * * * *
当然这⾥的周期设置可以结合云函数实际执⾏的时间来定,要充分考虑到云函数的超时时间。