【小程序】如何封装订阅消息面向对象实现,附云开发实战案例

消息能力是小程序能力中的重要组成,为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验。【小日常 时间管理日历】小程序需要通知提醒用户更按时的完成习惯培养,在创建、完成习惯的情况下发起订阅以便于下次来打卡。

  • 订阅消息推送位置:服务通知

  • 订阅消息下发条件:开发者通过一定的方式触发用户主动订阅

  • 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面。

一、技术实现

1. 使用案例

把订阅消息内置成一个类,开发使用者只需要传入对应订阅的模板和通知的相关内容资料,通过面向对象的方法去调用。

js 复制代码
// 创建习惯 or 完成打卡习惯 发起订阅消息
const tmplIds = ['66ZT3ijLzbrQSmlefWsOosXZz5EYcSxsizQUGMvMF90']
// habitInfo:需要提醒完成的习惯信息
const subMsg = new SubscribeMessage(tmplIds,{ habit: habitInfo })
subMsg.request()

具体的模板可以在小程序运营后台查看

2. 封装订阅类

  • 第一步:创建对象,保存资料
js 复制代码
class SubscribeMessage {
  tmplIds = []; //需要订阅的消息模板的id的集合,一次调用最多可订阅3条消息
  habit = null; //需要订阅的习惯
  acceptList = []; //用户成功订阅列表

  constructor(tmplIds, { habit = {} } = {}) {
    this.tmplIds = tmplIds;
    this.habit = habit;
  }
  ...
 }
  • 第二步:发起订阅消息
js 复制代码
  /**
   * @description 请求发布订阅消息
   * errMsg 接口调用成功时errMsg值为'requestSubscribeMessage:ok'
   * TEMPLATE_ID [TEMPLATE_ID]是动态的键,即模板id,值包括'accept'、'reject'、'ban'、'filter'。
   * 'accept'表示用户同意订阅该条id对应的模板消息,
   * 'reject'表示用户拒绝订阅该条id对应的模板消息,
   * 'ban'表示已被后台封禁
   * 'filter'表示该模板因为模板标题同名被后台过滤。
   * 例如 { errMsg: "requestSubscribeMessage:ok", zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: "accept"}
   * 表示用户同意订阅zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE这条消息
   */
  async request() {
    try {
      const subRes = await this._requestSubscribe();
      console.log("subRes", subRes);
      for (let i = 0; i < this.tmplIds.length; i++) {
        console.log("subRes[this.tmplIds[i]]", subRes[this.tmplIds[i]]);
        if (subRes[this.tmplIds[i]] === "accept") {
          //成功同意的订阅消息
          this.acceptList.push(this.tmplIds[i]);
        }
        if (this.acceptList.length) {
          //有同意订阅消息的调用api
          this.addAccept();
        }
      }
    } catch (error) {
      //订阅接口调用失败的回调函数
      console.log("订阅接口调用失败", error);
    }
  }

微信订阅消息AP-Promise化

js 复制代码
  /**
   * @description 订阅消息promise化
   * @returns {Promise} requestSubscribeMessage
   */
  _requestSubscribe() {
    return new Promise((resolve, reject) => {
      console.log("_requestSubscribe", this.tmplIds);
      wx.requestSubscribeMessage({
        tmplIds: this.tmplIds,
        success(res) {
          console.log("_requestSubscribe", res);
          resolve(res);
        },
        fail(res) {
          reject(res);
        },
      });
    });
  }
  • 第三步:处理订阅回调
js 复制代码
  // 用户点击订阅成功回调服务:调用接口服务保存对应需要处理的订阅消息
  async addAccept() {
    for (let i = 0; i < this.acceptList.length; i++) {
      const habit = JSON.stringify(this.habit);
      wx.cloud
        .callFunction({
          name: "subscribe",
          data: {
            templateId: this.acceptList[i],
            content: habit,
            $url: "subMsg",
          },
        })
        .then((res) => {
          console.log("==res==", res);
        });
    }
  }
}

二、云开发- 订阅消息

1. 保存订阅信息

创建messages数据集,保存对应前端回调进来的订阅消息(用户openid、消息模板、习惯信息),初始化未完成

js 复制代码
// 订阅消息云函数文件
const cloud = require('wx-server-sdk')
const TcbRouter = require('tcb-router')
const rp = require('request-promise')
cloud.init({
    env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database() // 初始化数据库

// 云函数入口函数
exports.main = async (event, context) => {
  const app = new TcbRouter({
    event
  })

  app.router('subMsg', async(ctx, next) => {
    try {
      const {OPENID} = cloud.getWXContext();
      // 在云开发数据库中存储用户订阅的课程
      const result = await db.collection('messages').add({
        data: {
          touser: OPENID, // 订阅者的openid
          page: 'pages/punch-card/index', // 订阅消息卡片点击后会打开小程序的哪个页面
          templateId: event.templateId, // 订阅消息模板ID
          content: event.content,
          done: false, // 消息发送状态设置为 false
        },
      });
      ctx.body = {
        code: 200,
        data: result
      }
    } catch (err) {
      ctx.body = err;
    }

  })

  return app.serve()
}

2. 发送订阅信息

创建send 云函数,遍历message数据集未发送的订阅消息,开启定时触发器去查询当前时间是否存在需要通知打卡的信息

js 复制代码
const cloud = require('wx-server-sdk');

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

  try {
	// 从云开发数据库中查询等待发送的消息列表
	const {OPENID} = cloud.getWXContext();
    const messages = await db
      .collection('messages')
      // 查询条件这里做了简化,只查找了状态为未发送的消息
      // 在真正的生产环境,可以根据开课日期等条件筛选应该发送哪些消息
      .where({
        templateId: '66ZT3ijLzbrQSmlefWsOosXZz5EYcSxsizQUGMvMF90',
        done: false,
        touser: OPENID // 订阅者的openid
      })
      .get();
    // 循环消息列表
    const sendPromises = messages.data.map(async message => {
      try {
        const content = JSON.parse(message.content)
        const  clockTime = content.clockTime
        const clockDate =  clockTime.split(':')
        const currentDate =  new Date()
        const clockhour = Number(clockDate[0]);  
        const clockMinute = Number(clockDate[1]);  
        const currentHour = currentDate.getHours();  
        const currentMinute = currentDate.getMinutes();  
        
        // 是不是当前打卡的时间  
        const isCurentTime = clockhour == currentHour && clockMinute == currentMinute;  
        if(!isCurentTime) {
          return {
            'msg': '当前时间内没有打卡发送记录',
            isCurentTime,
            clockhour,
            currentHour,
            clockMinute,
            currentMinute
          }
        }
        // 发送订阅消息
        await cloud.openapi.subscribeMessage.send({
          touser: message.touser,
          page: message.page,
          // miniprogramState: 'developer',
          data: {
            thing1: {
              "value": `小日常 | ${content.name}`
            },
            thing2: {
              "value": '赶紧记录一下今天的习惯完成情况吧!'
            }
          },
          templateId: '66ZT3ijLzbrQSmlefWsOosXZz5EYcSxsizQUGMvMF90',
        });
        // 发送成功后将消息的状态改为已发送
         db.collection('messages')
          .doc(message._id)
          .update({
            data: {
              done: true,
            },
          });
      } catch (e) {
        return e;
      }
    });

    return Promise.all(sendPromises);
  } catch (err) {
    return err;
  }
};

添加定时触发器: 一分钟触发一次

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

三、项目体验

「小日常 时间管理日历」 是一款习惯养成追踪的工具类小程序。

  • 不紧不慢,向上生长
  • 大大的梦想也需要分解成每一个小日常的努力

相关推荐
小白学习日记36 分钟前
【复习】HTML常用标签<table>
前端·html
丁总学Java1 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele1 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀1 小时前
CSS——属性值计算
前端·css
xgq2 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试
用户3157476081352 小时前
前端之路-了解原型和原型链
前端
永远不打烊2 小时前
librtmp 原生API做直播推流
前端
北极小狐2 小时前
浏览器事件处理机制:从硬件中断到事件驱动
前端
无咎.lsy2 小时前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec2 小时前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron