社区/学习指南/小程序·云开发高级教程

CloudID与动态消息

开通了云开发的小程序可以使用 Cloud.CloudID 接口返回一个 CloudID(开放数据 ID)特殊对象,将该对象传至云函数就可以获取其对应的开放数据,比如获取微信运动的步数、手机号等开放数据,而这个功能如果是使用非云开发的方式除了需要处理登录的问题,还需要进行加解密,十分繁琐。

13.7.1 获取微信步数

获取微信运动步数的小程序接口为 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)

        }

      })

    }

  })

}

13.7.2 获取用户手机号

要获取用户的手机号,需要将 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 区号。

13.7.3 获取微信群 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 写入到上面的组件,依然不会显示群名,或者使用真机调试也无法显示,这是因为测试群或者新建的群,可能会无效。

13.7.4 动态消息

动态消息发出去之后,开发者可以通过后台接口修改部分消息内容,动态消息也有对应的提醒按钮,用户点击提醒按钮可以订阅提醒,开发者可以通过后台修改消息状态并推送一次提醒消息给订阅了提醒的用户。效果如下所示,这种特别适合我们做抢购、拼团等运营活动:

要让转发的小程序卡片里有动态消息,首先需要使用云调用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,以及 templateInfoactivityId等参数:


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", //从数据库拉取
        },
      ],
    },
  });
});

本文出自 李东bbsky