From 9435b90f62546f6abef5ba15aa493ef985abc121 Mon Sep 17 00:00:00 2001 From: fanxb Date: Mon, 29 Jul 2019 15:53:23 +0800 Subject: [PATCH] update --- 前端/微信小程序/1.mpvue中模板消息推送.md | 284 +++++++++++------------ 1 file changed, 140 insertions(+), 144 deletions(-) diff --git a/前端/微信小程序/1.mpvue中模板消息推送.md b/前端/微信小程序/1.mpvue中模板消息推送.md index 98b965c..5204c9d 100644 --- a/前端/微信小程序/1.mpvue中模板消息推送.md +++ b/前端/微信小程序/1.mpvue中模板消息推送.md @@ -7,179 +7,175 @@ categories: - "小程序" --- -前段时间剁手了 PS4,在浏览商店时,发现官方商店真的不好用,主要是网络原因,次要是页面设计。所以就想自己做一个游戏查询的小程序,可以关注某个游戏,然后在这个游戏打折的时候发送通知给用户。最后发现有个很大的问题是:小程序没法直接给用户推送消息(当时还不知道模板消息),服务号才能。然后就用邮箱实现了通知功能,但是邮箱的局限实在是太大了(各大免费邮箱每天的发件数都很小,自己搭建的邮件服务器虽然没有发件限制,但是大概率会被放到垃圾箱)。 + 前段时间剁手了 PS4,在浏览商店时,发现官方商店真的不好用,主要是网络原因,次要是页面设计。所以就想自己做一个游戏查询的小程序,可以关注某个游戏,然后在这个游戏打折的时候发送通知给用户。最后发现有个很大的问题是:小程序没法直接给用户推送消息(当时还不知道模板消息),服务号才能。然后就用邮箱实现了通知功能,但是邮箱的局限实在是太大了(各大免费邮箱每天的发件数都很小,自己搭建的邮件服务器虽然没有发件限制,但是大概率会被放到垃圾箱)。 -然后某天在微信小程序的管理后台发现了模板消息这个东西,查了会资料发现可以通过这个来实现消息推送。要给用户发送模板消息需要`formId/prepay_id`这样一个东西,这个东西是怎么来的呢? + 然后某天在微信小程序的管理后台发现了模板消息这个东西,查了会资料发现可以通过这个来实现消息推送。要给用户发送模板消息需要`formId/prepay_id`这样一个东西,这个东西是怎么来的呢? - + -- formId:这个可以通过表单的提交来获取,需要在
组件中设置属性`report-submit="true"`,这样每次对这个表单的提交一次就会产生一个 formId. -- prepay_id:这个是支付动作产生的,具体的我也不太清楚,毕竟个人小程序并不能支付. + - formId:这个可以通过表单的提交来获取,需要在组件中设置属性`report-submit="true"`,这样每次对这个表单的提交一次就会产生一个 formId. + - prepay_id:这个是支付动作产生的,具体的我也不太清楚,毕竟个人小程序并不能支付. -下面具体来讲讲前后台的实现,前台使用 mpvue,后台使用 java + 下面具体来讲讲前后台的实现,前台使用 mpvue,后台使用 java -# 前台获取 formId + # 前台获取 formId -既然可以通过 form 的 submit 操作来获取到 formId,那我们稍微拓展一下,将我们的小程序页面中所有用户能点击的部分都有 form,button 来包裹一下,这样用户感知不到有表单提交操作,我们也能获取大量的 formId。 + 既然可以通过 form 的 submit 操作来获取到 formId,那我们稍微拓展一下,将我们的小程序页面中所有用户能点击的部分都用 form,button 来包裹一下,这样用户感知不到有表单提交操作,我们也能获取大量的 formId。 -注意不能用叠加的方式来一次点击获取多个 formId,这样方法已经不行了,获取到的都会是一样的。 + 注意不能用叠加的方式来一次点击获取多个 formId,这样方法已经不行了,获取到的都会是一样的。 -## 获取一个模板 + ## 获取一个模板 -要发送模板消息,首先要在小程序的管理后台上添加模板,步骤如下: + 要发送模板消息,首先要在小程序的管理后台上添加模板,步骤如下: -1.在模板库中选择一个模板 + 1.在模板库中选择一个模板 -![选择模板](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190507202930.png) + ![选择模板](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190507202930.png) -2.选择显示参数 + 2.选择显示参数 -选择要显示在消息中的参数,这里选择如下两个参数: + 选择要显示在消息中的参数,这里选择如下两个参数: -![选择参数](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190508101423.png) + ![选择参数](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190508101423.png) -这样就有了一个模板可以用来发消息了,在我的模板中可以看到模板 id,和字段 id + 这样就有了一个模板可以用来发消息了,在我的模板中可以看到模板 id,和字段 id -![模板id](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190508101706.png) + ![模板id](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190508101706.png) -## 获取 formId + ## 获取 formId -formId 是通过表单提交来获取到了,为了获取足够多的 formId,可以将能够点击的组件(比如按钮,列表单元..)包裹在 form 中,这样用户在日常使用中就能够收集到足够多的 formId。下面以包裹一个有赞的按钮为例。因为小程序的限制,设置`form-type`属性的按钮必须为 form 组件的直接子节点,所以并不能够在 form 中使用自定义组件,并将`form-type='submit'`设置到自定义组件中。 + formId 是通过表单提交来获取到了,为了获取足够多的 formId,可以将能够点击的组件(比如按钮,列表单元..)包裹在 form 中,这样用户在日常使用中就能够收集到足够多的 formId。下面以包裹一个有赞的按钮为例。因为小程序的限制,设置`form-type`属性的按钮必须为 form 组件的直接子节点,所以并不能够在 form 中使用自定义组件,并将`form-type='submit'`设置到自定义组件中。 -template 部分如下: + template 部分如下: -```html - - - - - -
-``` + ```html + +
+ + + +
+ ``` -style 目的为了去除原生 button 的背景,边框啥的,把 button 当一个 div 来使用,然后在 button 中设置 form-type 和 click 属性,这样既不影响 formId 的属性,也对原有逻辑不产生任何影响。之所以将 click 放在 button 上是因为 button 内部的组件没法获取到点击事件。 + style 目的为了去除原生 button 的背景,边框啥的,把 button 当一个 div 来使用,然后在 button 中设置 form-type 和 click 属性,这样既不影响 formId 的属性,也对原有逻辑不产生任何影响。之所以将 click 放在 button 上是因为 button 内部的组件没法获取到点击事件。 -formSubmit 代码如下: + formSubmit 代码如下: -```javascript -formSubmit(e) { - let item = { - value: e.mp.detail.formId, - expireTime: Date.now() + 7 * 24 * 60 * 60 * 1000 - }; - this.globalData.formIdList.push(item); -}, -``` - -该函数是在表单提交时触发,用于获取 formId,将 formId 和这个 formId 的过期时间一起存到 globalData 全局数据中。然后找一个时机将这些 formId 发送给服务器保存起来就行了。 - -这里放上我的做法以供参考。 - -我是在每次发送 http 请求前检查是不是有 formId 需要发送到服务器,如果有就将这些数据 JSON 序列化后放到一个自定义 header 中,发送出去,具体代码如下(http 请求工具为:flyio): - -```javascript -var Fly = require("flyio/dist/npm/wx"); -var fly = new Fly(); -//在请求预处理中 -fly.interceptors.request.use(request => { - request.headers["jwt-token"] = wxUtil.getGlobalData("jwt-token"); - //如果有formId就放到header里送过去 - let formIdList = getApp().globalData.formIdList; - if (formIdList.length > 0) { - request.headers["formIdList"] = JSON.stringify(formIdList); - getApp().globalData.formIdList = []; - } - if (request.method == "GET") { - request.params["_t"] = new Date().getTime(); - } - return request; -}); -``` - -下面将后台的实现,基于 Spring Boot. - -# 后台处理 - -## 搜集 formId - -首先需要把 formId 收集起来存到数据库,那么就需要检查每个请求,看 header 中有没有携带 formId,如果有就存到数据库中,注意要和用户对应起来,某个用户点击产生的 formId 只能用于给这个用户推送消息。 - -因为要将 formId 和用户绑定起来,因此我是在身份认证过滤器中进行的 formId 处理,身份认证成功后,处理 formId。代码如下: - -```java -/** - * Description: 从请求头中获取formIdList,并插入数据库 - * - * @param request 请求头 - * @return void - * @author fanxb - * @date 2019/5/6 16:39 - */ -private void checkFormId(HttpServletRequest request) { - String str = request.getHeader(Constant.HEADER_FORM_ID); - if (StringUtil.isEmpty(str)) { - return; - } - List formKeyList = JSON.parseArray(str, FormKey.class); - //UserContextHolder用户将当前线程和用户绑定起来,方便后面获取用户信息 - int userId = UserContextHolder.get().getUser().getUserId(); - formKeyList.forEach(item -> item.setUserId(userId)); - this.formKeyDao.insertMany(formKeyList); -} -``` - -## 发送微信提醒 - -通过官方文档可以知道发送消息的流程如下: - -1 获取 accessToken,调用微信的大多数接口都需要这个东西,这个通过 appId 和 secret 来获取。详情参见:[https://developers.weixin.qq.com/miniprogram/dev/api-backend/auth.getAccessToken.html](https://developers.weixin.qq.com/miniprogram/dev/api-backend/auth.getAccessToken.html) - -2 调用微信发送服务通知的接口.这个接口文档在:[点击跳转](https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html) - -最终发送的 http 请求是这样的: - -```javascript -url: https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=上面获取的accessToken -method: post - -请求体格式为:application/json; charset=utf-8 - -body:{ - "touser": "用户的openId", - "template_id": "模板id", - "page": "点击跳转的小程序url路径", - "form_id": "收集到的formId", - "data": { - "keyword1": { - "value": "您有一个信息的提示消息" - }, - "keyword2": { - "value": "这是消息内容" - } + ```javascript + formSubmit(e) { + let item = { + value: e.mp.detail.formId, + expireTime: Date.now() + 7 * 24 * 60 * 60 * 1000 + }; + this.globalData.formIdList.push(item); }, - "emphasis_keyword": "keyword1.DATA" -} -``` + ``` -data 中的数据的按照顺序 keyword1,keyword2 对应于模板中字段的顺序。 + 该函数是在表单提交时触发,用于获取 formId,将 formId 和这个 formId 的过期时间一起存到 globalData 全局数据中。然后找一个时机将这些 formId 发送给服务器保存起来就行了。 -# 结束 + 这里放上我的做法以供参考。 -**关于源码**: + 我是在每次发送 http 请求前检查是不是有 formId 需要发送到服务器,如果有就将这些数据 JSON 序列化后放到一个自定义 header 中,发送出去,具体代码如下(http 请求工具为:flyio): -一般来说我都会提供源代码的,但是这个小程序是我放到线上准备长期运行的,所以就没有开放源码。 + ```javascript + var Fly = require("flyio/dist/npm/wx"); + var fly = new Fly(); + //在请求预处理中 + fly.interceptors.request.use(request => { + request.headers["jwt-token"] = wxUtil.getGlobalData("jwt-token"); + //如果有formId就放到header里送过去 + let formIdList = getApp().globalData.formIdList; + if (formIdList.length > 0) { + request.headers["formIdList"] = JSON.stringify(formIdList); + getApp().globalData.formIdList = []; + } + if (request.method == "GET") { + request.params["_t"] = new Date().getTime(); + } + return request; + }); + ``` -如果有什么疑问欢迎下面留言评论,一般我都会回复的。 + 下面将后台的实现,基于 Spring Boot. -**PS** -这就是那个小程序,欢迎批评指正。 + # 后台处理 -![小程序](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190430103928.png) + ## 搜集 formId -**本文原创发布于:**[https://www.tapme.top/blog/detail/20190507](https://www.tapme.top/blog/detail/20190507) + 首先需要把 formId 收集起来存到数据库,那么就需要检查每个请求,看 header 中有没有携带 formId,如果有就存到数据库中,注意要和用户对应起来,某个用户点击产生的 formId 只能用于给这个用户推送消息。 + + 因为要将 formId 和用户绑定起来,因此我是在身份认证过滤器中进行的 formId 处理,身份认证成功后,处理 formId。代码如下: + + ```java + /** + * Description: 从请求头中获取formIdList,并插入数据库 + * + * @param request 请求头 + * @return void + * @author fanxb + * @date 2019/5/6 16:39 + */ + private void checkFormId(HttpServletRequest request) { + String str = request.getHeader(Constant.HEADER_FORM_ID); + if (StringUtil.isEmpty(str)) { + return; + } + List formKeyList = JSON.parseArray(str, FormKey.class); + //UserContextHolder用户将当前线程和用户绑定起来,方便后面获取用户信息 + int userId = UserContextHolder.get().getUser().getUserId(); + formKeyList.forEach(item -> item.setUserId(userId)); + this.formKeyDao.insertMany(formKeyList); + } + ``` + + ## 发送微信提醒 + + 通过官方文档可以知道发送消息的流程如下: + + 1 获取 accessToken,调用微信的大多数接口都需要这个东西,这个通过 appId 和 secret 来获取。详情参见:[https://developers.weixin.qq.com/miniprogram/dev/api-backend/auth.getAccessToken.html](https://developers.weixin.qq.com/miniprogram/dev/api-backend/auth.getAccessToken.html) + + 2 调用微信发送服务通知的接口.这个接口文档在:[点击跳转](https://developers.weixin.qq.com/miniprogram/dev/api-backend/templateMessage.send.html) + + 最终发送的 http 请求是这样的: + + ```javascript + url: https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=上面获取的accessToken + method: post + + 请求体格式为:application/json; charset=utf-8 + + body:{ + "touser": "用户的openId", + "template_id": "模板id", + "page": "点击跳转的小程序url路径", + "form_id": "收集到的formId", + "data": { + "keyword1": { + "value": "您有一个信息的提示消息" + }, + "keyword2": { + "value": "这是消息内容" + } + }, + "emphasis_keyword": "keyword1.DATA" + } + ``` + + data 中的数据的按照顺序 keyword1,keyword2 对应于模板中字段的顺序。 + + # 结束 + + 工程全部源码在这里:[github](https://github.com/FleyX/psnDiscountAssistant) + + **PS** + 这就是那个小程序,欢迎批评指正。 + + ![小程序](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190430103928.png) + + **本文原创发布于:**[https://www.tapme.top/blog/detail/20190507](https://www.tapme.top/blog/detail/20190507)