uni-app 微信小程序发送订阅消息

这篇笔记📒主要记录uni-app微信小程序发送订阅消息,主要涉及的知识

  1. 小程序后台开通订阅消息功能
  2. 选择消息模板
  3. 一次性订阅和长期订阅的区别
  4. 通过uni.requestSubscribeMessage(Object object)调起订阅消息界面
  5. 调用小程序订阅消息接口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"
}

如果更换了模版 需要重新订阅新模版

相关推荐
@PHARAOH1 分钟前
WHAT - Tree Shaking 的前提是 ES Module
前端·webpack·ecmascript
鱼樱前端8 分钟前
📚 Vue Router 4 核心知识点(Vue3技术栈)面试指南
前端·javascript·vue.js
食指Shaye14 分钟前
Chrome 中清理缓存的方法
前端·chrome·缓存
午后书香25 分钟前
一天三场面试,口干舌燥要晕倒(二)
前端·javascript·面试
Book_熬夜!40 分钟前
CSS—补充:CSS计数器、单位、@media媒体查询
前端·css·html·媒体
人民广场吃泡面1 小时前
UniApp 运行的微信小程序如何进行深度优化
微信小程序·小程序·uni-app
编程毕设1 小时前
【含文档+PPT+源码】基于微信小程序的在线考试与选课教学辅助系统
java·微信小程序·小程序
程序员大澈2 小时前
4个 Vue 路由实现的过程
javascript·vue.js·uni-app
几度泥的菜花2 小时前
如何禁用移动端页面的多点触控和手势缩放
前端·javascript
狼性书生2 小时前
electron + vue3 + vite 渲染进程到主进程的双向通信
前端·javascript·electron