云开发 CloudID与动态消息
开通了云开发的小程序可以使用Cloud.CloudID接口返回一个 CloudID(开放数据 ID)特殊对象,将该对象传至云函数就可以获取其对应的开放数据,比如获取微信运动的步数、手机号等开放数据,而这个功能如果是使用非云开发的方式除了需要处理登录的问题,还需要进行加解密,十分繁琐。
一、获取微信步数
获取微信运动步数的小程序接口为wx.getWeRunData,可以获取用户过去三十天微信运动步数。使用可开发者工具新建一个页面页面比如openData,然后在openData.wxml里输入一个button按钮:
<button bindtap="getWeRunData">获取微信步数</button>
然后再在openData.js里输入以下代码,我们用事件处理函数getWeRunData来调用wx.getWeRunData接口,并打印结果。
getWeRunData(){
wx.getWeRunData({
success: (result) => {
console.log(result)
},
})
}
编译之后,点击按钮,我们可以在控制台看到返回的res对象里有encryptedData包括敏感数据在内的完整用户信息的加密数据、iv加密算法的初始向量, cloudID敏感数据对应的云 ID.
{errMsg: "getWeRunData:ok",
encryptedData: "ABeBwlCHs....6PvAax",
iv: "g8QPFXTLLD3N6Zn3YiuwEQ==",
cloudID: "30_jVhZr_Up-8_TV...kgP8yJ8ykN0I"}
这个cloudID只有在开通了云开发的小程序才会返回,我们可以将cloudID传入云函数,通过云调用就可以直接获取开放数据。
使用开发者工具新建云函数比如opendata,再index.js里输入以下代码,并部署上线,在云函数端接收到的 event 将会包含对应开放数据的对象。
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
exports.main = async (event, context) => {
return event
}
我们再来在前面的事件处理函数getWeRunData里上传经过cloud.CloudID接口获得的cloudID对象,然后调用opendata云函数,并在success里打印返回来的对象,就可以看到包含微信运动步数的对象啦:
getWeRunData(){
wx.getWeRunData({
success: (result) => {
console.log(result.cloudID)
wx.cloud.callFunction({
name: 'opendata',
data: {
weRunData: wx.cloud.CloudID(result.cloudID),
},
success:(res)=>{
console.log(res.result.weRunData.cloudID)
console.log(res.result.weRunData.data.stepInfoList)
}
})
}
})
}
二、获取用户手机号
要获取用户的手机号,需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到微信服务器返回的加密数据,如果开通了云开发,就能在回调对象了获取到cloudID。使用开发者工具在openData.wxml里输入如下代码:
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
然后再在openData.js里输入以下代码,我们打印事件处理函数getPhoneNumber返回的结果。
getPhoneNumber (result) {
console.log("result内容",result.detail)
},
同样我们也会获得一个类似于微信运动步数的返回结果
{errMsg: "getPhoneNumber:ok",
encryptedData: "Aw+W76TSvYAPS.....g==",
iv: "9wSepi6qx...=",
cloudID: "30_sSext5q.....qmLQ"}
我们仍然只需要将获取到cloudID经过cloud.CloudID()接口处理返回的对象上传并调用云函数:
getPhoneNumber (result) {
wx.cloud.callFunction({
name: 'opendata',
data: {
getPhoneNumber: wx.cloud.CloudID(result.detail.cloudID),
},
success:(res)=>{
console.log("云函数返回的对象",res.result.getPhoneNumber)
}
})
},
在getPhoneNumber的data对象里的phoneNumber是用户绑定的手机号(国外手机号会有区号)、purePhoneNumber是没有区号的手机号、countryCode区号。
三、获取微信群ID和群名称
要获取微信群ID和群名称,需要经过一系列相对比较复杂的处理,需要经过以下步骤,具体的代码和开发方式后面会具体介绍:
- 首先需要小程序的分享里的
withShareTicket: true
,分享也必须分享到微信群里;
- 点击微信群里的小程序卡片,才能获取到
shareTicket
,
- 然后将shareTicket传入到
wx.getShareInfo
里就会得到微信群敏感数据对应的cloudID,
- 然后我们需要将cloudID通过
wx.cloud.CloudID(cloudID)
传入到云函数,云函数就可以返回微信群ID,也就是openGId
- 最后我们需要再通过
<open-data type="groupName" open-gid="{{openGId}}"></open-data>
来显示群名
1、创建一个转发分享
通过给 button 组件设置属性open-type="share"
,可以在用户点击按钮后触发页面的生命周期函数Page.onShareAppMessage
事件。首先我们使用开发者工具新建一个页面,比如share,然后再在share.wxml创建一个button组件,比如:
<button open-type="share">转发</button>
要获取群聊的名称以及群的标识openGId,需要带shareTicket的转发才可以,我们在share.js页面生命周期函数onShareAppMessage
里输入如下代码,设置withShareTicket
为true:
onShareAppMessage: function (res) {
wx.updateShareMenu({
withShareTicket: true,
success(res) {
console.log(res)
},
fail(err) {
console.log(err)
}
})
if (res.from === 'button') {
console.log(res.target) //可以在这里将用户点击button的次数存储到数据库,相当于埋点
}
return {
title: '云开发技术训练营',
path: 'pages/share/share?openid=oUL-m5FuRmuVmxvbYOGuXbuEDsn8',
imageUrl:"cloud://xly-xrlur.786c-xly-xrlur-1300446086/share.png"//支持云存储的fileID
}
},
关于显示右上角菜单的转发按钮可以使用
wx.showShareMenu
接口,而onShareAppMessage
除了可以监听用户点击页面内的button,也可以监听右上角菜单“转发”按钮的行为,无论是哪一种,都可以自定义菜单的title、path、imageUrl等,这里就不具体写代码啦。
2、获取shareTickets
值得注意的是,只有转发到微信群聊中,再通过微信群聊里的小程序卡片进入到小程序才可以获取到shareTickets返回值,单聊没有shareTickets;shareTicket仅在当前小程序生命周期内有效。但是在开发时,怎么把小程序转发到微信群里面去呢?开发者工具提供了带shareTickets的调试方法。
在开发者工具的模拟器里点击"转发"button,就会出现一个测试模拟群列表,我们可以将小程序转发到一个群聊里面去,比如测试模拟群4
。调试时,我们要添加自定义编译模式,在进入场景里选择1044: 带 shareTicket 的小程序消息卡片
,选择进入的群为你转发的群,具体可以参考如下图:
获取shareTicket,我们可以使用wx.getLaunchOptionsSync()
来获取小程序启动时的参数,这个参数与App.onLaunch 的回调参数一致,而shareTicket就在这个参数对象里。我们可以在share.js的onLoad生命周期函数里来获取它:
onLoad:function (options) {
const res = wx.getLaunchOptionsSync()
console.log('小程序启动时的参数',res)
const {shareTicket} = res
console.log('shareTicket的值',shareTicket)
},
如果你直接使用普通编译(不使用上面的调试方法),是获取不到shareTicket的,shareTicket的值会为
undefined
,同时如果小程序直接加载(而不是通过点击群聊里分享的小程序卡片进入),shareTicket的值也是undefined
。
3、获取cloudID并获取群IDopenGId
当我们获取到shareTicket之后,就可以调用wx.getShareInfo
接口来获取到关于转发的信息,尤其是cloudID。然后我们可以把获取到的CloudID,传入到云函数,比如share云函数。
使用开发者工具新建一个share云函数,在index.js里输入以下代码(这个其实就是返回event对象,如此简单的云函数我们可以和其他云函数合并到一起使用,比如获取openid等):
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
const TcbRouter = require('tcb-router');
exports.main = async (event, context) => {
return event
}
然后再在小程序端share.js的生命周期函数里继续写如下代码,先判断shareTicket是否为空(也就是判断是否是通过微信群聊小程序卡片进入的),然后调用wx.getShareInfo来获取CloudID,再将CloudID传入到wx.cloud.CloudID()
接口,并将该对象传至云函数share就可以返回这个CloudID对应的开放数据了(这里的开放数据主要是openGId)。
onLoad:function (options) {
const that = this
const res = await wx.getLaunchOptionsSync()
const {shareTicket} = res
if(shareTicket!=null){ //当shareTicket不为空时,调用wx.getShareInfo来获取CloudID
wx.getShareInfo({
shareTicket:shareTicket,
success:function (res) {
const cloudID = res.cloudID
wx.cloud.callFunction({
name: 'share',
data: {
groupData: wx.cloud.CloudID(cloudID)
},
success: function (res) {
that.setData({
openGId:res.result.groupData.data.openGId
})
}
})
}
})
}
},
4、显示群的名称
openGId为当前群的唯一标识,也就是每个微信群都有唯一且不变的这样一个ID,可以用于区分不同的微信群。我们可以把微信群内点击了小程序分享卡片的群成员的用户信息与这个openGId相关联,这样就可以弄群排行榜等一些基于微信群的开发。
不过我们只能获取微信群的群ID,是不能获取微信群的名称的,但是可以通过开放能力来显示微信群的名称,我们只需要把获取到的openGId字符串传入到open-gid
就可以了。
<open-data type="groupName" open-gid="{{openGId}}"></open-data>
可能你在调试的时候会出现,即使你把openGId写入到上面的组件,依然不会显示群名,或者使用真机调试也无法显示,这是因为测试群或者新建的群,可能会无效。
四、动态消息
动态消息发出去之后,开发者可以通过后台接口修改部分消息内容,动态消息也有对应的提醒按钮,用户点击提醒按钮可以订阅提醒,开发者可以通过后台修改消息状态并推送一次提醒消息给订阅了提醒的用户。效果如下所示,这种特别适合我们做抢购、拼团等运营活动:
要让转发的小程序卡片里有动态消息,首先需要使用云调用updatableMessage.createActivityId
接口来创建activityId
,然后将activityId和templateInfo传入到wx.updateShareMenu
,而要更新动态消息则需要使用到updatableMessage.setUpdatableMsg
的接口。我们可以把创建动态消息和更新动态消息的云函数使用tcb-router整合到一个云函数里面。
1、创建 activityId
使用开发者工具新建一个云函数,云函数的名称为activity,然后在package.json增加tcb-router最新版latest的依赖并用npm install安装:
"dependencies": {
"wx-server-sdk":"latest",
"tcb-router": "latest"
}
以及在config.json里添加云调用的权限,用于生成ActivityId以及修改被分享的动态消息:
{
"permissions": {
"openapi": [
"updatableMessage.createActivityId",
"updatableMessage.setUpdatableMsg"
]
}
}
然后再在index.js里输入以下代码,使用createActivityId
生成ActivityId并返回:
const cloud = require('wx-server-sdk')
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV,
})
const TcbRouter = require('tcb-router');
exports.main = async (event, context) => {
const app = new TcbRouter({event})
app.use(async (ctx, next) => {
ctx.data = {}
await next();
});
app.router('getActivityId',async (ctx, next)=>{
const result = await cloud.openapi.updatableMessage.createActivityId()
ctx.data.activityID = result
ctx.body = {"activityID":ctx.data.activityID}
})
//后面我们会介绍如何更新动态消息,updatableMsg的router可以添加在这里
return app.serve();
}
2、在转发之前声明消息类型为动态消息
和前面一样,我们可以通过调用wx.updateShareMenu
接口,传入isUpdatableMessage: true
,以及 templateInfo
、activityId
等参数:
async onShareAppMessage(res) {
const activityId = (await wx.cloud.callFunction({
name: 'activity',
data: {
$url: "getActivityId",
}
})).result.activityID.activityId
wx.updateShareMenu({
withShareTicket: true,
isUpdatableMessage: true,
activityId: activityId,
templateInfo: {
parameterList: [{
name: 'member_count',
value: '4' //这里的数据可以来自数据库
}, {
name: 'room_limit',
value: '30' //这里的数据可以来自数据库
}]
}
})
return {
title: 'HackWeek技术训练营',
path: 'pages/share/share?openid=oUL-m5FuRmuVmxvbYOGuXbuEDsn8',
imageUrl:"cloud://xly-xrlur.786c-xly-xrlur-1300446086/1572315793633-633.png"
}
},
3、修改动态消息内容
动态消息发出去之后,我们可以通过这个activityId来追踪这个动态消息,当用户进入分享的小程序,报名参与了这个活动时,比如活动为拼团,30人这个团购项目就成功啦,现在已经有4个人参与了(可以从数据库获得),当有新的用户付费参与这个拼团时,我们可以在这个用户付费的回调函数里调用updatableMessage.setUpdatableMsg
这个接口来修改动态消息。比如:
wx.cloud.callFunction({
name: 'activity',
data: {
$url: "updatableMsg",
activityId: activityId, //activityId建议由前端传入,获取的方法如上
}
})
我们继续在activity云函数里添加一个updatableMsg的router即可
const {activityID} = event
app.router('updatableMsg',async (ctx, next)=>{
//我们可以用从数据库拉取现在拼团的人数,以及满团的人数,从而确定targetState的状态
const result = await cloud.openapi.updatableMessage.setUpdatableMsg({
activityID:activityID,
targetState:0,
templateInfo: {
parameterList: [{
name: 'member_count',
value: '5' //从数据库拉取
}, {
name: 'room_limit',
value: '30' //从数据库拉取
}]
}
})
})