云原生微信小程序开发实战读书笔记-4

站在小程序的角度上,订阅消息最普遍的应用场景有两种,一种是被动的,一种是主动的。

被动的订阅消息指: 消息的发送需要用户的某些行为作为触发,然后小程序再针对性地向用户发送消息反馈。以电商类小程序为例,当用户支付成功之后,小程序会向用户推送一条消息,用户可以通过这条消息跟踪订单进度。

主动的订阅消息是指: 小程序可以定期或不定期地向用户发送消息通知,不需要用户的某些行为作为触发点。比如学习类小程序在每天的固定时间向用户发送消息,通知用户打卡。

在被动场景下,小程序会对用户的行为及时反馈,从而提升用户的产品体验,以及对小程序的认可度、依赖度。在主动场景下,小程序可以通过定期或不定期地发送通知,把离开的用户拉回来继续使用小程序。所以,这两种场景下的订阅消息都能帮小程序提高用户的留存率。

订阅消息属于微信提供的开放接口之一,调用有两种方式:

  • 传统的调用方式,掌握这种方式后,你会明白订阅消息完整链路的基本原理;
  • 云调用方式,教你用免鉴权的方式快速实现订阅消息的调用,提高研发效率。

订阅消息完整的流程可以简单概况成下面这张图:

Drawing 0.png

  1. 在发送消息之前必须获得用户授权,这是第一步也最基本的一步。
  2. 用户授权之后,你需要把授权信息记录下来,因为小程序的订阅消息能定义多种类型,每种都需要得到用户的授权后才可以发送。
  3. 消息的发送需要一个触发点,根据订阅消息的两种应用场景,触发点可以是用户的某个行为比如支付订单,也可以是小程序主动触发,比如定时发送。

这三个步骤是小程序订阅消息的基本流程。

配置消息模板

小程序的订阅消息分为:一次性订阅消息和长期订阅消息。

  • 一次性你可以简单理解成得到用户的授权之后只能发送一条消息,如果再有发送需求还要再次申请用户的授权。
  • 长期订阅消息可以得到用户授权后发送多条消息。不过目前长期订阅消息只向政务民生、医疗、交通、金融、教育等线下公共服务开放(后面也许会开放出更多领域)。
登录微信公众平台之后,消息模板的配置入口在小程序管理后台中的“功能”-“订阅消息”里,点击添加即可配置消息模板。

配置完消息模板之后会拿到一个模板ID。

获取用户授权

小程序SDK提供了一个用来获取用户授权的API:wx.requestSubscribeMessage。你要用这个 API 为每一个模板 ID 授权,调用方式如下:

wx.requestSubscribeMessage({
  tmplIds: ['tmplId_1','tmplId_2','tmplId_3'],
  success (res) {
    console.log(res)
    / **
    {
      errMsg: 'requestSubscribeMessage:ok',
      tmplId_1: 'accept',
      tmplId_2: 'reject',
      tmplId_3: 'ban' 
    }
    */
  }
})

其中参数 tmplIds 是一个数组,数组中的每个元素就是前面配置的消息模板 ID ,如果你需要一次性获取多个模板的用户授权就在 tmplIds 数组里写入多个元素。调用wx.requestSubscribeMessage 之后,用户会在小程序端收到一个弹窗:

Drawing 4.png

用户可以对列表中的每个消息模板分别授权,也可以全部勾选(也就是全部授权)。用户点击“允许”按钮之后,将会触发 wx.requestSubscribeMessage 的 success 回调函数,返回的数据结构可以参考上面的示例代码,每个模板 ID 都有一个状态标识,accept 代表得到授权、reject 代表未获得授权、ban 代表被后台封禁。当然,正常情况下是不会出现 ban 状态的,除非你的订阅消息涉嫌骚扰用户或内容违禁。

流程如图:

Drawing 6.png

发送消息的传统调用方式

调用微信开放接口需要临时凭证 access_token,发送订阅消息的接口作为微信开放接口的一员同样要遵循这条规则。

服务端需要调用微信提供的subscribeMessage.send接口向用户发送订阅消息,这个接口有几个请求参数需要你特别关注:Drawing 8.png

其中 template_id 是上一步得到用户授权的几个模板 ID 中的一个,请注意,你每次调用 subscribeMessage.send 接口只能发送一条消息给用户,而不能同时发送多条,这是微信的限制。touser 参数的值是接收消息的用户 openId,access_token 是临时凭证。

发送消息需要一个触发点(可以是用户的某个行为触发,也可以是服务端的定时任务,具体采用哪种方式需要根据业务场景来决定),以定时任务这种场景为例,对照下图讲解一下完整的发送消息流程:**

Drawing 9.png

  1. 首先,服务器需要设定一个定时任务,比如每天 9 点触发一次,发送消息。
  2. 触发之后,第一步先检查本地缓存中是否存在有效期之内的 access_token ,如果不存在就需要重新请求微信的服务获取一个新的。
  3. 拿到有效的 access_token 之后,第二步是通过 subscribeMessage.send 接口向微信服务器发送一个请求。
  4. 微信服务器收到请求之后,发送一条订阅消息给指定的 openId 对应的用户,便完成了整个发送消息的流程

以上就是发送订阅消息的传统调用方式。你也看到了,在这套流程中,需要比较繁重的服务端开发工作,比如设定定时任务、缓存管理等,非常的麻烦。

发送消息的云调用方式

微信小程序的订阅消息功能提供了云调用的支持,你可以不用关注鉴权,也就是不需要关注 access_token,直接在云函数里调用微信 SDK 提供的openapi.subscribeMessage.sendAPI 就行,参数跟 subscribeMessage.send 接口一样,只是不再需要 access_token 了。

const cloud = require('wx-server-sdk')
cloud.init()
exports.main = async (event, context) => {
  try {
    const result = await cloud.openapi.subscribeMessage.send({
        touser: 'OPENID',
        templateId: 'TEMPLATE_ID',
        // ...其他参数
      })
    return result
  } catch (err) {
    return err
  }
}

除了避免管理 access_token 的麻烦以外,在前面传统调用方式中还用到了一项能力:定时任务。这项需求云函数同样可以满足,你可以给云函数配置一个定时触发器,使用简单的 JSON 配置文件就可以设定一个定时任务。比如我刚刚说的每天 9 点触发一次就可以写成下面这种格式的云函数定时触发器:

{
  // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
  "triggers": [
    {
      // name: 触发器的名字,规则见下方说明
      "name": "myTrigger",
      // type: 触发器类型,目前仅支持 timer (即 定时触发器)
      "type": "timer",
      // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见下方说明
      "config": "0 0 9/23 1/1 * * *"
    }
  ]
}

通过上面两个步骤就完成了跟传统调用方式一样的功能,而整个发送消息的流程也得到了简化:

Drawing 11.png

对比这两种调用方式,你应该能够明显感受到不同。当然,这并不是说我们只学会用云调用而不用学习传统方式了。云调用的底层也是类似传统的调用方式,只不过帮你完成了定时任务、缓存管理这些服务端的开发工作而已。