这篇笔记📒主要记录uni-app微信小程序发送订阅消息,主要涉及的知识
- 小程序后台开通订阅消息功能
- 选择消息模板
- 一次性订阅和长期订阅的区别
- 通过uni.requestSubscribeMessage(Object object)调起订阅消息界面
- 调用小程序订阅消息接口api.weixin.qq.com/cgi-bin/mes... 发送订阅消息
实现效果图
小程序后台开通订阅消息功能
登录小程序管理后台,在基础功能 > 订阅消息那里,开通订阅消息功能,就可以选择消息模板了
选择消息模板
开通后我们就可以看到消息模板了(一次性订阅)
选择自己想要的模板
在我的模板详情里点进去
一次性订阅和长期订阅的区别
类别 | 一次性订阅 | 长期订阅 |
---|---|---|
消息次数 | 只能发送一次 | 可以多次发送 |
触发条件 | 每次都需用户手动授权 | 一次授权,满足条件即可持续触发 |
有效期 | 24小时内有效 | 直到用户主动取消授权 |
用户体验 | 较繁琐,每次都需授权 | 用户体验更好,仅需一次授权 |
适用场景 | 订单状态变更、物流发货提醒、活动开始通知等 | 医疗预约提醒、天气预警、公共交通通知等 |
示例 | 用户下单后,选择订阅订单发货通知,仅通知一次 | 用户订阅天气预警服务,每次有异常天气都会收到推送 |
目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务
长期订阅我这个分类没有模板
通过uni.requestSubscribeMessage(Object object)调起订阅消息界面
代码实现,通过 uni.requestSubscribeMessage发起订阅消息请求
用户发生点击行为或者发起支付回调后,才可以调起订阅消息界面
vue
const handleBook = () => {
let id = "5HlqLI0I0I4m-peYdNEsr4CcVAH4aeKRKt2Khms2Hbg"; // 替换你自己的模板ID 模板详情那里就有模板ID
uni.requestSubscribeMessage({
tmplIds: [id],
success(res) {
if (res[id] !== "accept") {
// 用户未授权或选择拒绝,引导前往设置页面
uni.showModal({
title: "订阅失败",
content: "您已设置不再询问,请前往设置开启订阅提醒。",
confirmText: "去设置",
success(modalRes) {
if (modalRes.confirm) {
openSetting();
} else {
uni.showToast({
title: "您可能错过重要通知",
icon: "none",
});
}
},
});
} else {
uni.showToast({
title: "订阅成功",
icon: "success",
});
reserveCourse();
}
},
});
};
上面的代码里有加,如果用户拒绝消息推送的逻辑,openSetting(),我们要在这个方法里写如果用户拒绝消息推送,就引导用户打开消息设置界面,在这个界面授权,反正一定要消息推送😂, 以防用户误点了拒绝,又勾选了总是保持以上选择,不再询问
,不会重新弹出消息授权界面
javascript
const openSetting = () => {
uni.openSetting({
success(res) {
console.log("设置界面返回:", res);
if (res.subscriptionsSetting) {
console.log("订阅消息授权状态:", res.subscriptionsSetting);
}
},
fail(err) {
console.error("打开设置失败:", err);
},
});
};
到这一步,授权就通过了
调用小程序订阅消息接口 发送订阅消息
小程序订阅消息微信文档, 我们需要接口调用凭证access_token, 接收者(用户)的 openid,
js
https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html
获取用户的openid
- openid是微信用户在不同类型的产品的身份ID
- 微信用户访问公众号、小程序、移动应用、网站应用、小商店等都会有唯一的openid, 但同一个微信用户访问不同的产品生成的openid也是不一样的
参考文档,要调用小程序登录接口,
js
GET https://api.weixin.qq.com/sns/jscode2session
因为我们用的是uni-app, 咱们调用uni.login接口,
vue
const getOpenid = () => {
uni.login({
provider: "weixin", // 指定微信登录
success: (res) => {
// res.code 调用登录接口需要
const existingUserInfo = uni.getStorageSync("userInfo") || {};
// 我们通过云对象的方式调用登录接口
uniCloud
.importObject("cancelCourseMsg")
.getOpenid({
code: res.code,
userId: existingUserInfo.userId,
})
.then((res) => {
console.log("res---openid---999---", res);
})
.catch((err) => {
console.error("openid", err);
});
},
});
};
getOpenid()
调用登录接口的云对象
js
const appid = "xxxx"; // 替换为你的小程序 AppID
const secret = "xxxx"; // 替换为你的 AppSecret
const db = uniCloud.database();
module.exports = {
/**
* 获取用户openid
* @param {Object} event
* @returns {Object}
*/
async getOpenid(event) {
const { code, userId } = event; // 从前端传入 userId
const db = uniCloud.database();
const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${appid}&secret=${secret}&js_code=${code}&grant_type=authorization_code`;
const axios = require("axios");
try {
const response = await axios.get(url);
const data = response.data;
if (data.openid) {
const openid = data.openid;
const unionid = data.unionid;
// 查询当前用户是否存在(根据 userId)
const userCollection = db.collection("users");
const queryResult = await userCollection.doc(userId).get();
if (queryResult.data.length > 0) {
// 如果用户已存在,更新记录
await userCollection.doc(userId).update({
openid: openid,
unionid: unionid,
updateTime: new Date(),
});
} else {
// 如果用户不存在,可以选择新增或报错
return {
success: false,
message: "用户不存在",
};
}
// 返回 openid 和 session_key
return {
openid: openid,
session_key: data.session_key,
unionid: unionid,
};
} else {
return {
success: false,
error: data,
};
}
} catch (err) {
console.error("获取openid失败", err);
return {
success: false,
error: err,
};
}
},
}
在云对象目录安装axios 不然会提示没找到axios 上面的代码中,我把openid存到数据库里,
因为同一个用户小程序的openid是不会变的
,到这一步就可以获取openid了
调用发送消息接口
发送订阅消息的云对象方法,注意我这里的参数thing28 thing29 thing4都是在模版详情里的,不能随便写,
js
async sendCancelSubscribeMessage(event) {
const { openid, courseName, courseTime, courseTeacherName } = event;
const axios = require("axios");
// 获取 access_token
async function getAccessToken(appid, secret) {
const response = await axios.get(
`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`
);
return response.data.access_token;
}
try {
const accessToken = await getAccessToken(appid, secret);
const url = `https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=${accessToken}`;
const messageData = {
touser: openid,
template_id: "5HlqLI0I0I4m-peYdNEsr4CcVAH4aeKRKt2Khms2Hbg",
page: "pages/index/index",
data: {
thing28: {
value: courseName, // 课程名称
},
time30: {
value: courseTime, // 上课时间
},
thing29: {
value: courseTeacherName, // 上课教练
},
thing4: {
value: "预约人数不足", // 预约状态,
},
},
};
const result = await axios.post(url, messageData);
return result.data;
} catch (err) {
console.error("订阅消息发送失败", err);
return {
success: false,
error: err,
};
}
},
我们在前端调用下这个云对象方法,
vue
watchEffect(async () => {
if (isCancelled.value) {
try {
const userCollection = db.collection("users");
// 查询 openid
const res = await userCollection
.doc(userInfo.userId)
.field("openid")
.get();
if (res.result.data && res.result.data.length > 0) {
const openid = res.result.data[0].openid;
// 调用云对象发送订阅消息
const cancelCourseMsg = uniCloud.importObject("cancelCourseMsg");
const startTime = getStartTime().getTime();
const sendRes = await cancelCourseMsg.sendCancelSubscribeMessage({
openid: openid,
courseName: props.courseInfo.courseType,
courseTime: getClassStartTime(startTime), // YYYY-MM-DD HH:MM
courseTeacherName: props.courseInfo.courseTeacherName,
});
console.log("订阅消息发送成功:", sendRes);
} else {
console.warn("未找到用户 openid,无法发送订阅消息");
}
} catch (err) {
console.error("获取用户 openid 失败", err);
}
}
});
就可以了,如果出现下面这种,就是参数thing4有问题
js
{
errcode: 47003
errmsg: "argument invalid! data.thing4.value is empty rid: 677e5b80-35d4f4d4-21d4e906"
}
如果出现这种,就是因为订阅消息已经发送过一次,就不会再次出发,需要重新触发订阅
js
{
errcode: 43101,
errmsg: "user refuse to accept the msg rid: 677f443f-2d545c78-429fe0a5"
}
如果更换了模版 需要重新订阅新模版