微信小程序模板消息限制,实现无限制主动推送

优采云 发布时间: 2020-08-10 16:07

  需求背景

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

  微信小程序容许下发模板消息的条件分为两类:支付或则递交表单。通过递交表单来下发模板消息的限制为“允许开发者向用户在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 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 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 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 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 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 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线