微信小程序模板消息限制实现无限制主动推送的示例代码

需求背景

基于微信的通知渠道,微信小程序为开发者提供了可以高效触达用户的模板消息能力,在用户本人与小程序页面有交互行为后触发,通过微信聊天列表中的服务通知可快捷进入查看消息,点击查看详情还能跳转到下发消息的小程序的指定页面。

微信小程序允许下发模板消息的条件分为两类:支付或者提交表单。通过提交表单来下发模板消息的限制为“允许开发者向用户在7天内推送有限条数的模板消息(1次提交表单可下发1条,多次提交下条数独立,相互不影响)”。

然而,用户1次触发7天内推送1条通知是明显不够用的。比如,签到功能利用模板消息的推送来提醒用户每天签到,只能在用户前一天签到的情况下,获取一次推送模板消息的机会,然后用于第二天向该用户发送签到提醒。但是很多情况下,用户在某一天忘记签到,系统便失去了提醒用户的权限,导致和用户断开了联系;再比如,系统想主动告知用户即将做某活动,然而由于微信小程序被动触发通知的限制,系统将无法主动推送消息。

如何突破模板消息的推送限制?

突破口:“1次提交表单可下发1条,多次提交下发条数独立,相互不影响”

为了突破模板消息的推送限制,实现7天内任性推送,只需收集到足够的推送码,即每次提交表单时获取到的formId。一个formId代表着开发者有向当前用户推送模板消息的一次权限。

客户端

收集推送码

当表单组件中的属性report-submit=true时表示发送模板消息,提交表单便可以获取formId。接下来只要对原先的页面进行改造,将用户原先绑定了点击事件的界面用表单组件中的button按钮组件来代替,即把用户的交互点击的bindtap事件由表单bindsubmit来代替,从而捕获用户的点击事件来生成更多的推送码。

// 收集推送码
Page({
  formSubmit: funcition(e) {
let formId = e.detail.formId;
this.collectFormIds(formId); //保存推送码
let type = e.detail.target.dataset.type; // 根据type执行点击事件
},

  collectFormIds: function(formId) {
let formIds = app.globalData.globalFormIds; // 获取全局推送码数组
if (!formIds)
      formIds = [];
let data = {
      formId: formId,
      expire: new Data().getTime() + 60480000 // 7天后的过期时间戳
}
    formIds.push(data);
    app.globalData.globalFormIds = formIds;
},
})

上报推送码

等待用户下一次发起网络请求时,将globalFormIds发送给服务器。

// 上报推送码
Page({
  onLoad: funcition(e) {
this.uploadFormIds(); //上传推送码
},

  collectFormIds: function(formId) {
var formIds = app.globalData.globalFormIds; // 获取全局推送码
if (formIds.length) {
      formIds = JSON.stringify(formIds); // 转换成JSON字符串
      app.globalData.gloabalFomIds = ''; // 清空当前全局推送码
}
    wx.request({ // 发送到服务器
      url: 'http://xxx',
      method: 'POST',
      data: {
        openId: 'openId',
        formIds: formIds
},
      success: function(res) {
}
});
},
})

服务端

存储推送码

高频IO,采用Redis来存储推送码。

/**
* 收集用户推送码
*
* @param openId    用户的openid
* @param formTemplates 用户的表单模板
*/
public void collect(String openId, List<FormTemplateVO> formTemplates) {
  redisTemplate.opsForList().rightPushAll("mina:openid:" + openId, formTemplates);
}

推送模板消息

下面实现了群发的功能,针对特定用户类似。

/**
* 推送消息
*
* @param templateId 模板消息id
* @param page    跳转页面
* @param keyWords  模板内容
*/
public void push(String templateId, String page, String keyWords) {
String logPrefix = "推送消息";

// 获取access token
String accessToken = this.getAccessToken();

// 创建消息通用模板
MsgTemplateVO msgTemplateVO = MsgTemplateVO.builder().template_id(templateId).build();
// 跳转页面
  msgTemplateVO.setPage(StringUtils.isNotBlank(page) ? page : "");
// 模板内容
if (StringUtils.isNotBlank(keyWords)) {
String[] keyWordArr = keyWords.split(BaseConsts.COMMA_STR);
Map<String, MsgTemplateVO.KeyWord> keyWordMap = new HashMap<>(8);
for (int i = 0; i < keyWordArr.length; i++) {
MsgTemplateVO.KeyWord keyWord = msgTemplateVO.new KeyWord(keyWordArr[i]);
      keyWordMap.put(MsgTemplateVO.KEYWORD + (i + 1), keyWord);
}
    msgTemplateVO.setData(keyWordMap);
} else {
    msgTemplateVO.setData(Collections.emptyMap());
}

// 获取所有用户
List<String> openIdList = minaRedisDao.getAllOpenIds();

for (String openId : openIdList) {
// 获取有效推送码
String formId = minaRedisDao.getValidFormId(openId);
if (StringUtils.isBlank(formId)) {
      LOGGER.error("{}>>>openId={}>>>已无有效推送码[失败]", logPrefix, openId);
continue;
}

// 指派消息
MsgTemplateVO assignMsgTemplateVO = msgTemplateVO.assign(openId, formId);

// 发送消息
Map<String, Object> resultMap;
try {
String jsonBody = JsonUtils.getObjectMapper().writeValueAsString(assignMsgTemplateVO);

String resultBody = OkHttpUtils.getInstance().postAsString(messageUrl + accessToken, jsonBody);
      resultMap = JsonUtils.getObjectMapper().readValue(resultBody, Map.class);
} catch (IOException e) {
      LOGGER.error("{}>>>openId={}>>>{}[失败]", logPrefix, openId, e.getMessage(), e);
continue;
}

if ((int) resultMap.get(ResponseConsts.Mina.CODE) != 0) {
      LOGGER.error("{}>>>openId={}>>>{}[失败]", logPrefix, openId, resultMap.get(ResponseConsts.Mina.MSG));
continue;
}

    LOGGER.info("{}>>>openId={}>>>[成功]", logPrefix, openId);
}
}

/**
* 根据用户获取有效的推送码
*
* @param openId 用户的openid
* @return 推送码
*/
public String getValidFormId(String openId) {
List<FormTemplateVO> formTemplates = redisTemplate.opsForList().range("mina:openid:" + openId, 0, -1);

String validFormId = "";
int trimStart = 0;

int size;
for (int i = 0; i < (size = formTemplates.size()); i++) {
if (formTemplates.get(i).getExpire() > System.currentTimeMillis()) {
      validFormId = formTemplates.get(i).getFormId();
      trimStart = i + 1;
break;
}
}

// 移除本次使用的和已过期的
  redisTemplate.opsForList().trim(KEY_MINA_PUSH + openId, trimStart == 0 ? size : trimStart, -1);

return validFormId;
}

以上方案可以实现在用户最后一次使用小程序后的7天内,对用户发送多条模板消息唤回用户。

(0)

相关推荐

  • 在小程序中推送模板消息的实现方法

    前段时间剁手了 PS4,在浏览商店时,发现官方商店真的不好用,主要是网络原因,次要是页面设计.所以就想自己做一个游戏查询的小程序,可以关注某个游戏,然后在这个游戏打折的时候发送通知给用户.最后发现有个很大的问题是:小程序没法直接给用户推送消息(当时还不知道模板消息),服务号才能.然后就用邮箱实现了通知功能,但是邮箱的局限实在是太大了(各大免费邮箱每天的发件数都很小,自己搭建的邮件服务器虽然没有发件限制,但是大概率会被放到垃圾箱). 然后某天在微信小程序的管理后台发现了模板消息这个东西,查了会资料

  • 微信小程序实现发送模板消息功能示例【通过openid推送消息给用户】

    本文实例讲述了微信小程序实现发送模板消息功能.分享给大家供大家参考,具体如下: 一.获取access_token access_token是接口调用的凭证,目前有效期为两个小时,需要定时刷新,重复获取将导致上次获取的access_token失效.(注:不建议每次调用需要access_token的接口,都去重新获取access_token,会导致失败) 获取access_token的接口地址: https://api.weixin.qq.com/cgi-bin/token?grant_type=c

  • 微信小程序模板消息限制实现无限制主动推送的示例代码

    需求背景 基于微信的通知渠道,微信小程序为开发者提供了可以高效触达用户的模板消息能力,在用户本人与小程序页面有交互行为后触发,通过微信聊天列表中的服务通知可快捷进入查看消息,点击查看详情还能跳转到下发消息的小程序的指定页面. 微信小程序允许下发模板消息的条件分为两类:支付或者提交表单.通过提交表单来下发模板消息的限制为"允许开发者向用户在7天内推送有限条数的模板消息(1次提交表单可下发1条,多次提交下条数独立,相互不影响)". 然而,用户1次触发7天内推送1条通知是明显不够用的.比如,

  • 微信小程序获取公众号文章列表及显示文章的示例代码

    微信小程序中如何打开公众号中的文章,步骤相对来说不麻烦. 1.公众号设置 小程序若要获取公众号的素材,公众号需要做一些设置. 1.1 绑定小程序 公众号需要绑定目标小程序,否则无法打开公众号的文章. 在公众号管理界面,点击小程序管理 --> 关联小程序 输入小程序的AppID搜索,绑定即可. 1.2 公众号开发者功能配置 (1) 在公众号管理界面,点击开发模块中的基本配置选项. (2) 开启开发者秘密(AppSecret),注意保存改秘密. (3) 设置ip白名单,这个就是发起请求的机器的外网i

  • 基于Taro的微信小程序模板消息-获取formId功能模块封装实践

    前言 在微信小程序中,小程序提供了一种能力-模板消息,官方文档是这样描述的:"基于微信的通知渠道,我们为开发者提供了可以高效触达用户的模板消息能力,以便实现服务的闭环并提供更佳的体验." 看图说话: 下发模板消息效果图 这无疑又给小程序提供了与用户进行通知.反馈的新能力,但是小程序为了防止模板消息对用户造成信息轰炸,影响用户体验,也设置了一些规矩: 模板推送位置:服务通知 模板下发条件:用户本人在微信体系内与页面有交互行为后触发 模板跳转能力:点击查看详情仅能跳转下发模板的该帐号的各个

  • 微信小程序模板消息推送的两种实现方式

    最近在做微信小程序,为了让用户能更加方便的获取到小程序内部的通知,便想着用模板消息来解决此类问题.首先介绍一下两种方法 第一种是传统的按照微信开发文档来实现模板消息的获取,首先要明确的是你要有一个第三方的服务器,因为微信现在规定在小程序内部不能直接请求微信的服务器,而是要请求第三方服务器,由第三方服务器来请求微信的服务器 1.传统方式实现模板消息 1.1获取用户openId 这一步可以在wx.login方法内获取,用来标注模板消息的接受者 1.2获取模板ID 有两个方法可以获取模版ID 通过模版

  • 微信小程序模板和模块化用法实例分析

    本文实例讲述了微信小程序模板和模块化用法.分享给大家供大家参考,具体如下: template 模板,在定义的时候使用name属性,如以下声明方式 <template name="mytemp"> <view> name:{{names}} address:{{add}} </view> </template> 模板不能直接在界面上显示,需要调用的时候才能显示出来,调用的时候需要使用is属性来说明调用的是哪个模板,并且传参的形式可以有多种

  • 微信小程序模板(template)使用详解

    本文为大家分享了微信小程序模板template的使用方法,供大家参考,具体内容如下 效果图 以MUI的实例首页和列表页面为实例 通过上图,可以看出两个页面的列表部分很相近,以每行作为单元制作模板. template模板 1.模板存放的位置以及使用模板页面存放的位置 template模板的WXML <!--右侧无箭头 --> <template name="listNone"> <view class="tui-menu-list">

  • 微信小程序订阅消息(java后端实现)开发

    订阅消息说明 订阅消息是微信近期新出的一个能力,用来代替原有的模板消息(原有的模板消息即将下线) 订阅消息的功能较模板消息有所提升,"7天"的限制取消,同时有"一次性"和"永久"订阅.(功能上是这样说的,但是实际开发时发现"永久"订阅还是对小程序的服务类目有要求的,客户的小程序只支持"一次性"订阅) 官方通道: 小程序前端:点击进入 小程序服务端:点击进入 开发思路 用户在小程序内触发按钮或进行支付操作时前

  • 微信小程序模板template简单用法示例

    本文实例讲述了微信小程序模板template简单用法.分享给大家供大家参考,具体如下: 模板呢,就是为了方便你重复写一些代码而建立的,目前我指发现它的功能适合建立一些表格类型的数据, 比如,录入认得信息啥的(都有名字,年龄,性别啥的) 1. index.wxml代码 <view class="page"> <template name="aafa"> <view> <text> 姓名:{{name}} 年龄:{{age

  • 微信小程序模板与设置WXML实例讲解

    目录 一.WXML模板语法--数据绑定 二.WXML模板语法--事件绑定 三.WXML模板语法--条件渲染 1.wx:if 2.结合<block>使用wx:if 3.hidden隐藏 四.WXML模板语法--列表渲染 1.wx:for遍历数组 2.手动指定索引和当前项的变量名* 3.wx:key的使用 一.WXML模板语法--数据绑定 数据绑定的基本原则 在 data 中定义数据 在页面对应的.js文件中,把数据定义到data对象中,如下图在home页面中的home.js中定义home页面的d

  • 微信小程序tab切换可滑动切换导航栏跟随滚动实现代码

    简介 看到今日头条小程序页面可以滑动切换,而且tab导航条也会跟着滚动,点击tab导航,页面滑动,切导航栏也会跟着滚动,就想着要怎么实现这个功能 像商城类商品类目如果做成左右滑动切换类目用户体验应该会好很多,我这里只是一个小demo,没有怎么去考虑数据的问题,主要是想着去实现这么个功能,有可能后期引入数据后会出现问题,欢迎提出互相讨论 解决过程 1.在想要实现这个问题的时候找了不少别人的博客看,主体页面布局方面是比较统一的,tab导航栏使用<scroll-view>标签,内容使用<swi

随机推荐