微信小程序定时订阅消息问题

订阅消息一次授权多次接收

个人开发只能订阅一次性消息,一次订阅授权只能发送一次信息,如何解决?

明确几个概念

  1. 订阅授权次数跟发送消息次数的关系
    • 用户授权订阅一次,才能发送一次消息。发送一次消息,消耗掉一次授权允许次数。
    • 授权次数可以累加。用户勾选总是保持以上选择,下次订阅消息不会再弹窗。
  2. 授权操作必须用户点击才能授权。即使点击总保持以上选择,还是需要用户去点击才能授权。所以函数调用授权操作或页面初始化加载授权操作都是不行的,必须由用户点击才能授权。

思考想法

  1. 每个订阅api一次最多授权3条模板,选取三个相近模板,在授权操作时同时授权三个模板。
  2. 点击率频繁的按钮或者可能点击的地方绑定授权操作,空白地方大面积透明按钮绑定授权操作。(此方案建立在用户第一次授权时点击了总保持以上选择,否则频繁弹窗影响用户体验)
  3. 设置每日签到或者消息订阅提示按钮,在按钮的处绑定授权。

实际解决

由于我的项目单纯采用云函数和云数据库开发,没有后端。所以以下解决方案针对云开发。

  1. 一号方案确定是相近的模板不容易找到,最多只能订阅三次,所以没有采用。

  2. 二号方案:点击跳转事件绑定授权(用户第一次授权完选择总保持以上选择后,以后授权用户是无感的)

    1
    2
    3
    4
    5
    6
    <view class="view_3">
    <navigator url="/pages/run/run" class="list-item" bindtap="shouquan"> <!--绑定授权事件-->
    <image class="item-image" src="/images/跑步.png" />
    <text class="item-text">跑步</text>
    <image class="image-jiantou" src="/images/duo.png" />
    </navigator>
  3. 三号方案:进入小程序首页加载订阅消息弹窗,引导用户授权

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    onShowModal() {
    wx.showModal({
    title: '订阅消息确认',
    content: '避免错过重要信息,点击确认按钮进行订阅消息(务必勾选总保持以上选择)',
    cancelText: "取消",
    cancelColor: "#00f",
    confirmText: "确认",
    confirmColor: "#f00",
    complete: (res) => {
    if (res.cancel) {
    this.onShowToast();
    }
    if (res.confirm) {
    console.log("test1:")
    this.shouquan(); //调用订阅授权
    }
    }
    })
    }

    image-20231029225841283

    参考文章:

    关于一次性订阅消息requestSubscribeMessage的讨论 | 微信开放社区 (qq.com)

    小程序 订阅消息 一次授权多次接收_小程序 一次性订阅消息 多次推送-CSDN博客

定时发送动态的订阅消息

问题

想动态的获取天气信息,然后定时发送给用户。但是采用云函数开发,云函数不能调取本地的缓存数据,无法使用本地的setTimeout(延迟多少时间执行一次)和setInterval(每隔一定时间执行一次)方法。

解决方法

  1. 首先使用云函数的定时触发器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    // triggers 字段是触发器数组,目前仅支持一个触发器,即数组只能填写一个,不可添加多个
    "triggers": [
    {
    // name: 触发器的名字,规则见下方说明
    "name": "myTrigger",
    // type: 触发器类型,目前仅支持 timer (即 定时触发器)
    "type": "timer",
    // config: 触发器配置,在定时触发器下,config 格式为 cron 表达式,规则见下方说明
    "config": "0 0 2 1 * * *"
    }
    ]
    }

    image-20231029232521079image-20231029232552257

    更多用法详见官方文档:开发指引 / 基础能力 / 云函数 / 定时触发器 (qq.com)

  2. 但是云函数需要的天气信息参数是存在本地的(由于查询天气需要经纬度信息,云开发似乎没有类似wx.getLocation的方法,所以只好查询天气方法写在了本地)。解决办法是在本地定时执行查询天气信息的函数然后更新到数据库里。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    //获取用户经纬度
    function getUserLocation(){
    // var that = this;
    wx.getLocation({
    type: 'wgs84',
    success: function (res) {
    console.log(res)
    queryWeather(res);
    },
    })
    }
    //查询天气信息
    function queryWeather(res) {
    // var that = this;
    wx.request({
    url: 'https://jisuqgtq.market.alicloudapi.com/weather/query', //使用的是阿里提供的天气API接口
    header: {
    "Authorization": "APPCODE 自己的APPCODE"
    },
    data: {
    'location': res.latitude + "," + res.longitude
    },
    method: "GET",
    success: function (resRequest) {
    console.info("HTTP - GET")
    console.info(resRequest)
    if (resRequest.statusCode === 200 && resRequest.data.status == 0) {
    var result = resRequest.data.result;
    wx.setStorageSync("weather", result);
    console.info(result)
    }
    }
    })
    }
    //将天气信息添加入数据库
    function addWeather() {
    //以用户openid为关键词,将查询的数据插入对应的天气信息
    wx.cloud
    .callFunction({
    name: "getopenid",
    })
    .then((res) => {
    let openid = res.result.openid;
    getUserLocation();
    var weather = wx.getStorageSync('weather')
    //阿里的天气信息API包含的信息很多,这里由于订阅消息的模板有字数限制,thing参数只能在20字符以内
    let weatherMessage = "天气" + weather.weather + "当前气温" + weather.temp + "℃" + "详情点击查看"
    //将天气信息添加数据库
    wx.cloud
    .database()
    .collection("userInfo")
    .where({
    openid: openid
    })
    .update({
    data: {
    weatherMessage: weatherMessage,
    }
    })
    .then((res) => {
    console.log("添加天气信息成功", res);
    })
    .catch((res) => {
    console.log("添加天气信息失败", res);
    });
    })
    .catch((res) => {
    console.log("获取openid失败", res);
    });
    }

    本地定时任务用的是setInterval()每隔一段时间执行任务,将查询的天气结果存储在数据库里。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    startInter: function () {
    var that = this;
    that.data.inter = setInterval(
    function () {
    // TODO 你需要无限循环执行的任务
    addWeather();
    console.log('setInterval 每过一个小时执行一次天气信息添加入数据库任务')
    }, 3600000);
    },
  3. 最后是发送消息的云函数部分

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    //转换成消息模版所需要的格式,date 年/月/日 时:分:秒
    function timeStampToTime(date) {
    const formatNumber = n => {
    n = n.toString()
    return n[1] ? n : `0${n}`
    }
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    const hour = date.getHours()
    const minute = date.getMinutes()
    const second = date.getSeconds()
    return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
    }

    // 云函数入口文件
    const cloud = require('wx-server-sdk')

    cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) // 使用当前云环境

    const db = cloud.database()
    const tableName = 'userInfo'

    // 云函数入口函数
    exports.main = async (event, context) => {
    try {
    // 从云开发数据库中查询等待发送的消息用户列表
    const msgArr = await db
    .collection(tableName)
    .get()
    console.log("msgArr:", msgArr);
    for (const msgData of msgArr.data) {
    // 发送订阅消息
    await cloud.openapi.subscribeMessage.send({
    touser: msgData.openid, // 要发送用户的openid
    page: 'pages/index/index', // 用户通过消息通知点击进入小程序的页面
    lang: 'zh_CN',
    // 订阅消息模板ID
    templateId: '你的模板id',
    // 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
    // 正式版删除此行
    miniprogramState: 'developer',
    // 要发送的数据,要和模板一致
    data: {
    thing1: {
    value: 'xxx'
    },
    thing8: {
    value: 'xxx'
    },
    date3: { //注意date参数的格式问题 必须是2023年10月1日,或:2023年10月1日 15:01
    value: timeStampToTime(new Date())
    },
    thing2: {
    value: 'xxx'
    },
    thing7: {
    value: msgData.weatherMessage //发送的天气信息
    },
    }
    })
    }
    }
    catch (e) {
    console.log(e)
    return e
    }
    }

    订阅消息模板参数限制官方文档:消息相关 / 订阅消息 / 发送订阅消息 (qq.com)

    image-20231029234642543

    参考文章

    微信小程序云开发定时推送订阅消息_微信小程序定时提醒功能-CSDN博客

    微信小程序开发:云函数定时推送消息 - 知乎 (zhihu.com)