在准备开发这个功能之前,请确保你已经阅读过云开发文档和以下相关官方文档。
我们来设定一个需求场景。以小程序【抽奖助手】为例,用户参与抽奖后,需要在开奖时间发送给用户开奖结果通知。这个通知采用模板消息形式下发。
先整理一下思路,实现这个功能我们需要哪些模块?
- 定时任务执行器。根据任务类型调用相应任务处理程序。
- 开奖任务处理程序,开奖后发送模板消息,通知用户结果。
- 云函数中调用 sendTemplateMessage 后端接口,发送模板消息。
- 周期获取AccessToken。请求后端接口需要用AccessToken,周期更新AccessToken,放入数据库中,随用随取。
模块实现
1.定时任务执行器
以云数据库形式实现。添加一个定时任务就是在该集合增加一条记录,移除同理。
记录字段设计:
timeingTask{
_id:
taskType: //任务类型,决定如何处理这个任务
execTime: // 触发时间。到达这个时间开始执行。
data:{} // 必要数据
}
然后设置云函数周期执行。每分钟查询一次该定时任务数据库,是否有任务到达执行时间。如果有则根据类型进行处理,并在数据库中移除该任务。
const cloud = require('wx-server-sdk')
cloud.init({
env: '你的云环境ID'
})
const db = cloud.database()
exports.main = async(event, context) => {
const execTasks = []; // 待执行任务栈
// 1.查询是否有定时任务。(timeingTask)集合是否有数据。
let taskRes = await db.collection('timeingTask').limit(100).get()
let tasks = taskRes.data;
// 2.定时任务是否到达触发时间。只触发一次。
let now = new Date();
try {
for (let i = 0; i < tasks.length; i++) {
if (tasks[i].execTime <= now) { // 时间到
execTasks.push(tasks[i]); // 存入待执行任务栈
// 定时任务数据库中删除该任务
await db.collection('timeingTask').doc(tasks[i]._id).remove()
}
}
} catch (e) {
console.error(e)
}
// 3.处理待执行任务
for (let i = 0; i < execTasks.length; i++) {
let task = execTasks[i];
if (task.taskType == 1) { // 定时开奖任务
const kaiJinag = require('kaiJiang.js')
try {
await kaiJinag.kai(task.data.activity_id)
} catch(e) {
console.error(e)
}
}
}
}
使云函数每分钟执行的触发器代码:
{
"triggers": [
{
"name": "timeingTaskExecutor",
"type": "timer",
"config": "0 */1 * * * * *"
}
]
}
2.开奖任务处理程序 kaijiang.js
const cloud = require('wx-server-sdk')
const templateMessage = require('templateMessage.js')
const COLL_FIELD_NAME = 'publicField';
const FIELD_NAME = 'ACCESS_TOKEN'
const MSGID = '你的模板消息ID';
cloud.init({
env: '你的云环境ID'
})
const db = cloud.database()
const kai = async activity_id => {
// 根据活动id,获取参与用户信息,获取到用户的 openid 和 formid.
// 开奖程序省略
// 从数据库中获取AccessToken
let tokenRes = await db.collection(COLL_FIELD_NAME).doc(FIELD_NAME).get();
let token = tokenRes.data.token; // access_token
let page = '点击模板消息,想要打开的小程序页面';
let msgData = {
"keyword1": {
"value": activity.prizeName
},
"keyword2": {
"value": "你参与的抽奖活动正在开奖,点击查看中奖名单"
},
};
let openid = '用户openid';
let formid = '用户formid';
await templateMessage.sendTemplateMsg(token, MSGID, msgData, openid, formid, page);
}
module.exports = {
kai: kai,
}
3.发送模板消息 templateMessage.js
封装在一个 js 文件里,传入必要参数调用即可。
const rp = require('request-promise');
const sendTemplateMsg = async (token, msgid, msgData, openid, formid, page) => {
await rp({
json: true,
method: 'POST',
uri: 'https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=' + token,
body: {
touser: openid,
template_id: msgid,
page: page,
form_id: formid,
data: msgData
}
}).then(res => {
}).catch(err => {
console.error(err)
})
}
module.exports = {
sendTemplateMsg: sendTemplateMsg,
}
4.周期获取AccessToken
使用云函数触发器,使云函数每小时请求一次AccessToken,并将AccessToken存入云数据库中。
const cloud = require('wx-server-sdk')
const rq = require('request-promise')
const APPID = '你的APPID';
const APPSECRET = '你的APPSECRET';
const COLLNAME = 'publicField';
const FIELDNAME = 'ACCESS_TOKEN'
cloud.init({
env: '你的云环境ID'
})
const db = cloud.database()
exports.main = async(event, context) => {
try {
let res = await rq({
method: 'GET',
uri: "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APPID + "&secret=" + APPSECRET,
});
res = JSON.parse(res)
let resUpdate = await db.collection(COLLNAME).doc(FIELDNAME).update({
data: {
token: res.access_token
}
})
} catch (e) {
console.error(e)
}
}
使云函数一小时执行一次的触发器代码:
{
"triggers": [
{
"name": "pollGetAccessToken",
"type": "timer",
"config": "0 0 */1 * * * *"
}
]
}
这是我做的产品册小程序中的部分代码,目前项目还没有发布,发布后会开源出来。项目中用到了挺多开源组件,给我很大的帮助,希望我的分享也可以帮助到一些人。
有问题可以在公众号后台联系我,我看到都会回复的。
可是这样不是很消耗云资源吗,还有其他方法吗
最核心的是如何偷偷摸摸收集form_id
其余的都可以去参考文档了。
用户触发才能发送把。
以小程序【抽奖助手】为例,用户参与抽奖后,需要在开奖时间发送给用户开奖结果通知。
“用户参与抽奖”,这个动作获取formid