【小程序】如何封装订阅消息面向对象实现,附云开发实战案例
消息能力是小程序能力中的重要组成,为开发者提供了订阅消息能力,以便实现服务的闭环和更优的体验。【小日常 时间管理日历】小程序需要通知提醒用户更按时的完成习惯培养,在创建、完成习惯的情况下发起订阅以便于下次来打卡。
-
订阅消息推送位置:服务通知
-
订阅消息下发条件:开发者通过一定的方式触发用户主动订阅
-
订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面。
一、技术实现
1. 使用案例
把订阅消息内置成一个类,开发使用者只需要传入对应订阅的模板和通知的相关内容资料,通过面向对象的方法去调用。
// 创建习惯 or 完成打卡习惯 发起订阅消息
const tmplIds = ['66ZT3ijLzbrQSmlefWsOosXZz5EYcSxsizQUGMvMF90']
// habitInfo:需要提醒完成的习惯信息
const subMsg = new SubscribeMessage(tmplIds,{ habit: habitInfo })
subMsg.request()
具体的模板可以在小程序运营后台查看
2. 封装订阅类
- 第一步:创建对象,保存资料
class SubscribeMessage {
tmplIds = []; //需要订阅的消息模板的id的集合,一次调用最多可订阅3条消息
habit = null; //需要订阅的习惯
acceptList = []; //用户成功订阅列表
constructor(tmplIds, { habit = {} } = {}) {
this.tmplIds = tmplIds;
this.habit = habit;
}
...
}
- 第二步:发起订阅消息
/**
* @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化
/**
* @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);
},
});
});
}
- 第三步:处理订阅回调
// 用户点击订阅成功回调服务:调用接口服务保存对应需要处理的订阅消息
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、消息模板、习惯信息),初始化未完成
// 订阅消息云函数文件
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数据集未发送的订阅消息,开启定时触发器去查询当前时间是否存在需要通知打卡的信息
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;
}
};
添加定时触发器: 一分钟触发一次
{
"permissions": {
"openapi": ["subscribeMessage.send"]
},
"triggers": [
{
"name": "myTrigger",
"type": "timer",
"config": "0 */1 * * * * *"
}
]
}
三、项目体验
「小日常 时间管理日历」 是一款习惯养成追踪的工具类小程序。
- 不紧不慢,向上生长
- 大大的梦想也需要分解成每一个小日常的努力
转载自:https://juejin.cn/post/7290375566255276071