需求背景
基于微信的通知渠道,微信小程序为开发者提供了可以高效触达用户的模板消息能力,在用户本人与小程序页面有交互行为后触发,通过微信聊天列表中的服务通知可快捷进入查看消息,点击查看详情还能跳转到下发消息的小程序的指定页面。
微信小程序允许下发模板消息的条件分为两类:支付或者提交表单。通过提交表单来下发模板消息的限制为“允许开发者向用户在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) ""); // 模板内容 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天内,对用户发送多条模板消息唤回用户。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
稳了!魔兽国服回归的3条重磅消息!官宣时间再确认!
昨天有一位朋友在大神群里分享,自己亚服账号被封号之后居然弹出了国服的封号信息对话框。
这里面让他访问的是一个国服的战网网址,com.cn和后面的zh都非常明白地表明这就是国服战网。
而他在复制这个网址并且进行登录之后,确实是网易的网址,也就是我们熟悉的停服之后国服发布的暴雪游戏产品运营到期开放退款的说明。这是一件比较奇怪的事情,因为以前都没有出现这样的情况,现在突然提示跳转到国服战网的网址,是不是说明了简体中文客户端已经开始进行更新了呢?
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]