微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

实战分享: 小程序云开发玩转订阅消息

微信官方为提升小程序模板消息能力的使用体验,对模板消息的下发条件进行了调整。原有的小程序模板消息接口于 2020 年 1 月 10 日下线,届时将无法使用旧的小程序模板消息接口发送模板消息,取而代之的是新的一次性订阅消息和长期订阅消息。

订阅消息给小程序开发者带来了更好的触达用户的能力,在具体实施过程中,开发者如何把模板消息换成新的订阅消息,是否需要购买服务器来实现服务器鉴权,怎样才能在用户订阅之后一段时间后,给用户发送长期或一次性订阅消息呢?

小程序·云开发最近支持了通过云调用免 access_token 发送订阅消息,还新增支持了在定时触发器中实现云调用,这些能力可以帮助开发者轻松玩转小程序订阅消息。

我们今天会利用小程序·云开发进行一个小程序中实现订阅开课提醒的实战,帮助大家了解如何基于小程序·云开发快速接入小程序订阅消息。

整体时序图

环境准备

获取订阅消息模板 ID

微信小程序管理后台中,新增一个订阅消息的模板,这里我们新增了一个开课提醒的模板。

引导用户订阅

微信小程序提供了wx.requestSubscribeMessage 接口来发起申请订阅权限界面。

在 "订阅开课提醒" 的按钮上绑定 tap 事件,事件处理器我们这里用的 onSubscribe

index.wxml

<button
  class="btn"
  data-item="{{ item }}"
  bindtap="onSubscribe"
  hover-class="btn-hover"
>
  订阅开课提醒
</button>
复制代码

onSubscribe 函数内,我们会调用微信 API wx.requestSubscribeMessage 申请发送订阅消息权限,当用户在弹窗同意订阅之后,我们会收到 success 回调,将订阅的课程信息调用云函数 subscribe 存入云开发数据库云函数 subscribe 的实现在下文会讲。

index.js

onSubscribe: function(e) {
    // 获取课程信息
    const item = e.currentTarget.dataset.item;

    // 调用微信 API 申请发送订阅消息
    wx.requestSubscribeMessage({
      // 传入订阅消息的模板id,模板 id 可在小程序管理后台申请
      tmplIds: [lessonTmplId],success(res) {
        // 申请订阅成功
        if (res.errMsg === 'requestSubscribeMessage:ok') {
          // 这里将订阅的课程信息调用云函数存入云开发数据
          wx.cloud
            .callFunction({
              name: 'subscribe',data: {
                data: item,templateId: lessonTmplId,},})
            .then(() => {
              wx.showToast({
                title: '订阅成功',icon: 'success',duration: 2000,});
            })
            .catch(() => {
              wx.showToast({
                title: '订阅失败',});
            });
        }
      },});
  },复制代码

订阅消息存入云开发数据库

接下来我们创建一个云函数 subscribe ,这个云函数的作用是将用户订阅信息存入云开发数据库的集合 messages 中,等待将来需要通知用户时进行调用

在微信开发者工具的云开发面板中创建数据库集合 messages

创建一个 subscribe 云函数,在云函数中我们将小程序端发送过来的课程订阅信息,存储在云开发数据库集合中,开发完成后,在微信开发者工具中右键上传并部署云函数

cloudfunctions/subscribe/index.js

const cloud = require('wx-server-sdk');
cloud.init();
const db = cloud.database();

exports.main = async (event,context) => {
  try {
    const {OPENID} = cloud.getWXContext();
    // 在云开发数据库中存储用户订阅的课程
    const result = await db.collection('messages').add({
      data: {
        touser: OPENID,// 订阅者的openid
        page: 'index',// 订阅消息卡片点击后会打开小程序的哪个页面
        data: event.data,// 订阅消息的数据
        templateId: event.templateId,// 订阅消息模板ID
        done: false,// 消息发送状态设置为 false
      },});
    return result;
  } catch (err) {
    console.log(err);
    return err;
  }
};
复制代码

利用定时触发器来定期发送订阅消息

接下来我们需要实现一个定时执行的云函数send,来检查数据库中是否有需要发送给用户订阅消息。如果有需要发送的订阅消息,会通过云调用 cloud.openapi.subscribeMessage.send订阅消息发送给用户

创建一个名叫 send云函数,首先要配置云函数,在 config.jsonpermissions 中新增 subscribeMessage.send的云调用权限,然后新增一个 sendMessagerTimer 的定时触发器,定时触发器的语法和 linuxcrontab 类似,比如,我们配置的 "0 * * * * * *" 代表每分钟执行一次云函数

cloudfunctions/send/config.json

{
  "permissions": {
    "openapi": ["subscribeMessage.send"]
  },"triggers": [
    {
      "name": "sendMessagerTimer","type": "timer","config": "0 * * * * * *"
    }
  ]
}
复制代码

接下来是实现发送订阅消息的云函数,这个云函数会从云开发数据库集合messages查询等待发送的消息列表,检查数据库中是否有需要发送给用户订阅消息,发送条件可以根据自己的业务实现,比如开课提醒可以根据课程开课日期来检查是否需要发送订阅消息,在我们下面的代码示例里做了简化,筛选条件只检查了状态为未发送。

查询到待发送的消息列表之后,我们会循环消息列表,依次发送每条订阅消息,发送成功后将数据库中消息的状态改为已发送。

cloudfunctions/send/index.js

const cloud = require('wx-server-sdk');

exports.main = async (event,context) => {
  cloud.init();
  const db = cloud.database();

  try {
    // 从云开发数据库查询等待发送的消息列表
    const messages = await db
      .collection('messages')
      // 查询条件这里做了简化,只查找了状态为未发送的消息
      // 在真正的生产环境,可以根据开课日期等条件筛选应该发送哪些消息
      .where({
        done: false,})
      .get();

    // 循环消息列表
    const sendPromises = messages.data.map(async message => {
      try {
        // 发送订阅消息
        await cloud.openapi.subscribeMessage.send({
          touser: message.touser,page: message.page,data: message.data,templateId: message.templateId,});
        // 发送成功后将消息的状态改为已发送
        return db
          .collection('messages')
          .doc(message._id)
          .update({
            data: {
              done: true,});
      } catch (e) {
        return e;
      }
    });

    return Promise.all(sendPromises);
  } catch (err) {
    console.log(err);
    return err;
  }
};
复制代码

最终效果

代码

github.com/binggg/tcb-…

关于我

binggg(Booker Zhao) @腾讯

- 先后就职于迅雷、腾讯等,个人开源项目有 mrn.js 等
- 创办了迅雷内部组件仓库 XNPM ,参与几个迅雷前端开源项目的开发
- 热衷于优化和提效,是一个奉行“懒惰使人进步”的懒人工程师
复制代码

社交资料

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。