云開發(fā) 訂閱消息

2020-07-22 15:31 更新

訂閱消息是小程序能力中的重要組成,當(dāng)用戶自主訂閱之后,可以向用戶以服務(wù)通知的方式發(fā)送消息的能力,當(dāng)用戶點(diǎn)擊訂閱消息卡片可以跳轉(zhuǎn)到小程序的頁面,這樣就可以實(shí)現(xiàn)服務(wù)的閉環(huán)和更優(yōu)的體驗(yàn),提高活躍度和用戶粘性。

一獲取訂閱消息授權(quán)

1、小程序端獲取訂閱消息授權(quán)次數(shù)

要獲取訂閱消息授權(quán),首先要調(diào)用接口wx.requestSubscribeMessage,這個(gè)接口會(huì)調(diào)起小程序訂閱消息界面,返回用戶訂閱消息的操作結(jié)果。注意這個(gè)接口只能在小程序端使用tap點(diǎn)擊或支付完成后觸發(fā)。如果是使用頁面加載或其他非用戶點(diǎn)擊類的事件來調(diào)用這個(gè)接口,就會(huì)報(bào)requestSubscribeMessage:fail can only be invoked by user TAP gesture的錯(cuò)誤。

要調(diào)用wx.requestSubscribeMessage,需要我們首先要有訂閱消息的模板ID,一次性模板 id 和永久模板 id 不可同時(shí)使用,基礎(chǔ)庫2.8.4之后一次性可以調(diào)起3個(gè)模板ID(不能多于3個(gè))。

使用開發(fā)者工具新建一個(gè)頁面,如subscribe,然后在subscribe.wxml里輸入以下代碼,我們通過點(diǎn)擊tap來觸發(fā)事件處理函數(shù):

<button bindtap="subscribeMessage">訂閱訂閱消息</button>

然后再在subscribe.js里輸入以下代碼,我們?cè)谑录幚砗瘮?shù)subscribeMessage里調(diào)用wx.requestSubscribeMessage接口:

subscribeMessage() {
  wx.requestSubscribeMessage({
    tmplIds: [
      "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",//模板
      "RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
      "EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
    ],
    success(res) {
      console.log("訂閱消息API調(diào)用成功:",res)
    },
    fail(res) {
      console.log("訂閱消息API調(diào)用失敗:",res)
    }
  })
},

建議大家在手機(jī)上進(jìn)行真機(jī)調(diào)試這個(gè)接口,點(diǎn)擊訂閱消息button,就能彈出授權(quán)彈窗。

  • 當(dāng)用戶點(diǎn)擊“允許”就會(huì)累積一次授權(quán),如果點(diǎn)擊N次允許就能累積N次授權(quán),這個(gè)授權(quán)是長期的,沒有時(shí)間限制,你可以在一天內(nèi)發(fā)完N次授權(quán),也可以在未來分批次發(fā)完;也就是說這個(gè)雖然是一次性訂閱消息,但是只要用戶授權(quán)了N次,我們?cè)诙虝r(shí)間就可以發(fā)N次,而不是只能發(fā)一次;累積了多少次就可以發(fā)送多少次。發(fā)送一次就會(huì)消耗一次,累積的授權(quán)次數(shù)被消耗完之后,還繼續(xù)發(fā),就會(huì)報(bào)錯(cuò)errcode":"43101","errmsg":"user refuse to accept the msg hint...

  • 當(dāng)用戶勾選了訂閱面板中的“總是保持以上選擇,不再詢問”時(shí),且允許或拒絕之后,訂閱消息的授權(quán)彈窗則永遠(yuǎn)不會(huì)再彈出,訂閱消息也會(huì)被添加到用戶的小程序設(shè)置頁,我們可以通過 wx.getSetting 接口可獲取用戶對(duì)相關(guān)模板消息的訂閱狀態(tài)。wx.getSetting的withSubscriptions可以獲取用戶訂閱消息的訂閱狀態(tài),當(dāng)然只能返回用戶勾選過訂閱面板中的“總是保持以上選擇,不再詢問”的訂閱消息。

  • 如果用戶勾選了總是允許,那由于用戶點(diǎn)擊按鈕都不會(huì)彈出授權(quán)彈窗,用戶點(diǎn)擊了授權(quán)按鈕仍然會(huì)累積授權(quán),起到一個(gè)靜默收集授權(quán)次數(shù)的效果。也就是說,如果你通過wx.getSetting的withSubscriptions獲取到用戶對(duì)某條模板消息勾選了“總是保持以上選擇,不再詢問”,那你可以設(shè)置一個(gè)靜默收集用戶授權(quán)次數(shù)的button,用戶不會(huì)彈出授權(quán)彈窗,但是會(huì)累積授權(quán)次數(shù)。

注意該接口調(diào)用成功之后返回的對(duì)象,[TEMPLATE_ID]是動(dòng)態(tài)的鍵,即模板id,值包括'accept'、'reject'、'ban'。'accept'表示用戶同意訂閱該條id對(duì)應(yīng)的模板消息,'reject'表示用戶拒絕訂閱該條id對(duì)應(yīng)的模板消息,'ban'表示已被后臺(tái)封禁,如下所示(以下值僅為案例):

{errMsg: "requestSubscribeMessage:ok", RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU: "accept", qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44: "reject", EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0: "accept"}

訂閱消息的累積次數(shù)決定了我們是否可以給用戶發(fā)送訂閱消息,也決定了可以發(fā)送幾次,因此記錄用戶給某個(gè)模板ID授權(quán)了多少次這個(gè)也就顯得很重要了,比如我們可以結(jié)合接口返回的res對(duì)象和inc原子自增在數(shù)據(jù)庫里記錄訂閱次數(shù),當(dāng)發(fā)送一次也會(huì)消耗一次,再用inc自減:

  subscribeMessage() {
    const tmplIds= [
      "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
      "RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
      "EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
    ];
    wx.requestSubscribeMessage({
      tmplIds:tmplIds,
      success(res) {
        console.log("訂閱消息API調(diào)用成功:",res)    
        tmplIds.map(function(item,index){
          if(res[item] === "accept"){
            console.log("該模板ID用戶同意了",item)
            //可以使用原子自增指令inc往數(shù)據(jù)庫里某個(gè)記錄授權(quán)次數(shù)的字段+1
          }
        })
      },
      fail(res) {
        console.log("訂閱消息API調(diào)用失?。?,res)
      }
    })
  },

wx.requestSubscribeMessage的參數(shù)tmplIds是數(shù)組可以容納3個(gè)模板ID,當(dāng)用戶點(diǎn)擊授權(quán)彈窗,三個(gè)模板ID都是默認(rèn)勾選的,只要用戶點(diǎn)擊允許,就會(huì)同時(shí)給三個(gè)模板ID累積次數(shù);如果用戶取消勾選了其中一個(gè)模板ID,并點(diǎn)擊總是允許,那另外兩個(gè)勾選的模板ID將不會(huì)再有授權(quán)彈窗。

2、訂閱消息授權(quán)與次數(shù)累積實(shí)踐

訂閱消息最核心的在于用戶的授權(quán)與授權(quán)次數(shù),也就是你在寫訂閱消息代碼時(shí)或在發(fā)送訂閱消息之前,最好是先用數(shù)據(jù)庫記錄用戶是否已經(jīng)授權(quán)以及授權(quán)的次數(shù),關(guān)于訂閱消息的授權(quán)次數(shù)的累積需要再說明的是:

  • 只能在小程序端通過調(diào)用wx.requestSubscribeMessage來進(jìn)行授權(quán)以及累積授權(quán)次數(shù),wx.requestSubscribeMessage也不能寫在云函數(shù)端;

  • 只能記錄和累積當(dāng)前用戶的授權(quán)與授權(quán)次數(shù),這個(gè)要注意分清楚,比如我們希望學(xué)生點(diǎn)擊之后通知老師、老師點(diǎn)擊之后通知學(xué)生,這個(gè)前提始終是你要通知誰,誰必須有授權(quán)或授權(quán)次數(shù)才能通知;通知誰就會(huì)消耗誰的授權(quán)次數(shù)。比如要做到學(xué)生完成作業(yè)點(diǎn)擊按鈕就可以通知老師,此時(shí)學(xué)生不必有授權(quán)次數(shù),老師必須有,而訂閱消息的通知?jiǎng)t需要在云函數(shù)端進(jìn)行;

  • 授權(quán)次數(shù)只能增不能減,如果你想實(shí)現(xiàn)用戶訂閱了訂閱消息之后(使用的是wx.requestSubscribeMessage接口),又取消了該訂閱消息的通知(不需要使用wx.requestSubscribeMessage接口),你可以在數(shù)據(jù)庫里記錄,不再發(fā)消息給用戶,但是用戶的授權(quán)次數(shù)并沒有減少。所以取消訂閱我們可以使用布爾型字段,而授權(quán)次數(shù)我們可以使用整數(shù)方便原子操作。

訂閱消息的種類很多,比如有的訂閱消息用戶接收一次之后就不會(huì)再接收,這時(shí)我們側(cè)重于記錄訂閱消息是否被用戶同意就可以了;但是有的訂閱消息記錄用戶授權(quán)的次數(shù)有利于我們可以更好的為用戶服務(wù),比如日?qǐng)?bào)、周報(bào)、活動(dòng)消息等一些與用戶交互比較頻繁的信息。在前面我們已經(jīng)多次強(qiáng)調(diào)了云數(shù)據(jù)庫的原子操作,這里再以訂閱消息次數(shù)累積的增加(授權(quán)只能增加)為例,來看原子操作是如何處理的。

使用云開發(fā)控制臺(tái)新建一個(gè)messages集合,messages集合的記錄結(jié)構(gòu)如下所示,在設(shè)計(jì)上我們把同一個(gè)用戶多個(gè)不同類型的訂閱消息內(nèi)嵌到一個(gè)數(shù)組templs里面。

_id:""  //可以直接為用戶的openid,這樣我們可以使用db.collection('messages').doc(openid)來處理;不過我們的案例的_id不是openid
_openid:""  //云開發(fā)自動(dòng)生成的openid
templs:[{  //把用戶授權(quán)過的模板列表都記錄在這里
  templateId:"qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",//訂閱
  page:"",
  data:{},             //訂閱消息內(nèi)容對(duì)象,建議內(nèi)嵌到里面,免得查兩次
  status:1,            //用戶對(duì)該條模板消息是否接受'accept'、'reject'、'ban',
  subStyle:"daily",   //訂閱類型,比如是每天daily,還是每周weekly
  done:false,         //本次是否發(fā)送了
  subNum:22,          //該條訂閱消息用戶授權(quán)累積的次數(shù);  
},{
}]

下面是用戶在小程序端點(diǎn)擊訂閱消息之后的完整代碼,記錄不同的訂閱消息被用戶點(diǎn)擊之后,次數(shù)的累積。代碼沒有記錄用戶是否拒絕reject,如果業(yè)務(wù)上有需要也是可以記錄的,不過拒絕不存在累積次數(shù)的問題。

subscribeMessage() {
  const that = this
  //模板ID建議放置在數(shù)據(jù)庫中,便于以后修改
  const tmplIds= [
    "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
    "RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
    "EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
  ];
  wx.requestSubscribeMessage({
    tmplIds:tmplIds,
    success: res => {
      console.log("訂閱消息API調(diào)用成功:",res)
      that.addMessages().then( id =>{
        tmplIds.map(function(item,index){
          if(res[item] === "accept"){
            console.log("該模板ID用戶同意了",item)  
            that.subscribeNum(item,id)
          }
        })
      })     
    },
    fail(res) {
      console.log("訂閱消息API調(diào)用失?。?,res)
    }
  })
},


async addMessages(){
  //查詢用戶訂閱過的訂閱消息,只會(huì)有一條記錄,所以沒有l(wèi)imit等限制
  const messages = await db.collection('messages').where({
    _openid:'{openid}'
  }).get()  
  //如果用戶沒有訂閱過訂閱消息,就創(chuàng)建一條記錄
  if(messages.data.length == 0){  
    var newMsg = await db.collection('messages').add({
      data:{
        templs:[]
      }
    })
  } 
  var id = messages.data[0] ? messages.data[0]._id : newMsg._id  
  return id
},


async subscribeNum(item,id){
  //注意傳入的item是遍歷,id為addMessages的id
  const subs = await db.collection('messages').where({  
    _openid:'{openid}',
    "templs":_.elemMatch({
      templateId:item
    })
  }).get()


  console.log('用戶訂閱列表',subs)
  //如果用戶之前沒有訂閱過訂閱消息就創(chuàng)建一個(gè)訂閱消息的記錄
  if(subs.data.length == 0){  
    db.collection('messages').doc(id).update({
      data: {
        templs:_.push({
          each:[{templateId:item,//訂閱
            page:"",  
            data:{},            
            status:1,           
            subStyle:"daily", 
            done:false,         
            subNum:1}],
          position:2                  
        })
      }
    })    
  }else{
    db.collection('messages').where({
      _id:id,
      "templs.templateId":item
    })
    .update({
      data:{
        "templs.$.subNum":_.inc(1)
      }
    })
  }
}

這里的"templs.$.subNum":_.inc(1)就是當(dāng)用于同意哪條訂閱消息,就會(huì)給該訂閱消息的授權(quán)次數(shù)進(jìn)行原子加1。

二、發(fā)送訂閱消息方式說明

當(dāng)我們?cè)谛〕绦蚨死鄯e了某個(gè)模板ID的授權(quán)次數(shù)之后,就可以通過云函數(shù)來調(diào)用subscribeMessage.send接口發(fā)送訂閱消息了。而這個(gè)云函數(shù)我們可以在小程序端調(diào)用,也可以使用云函數(shù)來調(diào)用云函數(shù),還能使用定時(shí)觸發(fā)器來調(diào)用云函數(shù)。

  • 小程序端發(fā)送訂閱消息,有些業(yè)務(wù)需要在用戶在小程序內(nèi)完成了某個(gè)操作之后,就需要向用戶發(fā)送訂閱消息,比如打卡、簽到、支付、發(fā)表成功等,這些業(yè)務(wù)都依賴于用戶的操作,當(dāng)操作完成之后就可以在回調(diào)函數(shù)里調(diào)用發(fā)送訂閱消息的云函數(shù);

  • 還有一種就是,如果你是小程序的管理員,訂閱消息的管理界面也在小程序里,當(dāng)管理員在小程序端點(diǎn)擊定點(diǎn)或群發(fā)訂閱消息時(shí),也可以調(diào)用云函數(shù)來發(fā)送訂閱消息;

  • 使用定時(shí)觸發(fā)器發(fā)送訂閱消息,這時(shí)訂閱消息就可以周期性、定時(shí)發(fā)送,不再需要用戶/管理員點(diǎn)擊就可以結(jié)合業(yè)務(wù)場(chǎng)景發(fā)送。

云函數(shù)調(diào)用subscribeMessage.send接口的方式有兩種,一種是HTTPS調(diào)用,還有一種就是云調(diào)用,建議使用云調(diào)用。調(diào)用subscribeMessage.send接口時(shí)有很多細(xì)節(jié)需要注意,尤其是data格式,必須符合格式要求。

訂閱消息的data必須與模板消息一一對(duì)應(yīng)

比如我們申請(qǐng)到一個(gè)訂閱課程開課提醒的模板,它的格式如下:

姓名{{phrase1.DATA}}
課程標(biāo)題{{thing2.DATA}}
課程內(nèi)容{{thing3.DATA}}
時(shí)間{{date5.DATA}}
課程進(jìn)度{{character_string6.DATA}}

與之相應(yīng)的data的寫法如下phrase1、thing2、thing3、date5、character_string6,這些需要一一對(duì)應(yīng),參數(shù)不能多也不能少,參數(shù)后面的數(shù)字比如date5不能改成date6,否則會(huì)報(bào)"openapi.subscribeMessage.send:fail argument invalid! hint:的錯(cuò)誤,也就是模板里有什么參數(shù),你就只能按部就班寫什么參數(shù):

data: {
  "phrase1": {
    "value": '李東'
  },
  "thing2": {
    "value": '零基礎(chǔ)云開發(fā)技術(shù)訓(xùn)練營第7課'
  },
  "thing3": {
    "value": '列表渲染與條件渲染'
  },
  "date5": {
    "value": '2019年10月20日 20:00'
  },
  "character_string6": {
    "value": 3
  }
}

訂閱消息參數(shù)值的內(nèi)容格式必須要符合要求

在技術(shù)文檔里,有一個(gè)關(guān)于訂閱消息參數(shù)值的內(nèi)容格式要求,這個(gè)在寫訂閱消息內(nèi)容的時(shí)候需要嚴(yán)格的一一對(duì)應(yīng),否則會(huì)出現(xiàn)格式錯(cuò)誤。

參數(shù)類別 參數(shù)說明 參數(shù)值限制 說明
thing.DATA 事物 20個(gè)以內(nèi)字符 可漢字、數(shù)字、字母或符號(hào)組合
number.DATA 數(shù)字 32位以內(nèi)數(shù)字 只能數(shù)字,可帶小數(shù)
letter.DATA 字母 32位以內(nèi)字母 只能字母
symbol.DATA 符號(hào) 5位以內(nèi)符號(hào) 只能符號(hào)
character_string.DATA 字符串 32位以內(nèi)數(shù)字、字母或符號(hào) 可數(shù)字、字母或符號(hào)組合
time.DATA 時(shí)間 24小時(shí)制時(shí)間格式(支持+年月日) 例如:15:01,或:2019年10月1日 15:01
date.DATA 日期 年月日格式(支持+24小時(shí)制時(shí)間) 例如:2019年10月1日,或:2019年10月1日 15:01
amount.DATA 金額 1個(gè)幣種符號(hào)+10位以內(nèi)純數(shù)字,可帶小數(shù),結(jié)尾可帶“元” 可帶小數(shù)
phone_number.DATA 電話 17位以內(nèi),數(shù)字、符號(hào) 電話號(hào)碼,例:+86-0766-66888866
car_number.DATA 車牌 8位以內(nèi),第一位與最后一位可為漢字,其余為字母或數(shù)字 車牌號(hào)碼:粵A8Z888掛
name.DATA 姓名 10個(gè)以內(nèi)純漢字或20個(gè)以內(nèi)純字母或符號(hào) 中文名10個(gè)漢字內(nèi);純英文名20個(gè)字母內(nèi);中文和字母混合按中文名算,10個(gè)字內(nèi)
phrase.DATA 漢字 5個(gè)以內(nèi)漢字 5個(gè)以內(nèi)純漢字,例如:配送中

下面列舉一些在使用過程中容易犯的錯(cuò)誤:

  • 可能已有的模板消息的格式和你想要的不一致,比如你希望發(fā)送的消息是用戶的昵稱,而不是姓名{{phrase1.DATA}},因?yàn)樾彰荒苁侵形?,且必?個(gè)字以內(nèi),那你就沒法擅自改動(dòng),只能去申請(qǐng)或復(fù)用其他的模板ID;

  • 每個(gè)格式對(duì)字符串的長度和類型都有嚴(yán)格的要求,比如thing,要求必須是20個(gè)以內(nèi)的字符,不能超過20個(gè)字符;有些只能是數(shù)字或字母,就不能是其他格式

三、使用云調(diào)用發(fā)送訂閱消息

在前面我們說過,在小程序端哪個(gè)用戶點(diǎn)擊授權(quán)就只會(huì)給哪個(gè)用戶增加授權(quán)次數(shù),而借助于云函數(shù)發(fā)送訂閱消息則用戶可以給任何人發(fā)送訂閱消息,發(fā)給哪個(gè)人就需要哪個(gè)人有授權(quán)次數(shù),就會(huì)減少哪個(gè)人的授權(quán)次數(shù),這一點(diǎn)要注意區(qū)分。

1、發(fā)送單條訂閱消息

新建一個(gè)云函數(shù)比如subscribeMessage,然后再在config.json的添加subscribeMessage.send權(quán)限,使用云函數(shù)增量上傳更新這個(gè)配置文件。

{
  "permissions": {
    "openapi": [
      "subscribeMessage.send"
    ]
  }
}

然后再在index.js里輸入以下代碼,注意這里的openid,是用戶自己的,這種適用于用戶在小程序端完成某個(gè)業(yè)務(wù)操作之后,就給用戶自己發(fā)訂閱消息;當(dāng)然這里的openid可以是其他累積了授權(quán)次數(shù)的用戶的,也就是當(dāng)我們?cè)谛〕绦蚨苏{(diào)用該云函數(shù)就能給其他人發(fā)訂閱消息了,這主要適用于管理員:

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV,
})
exports.main = async (event, context) => {
  const { OPENID } = cloud.getWXContext()
  try {
    const result = await cloud.openapi.subscribeMessage.send({
      touser: "oUL-m5FuRmuVmxvbYOGuXbuEDsn8",
      page: 'index',
      templateId: "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
      data: {
        "phrase1": {
          "value": '小明'
        },
        "thing2": {
          "value": '零基礎(chǔ)云開發(fā)技術(shù)訓(xùn)練營第7課'
        },
        "thing3": {
          "value": '列表渲染與條件渲染'
        },
        "date5": {
          "value": '2019年10月20日 20:00'
        },
        "character_string6": {
          "value": 3
        }
    }
    })
    return result
  } catch (err) {
    console.log(err)
    return err
  }
} 

2、批量發(fā)送訂閱消息

由于subscribeMessage.send的參數(shù)templateId和touser都是字符串,因此執(zhí)行一次subscribeMessage.send只能給一個(gè)用戶發(fā)送一條訂閱消息,那要給更多用戶比如1000人以內(nèi)(云函數(shù)一次可以獲取到1000條數(shù)據(jù))發(fā)訂閱消息,則需要結(jié)合數(shù)據(jù)庫的查詢數(shù)據(jù)庫內(nèi)所有有授權(quán)次數(shù)的用戶然后循環(huán)執(zhí)行來發(fā)消息,并在發(fā)完之后使用inc自減來減去授權(quán)次數(shù)。

由于我們把用戶授權(quán)的所有訂閱消息內(nèi)嵌到templs這個(gè)數(shù)組里,而要發(fā)送的訂閱消息的內(nèi)容則來自templs數(shù)組里符合條件的對(duì)象,這里涉及到相對(duì)比較復(fù)雜的數(shù)組的處理,因此數(shù)據(jù)分析處理神器聚合就派上用場(chǎng)了(當(dāng)然我們也可以使用普通查詢,普通查詢得到的是記錄列表,再使用一些數(shù)組方法如filter、map等取出列表里的templs嵌套的對(duì)象列表)。

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
const _ = db.command
const $ = db.command.aggregate
exports.main = async (event, context) => {
  const templateId ="qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44"
  try {
    const messages = (await db.collection('messages').aggregate()
      .match({   //使用match匹配查詢
        "templs.templateId":templateId,  //注意這里templs.templateId的寫法
        "done":false,
        "status":1 
      })
      .project({
        _id:0,
        templs: $.filter({  //從嵌套的templs數(shù)組里取出模板ID滿足條件的對(duì)象
          input: '$templs',
          as: 'item',
          cond: $.eq(['$$item.templateId',templateId])
        })
      })
      .project({   
        message:$.arrayElemAt(['$templs', 0]), //符號(hào)條件的是只有1個(gè)對(duì)象的數(shù)組,取出這個(gè)對(duì)象
      })
      .end()).list  //使用聚合查詢到的是一個(gè)list對(duì)象
    const tasks = []
    for (let item in messages) {
      const promise = cloud.openapi.subscribeMessage.send({
        touser: item.message._openid,
        page: 'index',
        templateId: item.message.templateId,
        data: item.message.data
      })
      tasks.push(promise)
    }
    return (await Promise.all(tasks)).reduce((acc, cur) => {
      return {
        data: acc.data.concat(cur.data),
        errMsg: acc.errMsg,
      }
    })
  } catch (err) {
    console.log(err);
    return err;
  }
}

特別注意的是,不要把查詢數(shù)據(jù)庫的語句放到循環(huán)里面,也就是我們可以一次性取出1000條需要發(fā)訂閱消息的用戶,然后再結(jié)合map和Promise.all方法給這1000個(gè)用戶發(fā)送訂閱消息,然后再一次性給所有這1000條數(shù)據(jù)進(jìn)行原子自增,不能一條一條處理,否則會(huì)造成數(shù)據(jù)庫性能的極大浪費(fèi)以及超出最大連接數(shù),而且也會(huì)導(dǎo)致云函數(shù)在最高60s的生命周期里也發(fā)送不了幾百條訂閱消息。

四、使用定時(shí)觸發(fā)器發(fā)訂閱消息

但是當(dāng)要發(fā)送訂閱消息的用戶有幾十萬幾百萬,那應(yīng)該怎么處理呢?如果全部讓云函數(shù)來執(zhí)行,即使將云函數(shù)的執(zhí)行超時(shí)時(shí)間修改為60s,也應(yīng)該會(huì)超時(shí),這時(shí)候我們可以結(jié)合定時(shí)器來發(fā)送訂閱消息。

使用定時(shí)觸發(fā)器來發(fā)送訂閱消息,也就是在小程序的云開發(fā)服務(wù)端,用定時(shí)觸發(fā)器調(diào)用訂閱消息的云調(diào)用接口openapi.subscribeMessage.send。當(dāng)我們每天要給數(shù)十萬人定時(shí)發(fā)送訂閱消息時(shí),這時(shí)候定時(shí)觸發(fā)器就不僅僅需要比如每天早上9點(diǎn)觸發(fā),而且還需要在9點(diǎn)之后能夠每隔一段時(shí)間比如40s,就來執(zhí)行一次云函數(shù)以便給數(shù)十萬用戶發(fā)送訂閱消息。

這時(shí)候Cron表達(dá)式可以這樣寫,意思是每天早上9點(diǎn)到11點(diǎn)每隔40s執(zhí)行一次云函數(shù):

0/40 * 9-11 * * * *

當(dāng)然這里的周期設(shè)置可以結(jié)合云函數(shù)實(shí)際執(zhí)行的時(shí)間來定,要充分考慮到云函數(shù)的超時(shí)時(shí)間。

云調(diào)用還支持組合模板并添加至帳號(hào)下的個(gè)人模板庫的接口subscribeMessage.addTemplate、刪除帳號(hào)下的個(gè)人模板subscribeMessage.deleteTemplate、獲取小程序賬號(hào)的類目subscribeMessage.getCategory、獲取當(dāng)前帳號(hào)下的個(gè)人模板列表subscribeMessage.getTemplateList等等接口,這里就不一一介紹啦。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)