Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Cloud Development Subscription messages


May 22, 2021 Mini Program Cloud Development Advanced



Subscription message is an important component of the ability of small programs, when the user subscribes independently, the ability to send messages to the user in the form of service notifications, when the user clicks on the subscription message card can jump to the page of the small program, so that the service can be closed and better experience, improve activity and user stickiness.

One gets the subscription message authorization

1, the small terminal to obtain the number of subscription message authorization

To obtain subscription message authorization, first call the interface wx.requestSubscribeMessage, which calls up the program subscription message interface and returns the results of the user's subscription message. N ote that this interface can only be triggered after the tap click or payment is completed on the small terminal. If you call this interface using page load or other non-user click requestSubscribeMessage:fail can only be invoked by user TAP gesture

To call wx.requestSubscribeMessage, we need to first have a template ID to subscribe to the message, a one-time template id and a permanent template id can not be used at the same time, the base library 2.8.4 after a one-time can be adjusted 3 template IDs (no more than 3).

Using the developer tool to create a new page, such as subscribe, and then entering the following code in subscribe.wxml, we trigger the event handler by clicking tap:

  1. <button bindtap="subscribeMessage">订阅订阅消息</button>

Then enter the following code .js the subscribe page, and we call the wx.requestSubscribeMessage interface in the event handler subscribeMessage:

  1. subscribeMessage() {
  2. wx.requestSubscribeMessage({
  3. tmplIds: [
  4. "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",//模板
  5. "RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
  6. "EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
  7. ],
  8. success(res) {
  9. console.log("订阅消息API调用成功:",res)
  10. },
  11. fail(res) {
  12. console.log("订阅消息API调用失败:",res)
  13. }
  14. })
  15. },

It is recommended that you do real machine debugging on the phone this interface, click on the subscription message button, you can pop up the authorization pop-up window.

  • When the user click "allow" will accumulate a license, if click N times allow can accumulate N authorization, this authorization is long-term, no time limit, you can send N authorizations in one day, can also be issued in batches in the future; Send once will consume once, the cumulative number of authorizations is consumed, and then continue to send, will report errcode":"43101","errmsg":"user refuse to accept the msg hint...

  • When the user ticks "Always keep the above selection, don't ask again" in the subscription panel, and after allowing or rejecting, the authorization window for the subscription message never pops up again, and the subscription message is added to the user's small program settings page, and we can get the user's subscription status to the relevant template message through the wx.getSetting interface. wx.getSetting's withSubscriptions can get the subscription status of the user's subscription message, and of course can only return the subscription message "Always keep the above selection, don't ask" that the user has checked in the subscription panel.

  • If the user ticks always allow, then because the user clicks the button will not pop up the authorization window, the user clicks the authorization button will still accumulate authorization, playing a silent collection of authorization number of effect. That is, if you get the user ticking "Always keep the above selection, don't ask" on a template message through the wx.getSetting's withSubscriptions, you can set up a button that silently collects the number of user authorizations, and the user does not pop up the authorization window, but accumulates the number of authorizations.

Note that the object returned after the interface call is successful, the TEMPLATE_ID key that is dynamic, that is, the template id, the values include 'accept', 'reject', 'ban'. 'accept' means that the user agrees to subscribe to the template message for the id, 'reject' means that the user refuses to subscribe to the template message for the id, and 'ban' means that it has been blocked in the background, as shown below (the following value is only a case):

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

The cumulative number of subscription messages determines whether we can send a subscription message to the user, but also determines how many times we can send a subscription message, so it is important to record how many times the user has authorized a template ID, for example, we can combine the res object returned by the interface and inc atoms to record the number of subscriptions in the database, when sent once will also consume once, and then use inc subtract:

  1. subscribeMessage() {
  2. const tmplIds= [
  3. "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
  4. "RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
  5. "EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
  6. ];
  7. wx.requestSubscribeMessage({
  8. tmplIds:tmplIds,
  9. success(res) {
  10. console.log("订阅消息API调用成功:",res)
  11. tmplIds.map(function(item,index){
  12. if(res[item] === "accept"){
  13. console.log("该模板ID用户同意了",item)
  14. //可以使用原子自增指令inc往数据库里某个记录授权次数的字段+1
  15. }
  16. })
  17. },
  18. fail(res) {
  19. console.log("订阅消息API调用失败:",res)
  20. }
  21. })
  22. },

wx.requestSubscribeMessage parameter tmplIds is an array can hold 3 template IDs, when the user clicks on the authorization screen, the three template IDs are checked by default, as long as the user clicks allowed, will give three template IDs cumulative times at the same time;

2, subscription message authorization and the cumulative practice of the number of times

At the heart of the subscription message is the number of authorizations and authorizations of the user, that is, when you write the subscription message code or before you send the subscription message, it is best to use the database to record whether the user has authorized and the number of authorizations, the cumulative number of subscription messages need to be explained:

  • Only on the small terminal by calling wx.requestSubscribeMessage for authorization and cumulative authorization times, wx.requestSubscribeMessage can not be written on the cloud function;

  • Can only record and accumulate the current user's authorization and authorization times, this should pay attention to clear, such as we want students to click after notifying teachers, teachers click after notifying students, this premise is always you want to notify who, who must have authorization or authorization times to notify; For example, to do students to complete the homework click button can inform the teacher, at this time students do not have to have authorization times, teachers must have, and subscription message notifications need to be carried out in the cloud function;

  • The number of authorizations can only increase or decrease, if you want to implement the user after subscribing to the subscription message (using the wx.requestSubscribeMessage interface), and cancel the notification of the subscription message (no need to use the wx.requestSubscribeMessage interface), you can record in the database, no longer send messages to the user, but the number of user authorizations has not decreased. So unsubscribe we can use Boolean fields, and authorization times we can use integers for atomic operation.

There are many kinds of subscription messages, such as some subscription message users will not receive once after receiving, then we focus on recording whether the subscription message is agreed by the user on it, but some subscription message record the number of times the user is authorized to better serve the user, such as daily reports, weekly reports, active messages and other information that interacts with the user more frequently. We've emphasized atomic operations in the cloud database many times before, and here's an example of how atomic operations are handled by the cumulative increase in subscription messages (authorization can only increase).

Using the cloud development console to create a new messages collection, the record structure of the messages collection is shown below, and we designed to ential multiple different types of subscription messages from the same user into an array of templs.

  1. _id:"" //可以直接为用户的openid,这样我们可以使用db.collection('messages').doc(openid)来处理;不过我们的案例的_id不是openid
  2. _openid:"" //云开发自动生成的openid
  3. templs:[{ //把用户授权过的模板列表都记录在这里
  4. templateId:"qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",//订阅
  5. page:"",
  6. data:{}, //订阅消息内容对象,建议内嵌到里面,免得查两次
  7. status:1, //用户对该条模板消息是否接受'accept'、'reject'、'ban',
  8. subStyle:"daily", //订阅类型,比如是每天daily,还是每周weekly
  9. done:false, //本次是否发送了
  10. subNum:22, //该条订阅消息用户授权累积的次数;
  11. },{
  12. }]

The following is the complete code after the user clicks on the subscription message on the small terminal, recording the cumulative number of times different subscription messages have been clicked by the user. The code does not record whether the user rejects reject, which can be recorded if there is a business need, but there is no cumulative number of rejection issues.

  1. subscribeMessage() {
  2. const that = this
  3. //模板ID建议放置在数据库中,便于以后修改
  4. const tmplIds= [
  5. "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
  6. "RCg8DiM_y1erbOXR9DzW_jKs-qSSJ9KF0h8lbKKmoFU",
  7. "EGKyfjAO2-mrlJQ1u6H9mZS8QquxutBux1QbnfDDtj0"
  8. ];
  9. wx.requestSubscribeMessage({
  10. tmplIds:tmplIds,
  11. success: res => {
  12. console.log("订阅消息API调用成功:",res)
  13. that.addMessages().then( id =>{
  14. tmplIds.map(function(item,index){
  15. if(res[item] === "accept"){
  16. console.log("该模板ID用户同意了",item)
  17. that.subscribeNum(item,id)
  18. }
  19. })
  20. })
  21. },
  22. fail(res) {
  23. console.log("订阅消息API调用失败:",res)
  24. }
  25. })
  26. },
  27. async addMessages(){
  28. //查询用户订阅过的订阅消息,只会有一条记录,所以没有limit等限制
  29. const messages = await db.collection('messages').where({
  30. _openid:'{openid}'
  31. }).get()
  32. //如果用户没有订阅过订阅消息,就创建一条记录
  33. if(messages.data.length == 0){
  34. var newMsg = await db.collection('messages').add({
  35. data:{
  36. templs:[]
  37. }
  38. })
  39. }
  40. var id = messages.data[0] ? messages.data[0]._id : newMsg._id
  41. return id
  42. },
  43. async subscribeNum(item,id){
  44. //注意传入的item是遍历,id为addMessages的id
  45. const subs = await db.collection('messages').where({
  46. _openid:'{openid}',
  47. "templs":_.elemMatch({
  48. templateId:item
  49. })
  50. }).get()
  51. console.log('用户订阅列表',subs)
  52. //如果用户之前没有订阅过订阅消息就创建一个订阅消息的记录
  53. if(subs.data.length == 0){
  54. db.collection('messages').doc(id).update({
  55. data: {
  56. templs:_.push({
  57. each:[{templateId:item,//订阅
  58. page:"",
  59. data:{},
  60. status:1,
  61. subStyle:"daily",
  62. done:false,
  63. subNum:1}],
  64. position:2
  65. })
  66. }
  67. })
  68. }else{
  69. db.collection('messages').where({
  70. _id:id,
  71. "templs.templateId":item
  72. })
  73. .update({
  74. data:{
  75. "templs.$.subNum":_.inc(1)
  76. }
  77. })
  78. }
  79. }

"templs.$.subNum":_.inc(1) number of times the subscription message is authorized to be atomic plus 1 when it is used to agree to which subscription message.

Second, send a subscription message description

Once we have accumulated the number of authorizations for a template ID on the small terminal, we can call the subscribeMessage.send interface via the cloud function to send a subscription message. This cloud function can be called on a small terminal, we can use a cloud function to call a cloud function, and we can use a time trigger to call a cloud function.

  • Small terminal to send subscription messages, some business needs to be in the user in the small program to complete an operation, you need to send subscription messages to the user, such as carding, check-in, payment, publishing success, these services are dependent on the user's operation, when the operation is completed can be called in the callback function to send a subscription message cloud function;

  • Another is that if you are the administrator of a small program, the management interface of subscription messages is also in the small program, when the administrator clicks on a fixed point or mass feed of subscription messages on the small program side, you can also call the cloud function to send subscription messages;

  • When you send a subscription message using a timed trigger, the subscription message can be sent periodically and on a timed nature, eliminating the need for a user/administrator click to send it in conjunction with the business scenario.

Cloud functions call the subscribeMessage.send interface in two ways, one is https://s.com and the other is a cloud call, which is recommended. There are a lot of details to note when calling the subscribeMessage.send interface, especially the data format, which must meet the format requirements.

The data that subscribes to the message must correspond to the template message one-to-one

For example, we applied for a template that subscribes to the course's opening reminder in the following format:

  1. 姓名{{phrase1.DATA}}
  2. 课程标题{{thing2.DATA}}
  3. 课程内容{{thing3.DATA}}
  4. 时间{{date5.DATA}}
  5. 课程进度{{character_string6.DATA}}

The corresponding data is written as follows phrase1, thing2, thing3, date5, character_string6, these need to correspond one by one, the parameters can not be more or less, the number behind the parameters such as date5 can not be changed to date6, otherwise it will report "openapi.subscribeMessage.send:fail argument invalid! hint: error, that is, what parameters are in the template, you can only write what parameters in order:

  1. data: {
  2. "phrase1": {
  3. "value": '李东'
  4. },
  5. "thing2": {
  6. "value": '零基础云开发技术训练营第7课'
  7. },
  8. "thing3": {
  9. "value": '列表渲染与条件渲染'
  10. },
  11. "date5": {
  12. "value": '2019年10月20日 20:00'
  13. },
  14. "character_string6": {
  15. "value": 3
  16. }
  17. }

The content format of the subscription message parameter value must meet the requirements

In the technical documentation, there is a content format requirement for the value of the subscription message parameter, which requires a strict one-to-one correspondence when writing the content of the subscription message, otherwise a formatting error will occur.

The parameter category Description of the parameters The parameter value limit Description
thing. DATA Things 20 characters or less Can be combined with Chinese characters, numbers, letters, or symbols
number. DATA Digital Numbers up to 32 digits Only numbers can be taken with a small number
letter. DATA Letters Letters up to 32 bits Only letters are 1
symbol. DATA Symbol Within 5 bits of the symbol Only symbols are signed
character_string. DATA String A number, letter, or symbol within 32 digits Can be combined with numbers, letters, or symbols
time. DATA Time 24-hour time format (supported by years and months) For example: 15:01, or: October 1, 2019 at 15:01
date. DATA Date Year-to-month format (support for 24-hour time) For example: October 1, 2019, or October 1, 2019 at 15:01
amount. DATA Amount 1 currency symbol plus 10 digits or less pure number, can be with a small number, the end can be with a "yuan" Can be taken with a small number
phone_number. DATA Phone Within 17 digits, numbers, symbols Phone number, e.g. : 86-0766-66888866
car_number. DATA License plate Within 8 digits, the first and last digits can be Chinese characters, and the rest are letters or numbers License plate number: Guangdong A8Z888 hanging
name. DATA Name 10 or less pure Chinese characters or 20 or less pure letters or symbols Chinese 10 chinese characters; 20 letters in pure English; Chinese and letters are mixed into Chinese characters, within 10 words
phrase. DATA Chinese characters 5 chinese characters or less 5 or less pure Chinese characters, e.g. in distribution

Here are some of the mistakes you can easily make while using them:

  • There may already be template messages in a format that doesn't work as well as you want, such as the message you want to send is the 姓名{{phrase1.DATA}} the name can only be Chinese, and must be within 5 words, then you can not change without authorization, can only apply for or reus other template ID;

  • Each format has strict requirements for the length and type of string, such as the following, which must be less than 20 characters, not more than 20 characters;

Third, use cloud calls to send subscription messages

As we said earlier, in the small terminal which user click authorization will only give which user increase the number of authorizations, and with the help of the cloud function to send subscription messages users can send subscription messages to anyone, to which person needs who has authorization times, will reduce the number of authorizations of which people, this should be distinguished.

1, send a single subscription message

Create a new cloud function, such as subscribeMessage, and then add subscribeMessage.send permissions at config.json, using the cloud function incremental upload to update the profile.

  1. {
  2. "permissions": {
  3. "openapi": [
  4. "subscribeMessage.send"
  5. ]
  6. }
  7. }

Then enter the following code in index.js, note that the openid here is the user's own, this applies to the user in the small terminal after completing a business operation, the user himself to send a subscription message;

  1. const cloud = require('wx-server-sdk')
  2. cloud.init({
  3. env: cloud.DYNAMIC_CURRENT_ENV,
  4. })
  5. exports.main = async (event, context) => {
  6. const { OPENID } = cloud.getWXContext()
  7. try {
  8. const result = await cloud.openapi.subscribeMessage.send({
  9. touser: "oUL-m5FuRmuVmxvbYOGuXbuEDsn8",
  10. page: 'index',
  11. templateId: "qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44",
  12. data: {
  13. "phrase1": {
  14. "value": '小明'
  15. },
  16. "thing2": {
  17. "value": '零基础云开发技术训练营第7课'
  18. },
  19. "thing3": {
  20. "value": '列表渲染与条件渲染'
  21. },
  22. "date5": {
  23. "value": '2019年10月20日 20:00'
  24. },
  25. "character_string6": {
  26. "value": 3
  27. }
  28. }
  29. })
  30. return result
  31. } catch (err) {
  32. console.log(err)
  33. return err
  34. }
  35. }

2, bulk send subscription messages

Since the parameters of subscribeMessage.sendtemplateId and toucher are strings, a subscribeMessage.send can only send one subscription message to one user, which gives more users such as 100 To send a subscription message within 0 people (the cloud function can get 1000 data at a time), you need to combine all authorized users in the database query database and then loop through the message, and use inc to subtract the number of authorizations after the send.

Since we embedded all user-authorized subscription messages into the array of templs, and the contents of the subscription messages to be sent came from eligible objects in the templs array, which involves the processing of relatively complex arrays, data analysis processing artifact aggregations are useful (of course, we can also use normal queries, ordinary queries get a list of records, and then use some array methods such as filter, map, etc. to take out a list of templs nested objects in the list).

  1. const cloud = require('wx-server-sdk')
  2. cloud.init({
  3. env: cloud.DYNAMIC_CURRENT_ENV
  4. })
  5. const db = cloud.database()
  6. const _ = db.command
  7. const $ = db.command.aggregate
  8. exports.main = async (event, context) => {
  9. const templateId ="qY7MhvZOnL0QsRzK_C7FFsXTT7Kz0-knXMwkF1ewY44"
  10. try {
  11. const messages = (await db.collection('messages').aggregate()
  12. .match({ //使用match匹配查询
  13. "templs.templateId":templateId, //注意这里templs.templateId的写法
  14. "done":false,
  15. "status":1
  16. })
  17. .project({
  18. _id:0,
  19. templs: $.filter({ //从嵌套的templs数组里取出模板ID满足条件的对象
  20. input: '$templs',
  21. as: 'item',
  22. cond: $.eq(['$$item.templateId',templateId])
  23. })
  24. })
  25. .project({
  26. message:$.arrayElemAt(['$templs', 0]), //符号条件的是只有1个对象的数组,取出这个对象
  27. })
  28. .end()).list //使用聚合查询到的是一个list对象
  29. const tasks = []
  30. for (let item in messages) {
  31. const promise = cloud.openapi.subscribeMessage.send({
  32. touser: item.message._openid,
  33. page: 'index',
  34. templateId: item.message.templateId,
  35. data: item.message.data
  36. })
  37. tasks.push(promise)
  38. }
  39. return (await Promise.all(tasks)).reduce((acc, cur) => {
  40. return {
  41. data: acc.data.concat(cur.data),
  42. errMsg: acc.errMsg,
  43. }
  44. })
  45. } catch (err) {
  46. console.log(err);
  47. return err;
  48. }
  49. }

In particular, don't put the statements that query the database in the loop, that is, we can take out 1000 users who need to send a subscription message at once. Then combine map and Project.all method to send subscription messages to these 1000 users, and then a one-time to all of these 1000 data for atomic self-growth, can not be processed one by one, otherwise it will cause a great waste of database performance and exceed the maximum number of connections, but also lead to cloud functions in the life cycle of up to 60s also send not hundreds of subscription messages.

Fourth, the use of timed triggers to send subscription messages

But when there are hundreds of thousands of users who want to send subscription messages, what should be said? If you let the cloud function execute all, even if you modify the execution timeout of the cloud function to 60s, you should time out, at which point we can combine the timer to send subscription messages.

Use a timed trigger to send subscription messages, that is, on the cloud development service side of a small program, use a timed trigger to invoke the cloud calling interface of the subscription message, openapi.subscribeMessage.send. When we're sending subscription messages to hundreds of thousands of people on a timely day, it's timed triggers that don't just need to be triggered at 9 a.m. every morning, but they also need to be able to perform a cloud function every once in a while after 9 a.m. to send subscription messages to hundreds of thousands of users.

This is when the Cron expression can be written like this, meaning that the cloud function is executed every 40s every morning from 9 a.m. to 11 a.m.:

  1. 0/40 * 9-11 * * * *

Of course, the periodic settings here can be combined with the actual execution of the time set by the cloud function, to take full account of the time-out of the cloud function.

Cloud calls also support interfaces that combine templates and add to the personal template library under the account, subscribeMessage.addTemplate delete the personal template under the subscribeMessage.deleteTemplate get the class name of the small subscribeMessage.getCategory subscribeMessage.getTemplateList and more.