小程序发送模版消息之 nodejs 实现
功能分析
现需要实现一个用户报名成功通知到功能,管理员在后台审核之后,会通过用户的申请,同时发送小程序报名成功的模版消息到用户的微信上。
首先需要分析一下微信发送模版消息的接口
//模版消息的结构
let opts = {
touser: param.openid, //目标用户的openid
template_id: template_id, //模版消息的id,需要在小程序管理配置获得
form_id: param.formId,//当前用户通过表单提交行为获得的formId 有效期7天
data: {
"keyword1": {
"value": '值1',
"color": "#1d1d1d"
},
"keyword2": {
"value": 值2,
"color": "#1d1d1d"
}
}
}
//发送小程序模版消息的请求
let data = {
method: 'POST',
//请求接口 需要一个生成有效期为两小时的 accessToken,见下方代码
url: `https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=${accessToken}`,
//将模版消息的结构字符画
body: JSON.stringify(opts),
header: {
'content-type': 'application/json' // 默认值
}
}
//请求获取临时 AccessToken 的 Promise函数
const request = require('request');
const fetchAccessTokenReq = new Promise((resolve, reject) => {
let APPID = '你的小程序appid';
let secret = '你的小程序appsecret';
let url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${secret}`
request.get(url, (err, response, data) => {
if (err) {
resolve(false);
} else {
let AccessToken = JSON.parse(data);
resolve(AccessToken);
}
})
})
表单收集
假设活动字段设计为如下
const bookingrecord = mongoose.Schema({
activity_id: String, //用户请求的参加活动的id
activity_content: Object, //活动对象
openid: String, //参加用户的openid
submit: Object,// 提交表单
isAllow: Boolean, //是否已经同意
formId:String //表单id 也可以为单个用户保存
})
在小程序的form
表单中间通过 submit
提交获取到 formid
,通过 post
请求提交并保存到数据库
// 在page 的 form 表单中收集
<form class='section' bindsubmit="bindFromSubmit" report-submit='true' report-submit='true'>
<text class='fontbold'>活动报名</text>
<text class='col'>* 真实姓名</text>
<input placeholder='真实姓名' data-key='realName' name=''></input>
<text class='col'>* 联系方式</text>
<input placeholder='可以联系到你的方式 微信/手机' data-key='contact'' name=''></input>
<text class='col'>* 备注</text>
<textarea placeholder='备注' data-key='remark'/ name=''>
<button class='main' form-type='submit' hover-class='none' wx:if='{{submit.realName && submit.contact && submit.remark}}'>提交报名</button>
<button class='default' hover-class='none' wx:else>不可提交</button>
</form>
// submit.js
bindFromSubmit: function(e) {
let formId = e.detail.formId;
}
这样就能为每一个用户申请的报名记录记录一个 formId
,同时也应该尽可能多的为单个用户保存,以防其他需求。
使用Promise整合请求
当管理员通过申请者的申请时,会将单条申请记录的字段更新,然后发送一条模版消息到用户的微信上,假设 formid
有效的情况下,使用 promise
依次处理请求,我尽量将代码写的浅显易懂。
// 通过报名申请并向报名者发送小程序模版消息
router.post('/api/allowApplyAndSendTemplateMsg', (req, res) => {
let _body = req.body;
let template_id = `模版id`; //模版消息的id
// 数据库操作
const updataCurrentOjbectAllow = new Promise((resolve, reject) => {
db.bookingrecord.updateOne({ '_id': object._id }, { $set: { 'isAllow': true } }).exec((err, doc) => {
if (err) {
resolve(false)
} else {
resolve(true);
}
})
})
// 获取token
const fetchAccessTokenReq = new Promise((resolve, reject) => {
const APPID = 'appid';
const secret = 'secret';
const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${secret}`
request.get(url, (err, response, data) => {
if (err) {
resolve(false)
} else {
let AccessToken = JSON.parse(data);
resolve(AccessToken);
}
})
})
// promise all
Promise.all([updataCurrentOjbectAllow, fetchAccessTokenReq]).then((response) => {
let db_options_res = response[0];
let accessToken = response[1].access_token;
sendTemplateMessage(object, db_options_res, accessToken).then(_res => {
if (_res) {
return res.json({
code: 1,
msg: '发送成功',
result: 'send template message success'
})
}
}).catch(err => {
// 错误处理
})
}).catch((err) => {
// 错误处理
})
const sendTemplateMessage = (param, dbres, accessToken) => {
return new Promise((resolve, reject) => {
if (!dbres) {
return res.json({
code: 0,
msg: '数据库写入失败',
result: 'db updata fail'
})
}
let opts = {
touser: param.openid,
template_id: template_id,
form_id: param.formId,
data: {
"keyword1": {
"value": '说明2',
"color": "#1d1d1d"
},
"keyword7": {
"value": '说明2',
"color": "#1d1d1d"
}
}
}
let data = {
method: 'POST',
url: `https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=${accessToken}`,
body: JSON.stringify(opts),
header: {
'content-type': 'application/json' // 默认值
}
}
request(data, function(error, response, data) {
let result = JSON.parse(data);
if (result.errcode == '0' && result.errmsg === 'ok') {
resolve(result)
} else {
reject(result)
}
});
})
}
})
其他问题 ⚠️
accessToken
在使用过之后会失效,需要重新获取。