【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

前言

最近用uniapp云开发搞了个小程序,大部分功能都已经实现了,结果卡死在了订阅消息这个功能上,由于订阅是前端实现,发送是后端实现,而uniapp云开发里面又单独封装了一个公共模块供开发者使用,所以导致文档看起来就很乱,不知道该看哪个了。

经过反复研究和沸点JYM给的思路,算是将这个功能完成了,接下来就把实现过程和踩过的坑记录一下,也给大家一个参考。

这里只讲解uniapp云开发中的公共模块uni-subscribemsg的使用,主要结合云开发去实现,不使用其他后端。

uni-subscribemsg 公共模块

这个公共模块将微信公众号模板消息、微信小程序订阅消息都封装起来了,可以很方便的使用云函数/云对象去操作发送。

点击进入 uni-subscribemsg 公共模块 插件地址

按照一般的思路,我们会在云函数中直接请求微信服务端接口

POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN

// 在云函数中请求微信服务端API(发送订阅消息)
const sendMsg = await uniCloud.httpclient.request(
"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + access_token, {
method: "POST",
...
});

通过引入公共模块之后,就可以直接使用公共模块中的方法去发送订阅消息

// 引入uni-subscribemsg公共模块
const UniSubscribemsg = require('uni-subscribemsg');
// 初始化实例
let uniSubscribemsg = new UniSubscribemsg({
dcloudAppid: "你项目的dcloudAppid",
provider: "weixin-mp",
});
// 发送订阅消息
let res = await uniSubscribemsg.sendSubscribeMessage({
touser: "用户openid",
template_id: "消息模板id",
page: "pages/index/index", // 小程序页面地址
miniprogram_state: "developer", // 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
lang: "zh_CN",
data: {
name1: {
value: "张三"
},
time2: {
value: "2023-12-21 15:30:20"
}
}
});

基础部分已说明,公共模块中的代码会在云函数/云对象中进行说明

获取公共模块中的参数

dcloudAppid: “你项目的dcloudAppid”

此参数在uniCloud后台获取点击进入uniCloud后台

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 列表中的Appid即为此参数的值

template_id: “消息模板id”

此参数在小程序后台获取点击进入小程序后台

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 侧边栏选择订阅消息

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 在我的模板中点击右侧 选用 按钮

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 右侧搜索 签到 就会出现类似的订阅模板了,直接选用你想要的就可以了

  • 你可以搜索其他关键词,和你的小程序类目相关即可

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 添加好模板之后,在模板列表中就会出现一个模板ID,复制下来,粘贴到发送消息的参数位置

touser: “用户openid”

此参数在用户登录时获取,可以参考我写的这篇文章。使用 Uniapp + UniCloud 云开发微信小程序获取用户信息

建表

根据自己的需求建立用户表签到表等,下面只介绍消息订阅表

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 在项目 – uniCloud – database上右键,新建 DB Schema

// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": false,
"create": false,
"update": false,
"delete": false
},
"properties": {
"_id": {
"description": "ID,系统自动生成"
},
"user_id": {
"title": "关联用户表中的id,订阅者",
"bsonType": "string",
"trim": "both"
},
"openid": {
"title": "微信openid",
"bsonType": "string",
"trim": "both"
},
"state": {
"title": "订阅状态",
"bsonType": "int",
"trim": "both",
"defaultValue": 1,
"enum": [{
"text": "正常",
"value": 1
}
],
"description": "过滤掉非正常的用户,方便发送订阅消息"
},
"type": {
"title": "类型",
"bsonType": "int",
"trim": "both",
"enum": [{
"text": "签到",
"value": 1
}
]
}
}
}
  • 具体字段可以根据自己的需要去修改

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 新建完成后,右键即可上传至云空间了

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 云空间中有了这张表之后,就表示创建完成了。

用户触发订阅消息

  • 当用户签到完成后,弹出订阅框(如果用户没有勾选“总是保持以上选择,不再询问”,则每次签到都会弹出)

  • 用户订阅成功之后,将订阅状态存入表中(上面新建的用户订阅表),方便发送时获取openid

const _this = this
// 弹出签到提醒的订阅消息,让用户订阅
uni.requestSubscribeMessage({
tmplIds: [""wb0SXQ86lR*****************liHZ7Y"],
success: (res) => {
// 判断是否订阅了
if (res["
wb0SXQ86lR*****************liHZ7Y"] === 'accept') {
// 订阅之后,就得将此用户信息记录下来,方便后续发送订阅消息
uniCloud.callFunction({
name: 'a_subscribe_sign',
data: {
action: 'setSubscribeUserInfo',
user_id: _this.user._id,
openid: _this.user.openid,
state: 1, // 正常
type: 1 // 签到
},
success: (res) => {
uni.showToast({
title: '订阅成功',
icon: 'success'
})
},
fail: () => {
uni.showToast({
title: '订阅失败',
icon: 'error',
duration: 2000
})
}
})
}
}
});
  • 用 * 号加密的部分是微信后台自己添加的订阅模板ID哦!

  • 其他参数根据自己的需求去获取即可。

云对象发消息

新建云对象

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 在项目 – uniCloud – cloudfunctions目录上右键 – 新建云函数/云对象

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 在弹出框里面选择云对象

  • 输入云对象名称

  • 点击 添加公共模块或扩展库依赖

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 管理扩展库中选择 uni-subscribemsg公共模块

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 新建完成后,就可以在目录中看到新建的云对象了

// 一个空的云对象代码
module.exports = {
_before: function() {

}
}

云对象定时发送

由于签到的订阅消息是在用户订阅之后,每天都会发送的,所以需要给一个定时任务,便于每天定时触发

注意:下面的操作均在DCloud控制台中完成

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 根据图片中的步骤说明,找到对应的云对象,点击后面的 详情 按钮

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

  • 进入详情页后,找到 定时器触发 模块,点击 编辑 按钮

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

["0 0 18 * * *"]
  • 将上面这个数组复制进去即可

  • 表示 每天下午六点整 准时触发

如需修改定时器时间,可以点击查看定时器文档

定时任务方法

  • 在uniCloud中,给云对象内置了一个定时触发的方法

module.exports = {
_timing: function () {
console.log('triggered by timing')
}
}
  • _timing就是内置的定时触发方法

接下来,我们就可以把发送订阅消息的代码复制进来了,引入模块的内容放在module.exports外层。

此处的源码就是上面介绍公共模块时的代码,稍加修改即可。

// 云对象教程: https://uniapp.dcloud.net.cn/uniCloud/cloud-obj
// jsdoc语法提示教程:https://ask.dcloud.net.cn/docs/#//ask.dcloud.net.cn/article/129
// 引入uni-subscribemsg公共模块
const UniSubscribemsg = require('uni-subscribemsg');
// 初始化实例
let uniSubscribemsg = new UniSubscribemsg({
dcloudAppid: "__U*******B9", // https://dev.dcloud.net.cn/pages/app/list
provider: "weixin-mp",
});

// 这里是签到定时任务
// 会定时推送微信订阅消息给用户

module.exports = {
_before: function() { // 通用预处理器

},
// 定时触发内置方法
_timing: async function() {
// 发送订阅消息
let res = await uniSubscribemsg.sendSubscribeMessage({
touser: "olhn80For3o23OrxC*********", // 用户openid
template_id: "wb0SXQ86lRoa*************liHZ7Y", // 消息模板id
page: "pages/login/login", // 小程序页面地址
miniprogram_state: "trial", // 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
lang: "zh_CN",
data: { // 模板参数
phrase6: {
value: "未签到"
},
thing2: {
value: "签到最高可获得20积分哦~"
},
time3: {
value: "2019年10月1日 15:01:01"
},
thing5: {
value: "点击去签到"
}
}
});
}
}

  • 源码中加了 * 号的都是需要自己定义的内容,均在上文中有讲解说明

订阅消息中的 data 对象中的内容,需要和订阅消息模板中的内容保持一致。(此处有不明白的,可以评论中提问)

此时就可以等待下午六点准时触发定时任务后下发订阅通知

【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

云函数发消息

由于云对象中无法调用云函数(获取用户openid)的接口,所以放弃了使用云对象下发定时任务的方法

新建云函数

  • 和新建云对象是一样的,只是选项不同哦!

  • 新建云函数的时候同样需要引入公共模块

云函数定时发送

在云函数中,也有一种方法去判断定时任务,通过云函数自带的 context 字段

exports.main = async (event, context) => {
let source = context.SOURCE // 当前云函数被何种方式调用
// client 客户端callFunction方式调用
// http 云函数url化方式调用
// timing 定时触发器调用
// server 由管理端调用,HBuilderX里上传并运行
// function 由其他云函数callFunction调用
}
  • 会根据source最终的值去判断是否为定时任务触发的

let source = context.SOURCE // 云函数调用来源
// 判断来源是否为定时任务触发调用
if (source === 'timing') {
// 这里面就是云对象中定时任务方法里面的代码,复制进来即可
}

动态获取用户openid

根据建表时定义的字段,我们可以将statetype作为查询参数

const db = uniCloud.database();
// 获取 `a-subscribe-user` 集合的引用(定于用户表)
const a_subscribe_sign = db.collection('a-subscribe-user');

// 循环判断客户端传递过来的 action
// 通过 action 判断请求对象
let result = {};
switch (event.action) {
// setSubscribeUserInfo用户触发订阅时的方法,添加到订阅用户表中
case 'setSubscribeUserInfo':
const res_sign = await a_subscribe_sign.add({
user_id: event.user_id,
openid: event.openid,
state: event.state,
type: event.type
})
break;
// getAllOpenId获取所有订阅的openid
case 'getAllOpenId':
// 获取所有正常用户的openid
// 用于发送订阅消息
const res_all_val = await a_subscribe_sign.where({
state: 1,
type: 1
}).get()
return res_all_val.data
}

将方法定义好之后,我们就可以在定时任务判断逻辑中引用了。

云函数完整代码

下面是云函数的所有代码,可以根据注释进行理解

'use strict';
exports.main = async (event, context) => {
//event为客户端上传的参数
// console.log('event : ', event)

const db = uniCloud.database();
// 获取 `a-subscribe-user` 集合的引用(定于用户表)
const a_subscribe_sign = db.collection('a-subscribe-user');

// 条件查询必备语法
// const dbCmd = db.command

// 循环判断客户端传递过来的 action
// 通过 action 判断请求对象
let result = {};
switch (event.action) {
case 'setSubscribeUserInfo':
const res_sign = await a_subscribe_sign.add({
user_id: event.user_id,
openid: event.openid,
state: event.state,
type: event.type
})
break;
case 'getAllOpenId':
// 获取所有正常用户的openid
// 用于发送订阅消息
const res_all_val = await a_subscribe_sign.where({
state: 1,
type: 1
}).get()

return res_all_val.data
break;
}




// 引入uni-subscribemsg公共模块
const UniSubscribemsg = require('uni-subscribemsg');
// 初始化实例
let uniSubscribemsg = new UniSubscribemsg({
dcloudAppid: "__UNI__C40B1B9", // https://dev.dcloud.net.cn/pages/app/list
provider: "weixin-mp",
});

let source = context.SOURCE // 云函数调用来源

// 判断来源是否为定时任务触发调用
if (source === 'timing') {

console.log('获取用户信息')

let callFunctionResult = await uniCloud.callFunction({
name: "a_subscribe_sign",
data: {
action: 'getAllOpenId'
}
})

console.log('callFunctionResult*****', callFunctionResult.result)

// 获取到所有数据后,循环拿出openid
let openids = []
for (let i in callFunctionResult.result) {
openids.push(callFunctionResult.result[i].openid)
}

// 过滤掉重复的openid
openids = [...new Set(openids)]

// 获取时间,并格式化
function formatDate(date, cut) {
var date = new Date(date);
var YY = date.getFullYear() + cut;
var MM =
(date.getMonth() + 1 < 10 ?
"0" + (date.getMonth() + 1) :
date.getMonth() + 1) + cut;
var DD = date.getDate() < 10 ? "0" + date.getDate() : date.getDate();
var hh =
(date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":";
var mm =
(date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) +
":";
var ss = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
return YY + '年' + MM + '月' + DD + '日' + " " + hh + mm + ss;
}

console.log('我是定时任务!', openids)

// 循环发送订阅消息(给所有获取到的openid)
for (let i in openids) {
// 发送订阅消息
let res = await uniSubscribemsg.sendSubscribeMessage({
touser: openids[i], // 用户openid
template_id: "wb0SXQ86lRoaJO8j4DxheOKpgVUuw-iER-gDRliHZ7Y", // 消息模板id
page: "pages/login/login", // 小程序页面地址
miniprogram_state: "trial", // 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
lang: "zh_CN",
data: { // 模板参数
phrase6: {
value: "未签到"
},
thing2: {
value: "签到最高可获得20积分哦~"
},
time3: {
value: formatDate()
},
thing5: {
value: "点击去签到"
}
}
});
console.log('订阅消息打印一下****', res)
}

}

//返回数据给客户端
// return event
};

总结

由于微信小程序订阅消息的限制,无法一次性批量触发,只能通过循环获取openid的方式去给每个用户发送。

由于uniapp云对象的限制,有些逻辑只能放在云函数里面,如果有更好的云对象操作方法,可以在评论区讨论一下,毕竟云对象我觉得相对于云函数来说是比较简单的。


原文始发于微信公众号(猿来是前端):【订阅消息】uniapp云开发给订阅用户发送微信小程序订阅消息(实战 + 踩坑)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/250299.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!