微信小程序 - 云开发轮询实现定时推送订阅消息
前言
受眾:已有小程序和云開發經驗(沒有的話照著流程和官方文檔也應該可以實現)
關于小程序的消息推送,我了解到的有以下幾種實現方式
1、模板消息,已于2020 年 1 月 10 日下線
2、通過服務端的統一服務消息下發推送,因為模板消息現已下線,現只支持公眾號。統一服務消息官方文檔
2、通過關注公眾號通過公眾號實現長期的消息推送
3、訂閱消息,包含一次性訂閱消息和長期訂閱消息
訂閱消息官方文檔
關于技術實現的選擇
關于小程序的消息推送的幾種實現方式,先簡單說一下各自的優缺點:
1、統一服務消息
優點:可以長期多次發送
缺點:因為模板消息現已下線,現只支持公眾號
2、通過公眾號實現
優點:可以長期多次發送
缺點:需要引導關注公眾號,沒有公眾號還得注冊一個,以下還有一些注意事項
1、公眾號和小程序需要在同一個微信開放平臺下,保證拿到相同的UnionID
2、如果需要在消息模板上加上小程序的入口,需要微信公眾號和小程序做關聯
3、小程序和公眾號都必須是認證過的
4、小程序需要提前知道公眾號的appid和appsecret
5、發送消息之前需要拿到用戶對應于公眾號的openid
3、訂閱消息實現
訂閱消息,包含一次性訂閱消息和長期訂閱消息,可惜長期訂閱消息只對指定類目開放
一次性訂閱消息
優點:服務端,云開發都可以實現推送
缺點:每次需要授權,每次授權同意只推送一次
哎,沒什么可挑的,最終選擇的是訂閱消息的一次性訂閱消息,下面是微信官方介紹
小程序訂閱消息
功能介紹
消息能力是小程序能力中的重要組成,我們為開發者提供了訂閱消息能力,以便實現服務的閉環和更優的體驗。
- 訂閱消息推送位置:服務通知
- 訂閱消息下發條件:用戶自主訂閱
- 訂閱消息卡片跳轉能力:點擊查看詳情可跳轉至該小程序的頁面
消息類型
1. 一次性訂閱消息
一次性訂閱消息用于解決用戶使用小程序后,后續服務環節的通知問題。用戶自主訂閱后,開發者可不限時間地下發一條對應的服務消息;每條消息可單獨訂閱或退訂。
2. 長期訂閱消息
一次性訂閱消息可滿足小程序的大部分服務場景需求,但線下公共服務領域存在一次性訂閱無法滿足的場景,如航班延誤,需根據航班實時動態來多次發送消息提醒。為便于服務,我們提供了長期性訂閱消息,用戶訂閱一次后,開發者可長期下發多條消息。
目前長期性訂閱消息僅向政務民生、醫療、交通、金融、教育等線下公共服務開放,后期將逐步支持到其他線下公共服務業務。
3. 設備訂閱消息
設備訂閱消息是一種特殊類型的訂閱消息,它屬于長期訂閱消息類型,且需要完成「設備接入」才能使用。
設備訂閱消息用于在設備觸發某些需要人工介入的事件時(例如設備發生故障、設備耗材不足等),向用戶發送消息通知。詳見設備訂閱消息文檔。
使用說明
步驟一:獲取模板 ID
在微信公眾平臺手動配置獲取模板 ID:
登錄 https://mp.weixin.qq.com 獲取模板,如果沒有合適的模板,可以申請添加新模板,審核通過后可使用。
步驟二:獲取下發權限
一次性訂閱消息、長期訂閱消息,詳見接口 wx.requestSubscribeMessage
設備訂閱消息,詳見接口 wx.requestSubscribeDeviceMessage
步驟三:調用接口下發訂閱消息
一次性訂閱消息、長期訂閱消息,詳見服務端接口 subscribeMessage.send
設備訂閱消息,詳見服務端接口 hardwareDevice.send
注意事項
- 用戶勾選 “總是保持以上選擇,不再詢問” 之后,下次訂閱調用 wx.requestSubscribeMessage 不會彈窗,保持之前的選擇,修改選擇需要打開小程序設置進行修改。
實現
設計思路
我這里的需求是紀念日的推送,到用戶設置的紀念日當天或者前幾天,需要給用戶推送一條消息,提醒用戶紀念日已到。因為是一次性授權,首先要考慮授權的時機,因此考慮是在用戶新增或者編輯紀念日信息那里,設置一個開關,開啟時判斷通知權限,無權限提醒到設置頁開啟通知,有權限請求授權,授權成功開關開啟,失敗或者未開啟通知權限開關關閉,最后提交信息到數據庫,推送的接口會每天輪詢一次,根據這個狀態和是否到紀念日提醒時間判斷是否進行推送,推送成功重置狀態。推送之后用戶再次開啟推送開關可實現下一次推送,形成一個閉環。
實現流程
- 首先要去小程序后臺選擇一個模板
- 然后在新增編輯頁面實現設計的邏輯(包含通知權限的判斷,請求一次性訂閱,失敗關閉開關狀態,保存數據到數據庫)
- 云開發輪詢推送消息(先查出需要推送的數據,然后對應模板的數據格式發送推送)
具體實現
1、選擇模板
登錄小程序開發后臺 - 訂閱消息 - 公共模板庫搜索選擇一個合適的模板,然后在我的模板那里可以查看模板詳情,模板id和詳情里的字段在云開發那里需要使用
2、編輯頁代碼實現
首先先把開關相關的頁面和邏輯實現,這里就不細說。
然后是判斷通知權限和請求一次性訂閱
2.1、判斷通知權限
判斷通知權限使用的是wx.getSetting
wx.getSetting({withSubscriptions: true,success (res) {console.log(res.authSetting)// res.authSetting = {// "scope.userInfo": true,// "scope.userLocation": true// }console.log(res.subscriptionsSetting)// res.subscriptionsSetting = {// mainSwitch: true, // 訂閱消息總開關// itemSettings: { // 每一項開關// SYS_MSG_TYPE_INTERACTIVE: 'accept', // 小游戲系統訂閱消息// SYS_MSG_TYPE_RANK: 'accept'// zun-LzcQyW-edafCVvzPkK4de2Rllr1fFpw2A_x0oXE: 'reject', // 普通一次性訂閱消息// ke_OZC_66gZxALLcsuI7ilCJSP2OJ2vWo2ooUPpkWrw: 'ban',// }// }} })2.2、請求一次性訂閱
請求一次性訂閱使用的是wx.requestSubscribeMessage
wx.requestSubscribeMessage({tmplIds: [''], // 模板idsuccess (res) { } })2.3、把數據存儲到云數據庫
每個項目數據結構不一樣,這里也不展開說
核心代碼
// 是否提醒onSwitchChange: function (event) {this.setData({["dict.isPush"]: event.detail.value})if (!event.detail.value) {this.setData({["dict.pushTime"]: ''})}if (event.detail.value) {this.checkAndRequestSubscribeMessage()}},// 檢查訂閱消息權限,未開啟提示前往開啟,已開啟請求訂閱消息checkAndRequestSubscribeMessage() {let that = thiswx.getSetting({withSubscriptions: true,success(res) {console.log(res.subscriptionsSetting)// 訂閱消息總開關是否開啟if (!res.subscriptionsSetting.mainSwitch) {that.subscriptionFailed()wx.showModal({title: '提示',content: '當前暫未開啟接消息提醒,是否前往設置頁開啟?',success(res) {if (res.confirm) {wx.openSetting()}}})} else {let templateId = 'c64Gp5-89xyD55rnDr0oBWQNphWlNm_l4MX-Sduuj2c' // 模板IDwx.requestSubscribeMessage({tmplIds: [templateId],success(res) {console.log(res)// 申請訂閱成功,將訂閱信息調用云函數存入云開發數據if (res.errMsg === 'requestSubscribeMessage:ok') {// res[templateId]: 'accept'、'reject'、'ban'、'filter'if (res[templateId] == 'accept') {} else {that.subscriptionFailed()}}},fail(err) {console.log(err)that.subscriptionFailed()wx.showToast({title: '訂閱失敗',icon: 'none'})}})}}})},// 訂閱失敗subscriptionFailed() {this.setData({["dict.isPush"]: false})},3、云開發輪詢推送
這里才是重點!!!首先在項目中選擇云函數目錄右鍵新建一個云函數,然后就需要在這個云函數中實現輪詢推送的代碼了,我這里建的云函數是push
3.1、推送
推送使用的是服務端的subscribeMessage.send方法。
這里是的是云調用進行推送,這樣可以使用云開發
subscribeMessage.send使用需要在云函數代碼的config.json文件中配置 subscribeMessage.send API 的權限,詳情
配置如下
"permissions": {"openapi": ["subscribeMessage.send"]},請求參數
| touser | string | 是 | 接收者(用戶)的 openid | |
| templateId | string | 是 | 所需下發的訂閱模板id | |
| page | string | 否 | 點擊模板卡片后的跳轉頁面,僅限本小程序內的頁面。支持帶參數,(示例index?foo=bar)。該字段不填則模板無跳轉。 | |
| data | Object | 是 | 模板內容,格式形如 { “key1”: { “value”: any }, “key2”: { “value”: any } } | |
| miniprogramState | string | 否 | 跳轉小程序類型:developer為開發版;trial為體驗版;formal為正式版;默認為正式版 | |
| lang | string | 否 | 進入小程序查看”的語言類型,支持zh_CN(簡體中文)、en_US(英文)、zh_HK(繁體中文)、zh_TW(繁體中文),默認為zh_CN |
官方請求示例
const cloud = require('wx-server-sdk') cloud.init({env: cloud.DYNAMIC_CURRENT_ENV, }) exports.main = async (event, context) => {try {const result = await cloud.openapi.subscribeMessage.send({"touser": 'OPENID',"page": 'index',"lang": 'zh_CN',"data": {"number01": {"value": '339208499'},"date01": {"value": '2015年01月05日'},"site01": {"value": 'TIT創意園'},"site02": {"value": '廣州市新港中路397號'}},"templateId": 'TEMPLATE_ID',"miniprogramState": 'developer'})return result} catch (err) {return err} }miniprogramState在正式環境要換成formal或注釋掉這行
3.2、輪詢
輪詢使用的云開發的定時觸發器
定時觸發器官方介紹
該功能需開發者工具 1.02.1811270 及以上版本方可使用 從開發者工具 1.02.1910182 開始,新上傳的定時觸發器內支持使用云調用
-
如果云函數需要定時 / 定期執行,也就是定時觸發,我們可以使用云函數定時觸發器。配置了定時觸發器的云函數,會在相應時間點被自動觸發,函數的返回結果不會返回給調用方。
-
在需要添加觸發器的云函數目錄下新建文件 config.json,格式如下:
官方示例
下面展示了一些 Cron 表達式和相關含義的示例:
- */5 * * * * * * 表示每5秒觸發一次
- 0 0 2 1 * * * 表示在每月的1日的凌晨2點觸發
- 0 15 10 * * MON-FRI * 表示在周一到周五每天上午10:15觸發
- 0 0 10,14,16 * * * * 表示在每天上午10點,下午2點,4點觸發
- 0 */30 9-17 * * * * 表示在每天上午9點到下午5點內每半小時觸發
- 0 0 12 * * WED * 表示在每個星期三中午12點觸發
triggers中的config字段可以控制觸發的頻率,具體開發測試時我使用的是50秒調用一次
"config": "*/50 * * * * * *"這里有個坑,如果代碼實現上傳并部署云函數之后,左等右等在日志中看不到日志,因為少了一個步驟,在上傳并部署云函數之后,需要右鍵云函數上傳觸發器,這樣才生效,想關閉可以刪除觸發器
輪詢推送完整代碼
config.json
{"permissions": {"openapi": ["subscribeMessage.send"]},"triggers": [{"name": "myTimer","type": "timer","config": "0 0 8 * * * *"}] }index.js
// 云函數入口文件 const cloud = require('wx-server-sdk')// 初始化 cloud cloud.init({// API 調用都保持和云函數當前所在環境一致env: cloud.DYNAMIC_CURRENT_ENV })const db = cloud.database() const _ = db.command const $ = db.command.aggregate const kTableName = '換成自己的表名'// 云函數入口函數 exports.main = async (event, context) => {try {// 從云開發數據庫中查詢等待發送的消息列表const msgArr = await db.collection(kTableName)// 查詢條件,已開啟推送,并且提醒時間為今天.where({A_IsPush: true,A_PushTime: timeStampToTime(new Date().getTime(), '{y}/{m}/ozvdkddzhkzd')}).get()// 循環消息列表const sendPromises = msgArr.data.map(async msgData => {try {// 發送訂閱消息await cloud.openapi.subscribeMessage.send({touser: msgData._openid, // 要發送用戶的openidpage: 'pages/home/home', // 用戶通過消息通知點擊進入小程序的頁面lang: 'zh_CN',templateId: 'c64Gp5-89xyD55rnDr0oBWQNphWlNm_l4MX-Sduuj2c', // 訂閱消息模板ID// 跳轉小程序類型:developer為開發版;trial為體驗版;formal為正式版;默認為正式版// miniprogramState: 'developer',// 要發送的數據,要和模板一致data: {// 紀念日名稱thing5: {value: msgData.A_Title},// 紀念日時間time2: {value: msgData.A_Time},// 備注thing4: {value: msgData.A_Remarks ? msgData.A_Remarks : '無'},}})// 發送成功后將數據狀態重置return db.collection(kTableName).doc(msgData._id).update({data: {A_IsPush: false,A_PushTime: '',A_NextTime: '',},})} catch (e) {return e}})return Promise.all(sendPromises)} catch (err) {console.log(err)return err} }function timeStampToTime(time, cFormat) {if (arguments.length === 0) {return null}const format = cFormat || '{y}-{m}-ozvdkddzhkzd {h}:{i}:{s}'let dateif (typeof time === 'object') {} else {if (('' + time).length === 10) time = parseInt(time) * 1000date = new Date(time)}const formatObj = {y: date.getFullYear(),m: date.getMonth() + 1,d: date.getDate(),h: date.getHours(),i: date.getMinutes(),s: date.getSeconds(),w: date.getDay()}const time_str = format.replace(/{(y|m|d|h|i|s|w)+}/g, (result, key) => {let value = formatObj[key]if (key === 'w') {return ['日', '一', '二', '三', '四', '五', '六'][value]}if (result.length > 0 && value < 10) {value = '0' + value}return value || 0})return time_str }// 定時觸發器 // https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/functions/triggers.html// 50秒一次 // "config": "*/50 * * * * * *"// 每天上午8點一次 // "config": "0 0 8 * * * *"package.json
{"name": "push","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","dependencies": {"wx-server-sdk": "~2.6.1"} }3.3、上傳并部署云函數
所有代碼實現之后,可以先把項目環境設置為開發環境,然后右鍵上傳并部署云函數,然后上傳觸發器
然后打開云函數控制臺,選擇云函數-日志,進行查看狀態,如果成功并且有需要推送的數據,手機會收到推送消息,失敗的話根據日志進行修改。
至此結束
最后推薦一下我的小程序,我的紀念日小助手
總結
以上是生活随笔為你收集整理的微信小程序 - 云开发轮询实现定时推送订阅消息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 菜鸟站长之家带你了解什么是百度SEO快排
- 下一篇: git提交如何忽略某些文件