【实战】微信小程序订阅消息,后台SpringBoot实现

消息能力是小程序能力中的重要组成,以便实现服务的闭环和更优的体验。 此前的小程序模板消息接口于2020年1月10日下线(我们开发者太难了…),2.10.0 版本开始,开发版和体验版小程序将禁止使用模板消息 fomrId。开发者可使用订阅消息功能。订阅消息特点:

  • 订阅消息推送位置:服务通知
  • 订阅消息下发条件:用户自主订阅
  • 订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面

订阅消息的使用主要以下几个步骤:

  1. 获取模板 ID
  2. 获取下发权限
  3. 调用接口下发订阅消息

根据我的理解和结合系统,对订阅消息功能总结了一个流程图,下面会有每一步的详细操作及代码
【实战】微信小程序订阅消息,后台SpringBoot实现_第1张图片

1. 获取模板ID

在微信公众平台手动配置获取模板 ID,登录 微信开发者平台 获取模板,如果没有合适的模板,可以申请添加新模板,审核通过后可使用。
【实战】微信小程序订阅消息,后台SpringBoot实现_第2张图片
【实战】微信小程序订阅消息,后台SpringBoot实现_第3张图片

一次性订阅和长期订阅

  1. 一次性订阅消息用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。
  2. 一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。

了解更多,自行参考官方文档的消息

2. 获取下发权限

订阅消息的发送同时还需要用户同意接收,否则用户将不会收到消息。在小程序中需要调用下面API,调起客户端小程序订阅消息界面。

由于开发文档写的细节不够到位,可能会遇到许多坑。

  1. 这个发送订阅消息的API无法在模拟器上生效,必须是真机调式状态下才可正常使用。
  2. 需要写回调函数才能生效。原理自己去了解吧…
wx.requestSubscribeMessage({
  // 你理解为询问用户,愿不愿意接收这个模板的订阅消息	
  tmplIds: [''],	//模板ID
  success(res) {
    console.log('已授权接收订阅消息')
  },
  complete(res) {
    console.log(res)
  }
})

3. 调用接口下发订阅消息

参考下发订阅消息官方文档:发送订阅消息

3.1 https请求

POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN

调用接口url:https://api.weixin.qq.com/cgi-bin/message/subscribe/send
请求方式method:POST
请求参数data:access_token

所以我们首先需要拿到到的是参数:access_token(后台接口调用凭据),参考官方API 文档,我自己实践了一下,请参考:获取access_token

3.2 POST请求体参数

post请求体中对应的参数,必须是json格式。注意仔细看官方文档,切记,格式很重要!!!

//介绍一下几个必须填写的请求参数
access_token:接口调用凭证
touser:接收者(用户)的 openid
template_id:所需下发的订阅模板id
data:模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }
//更多仔细查看官方API文档

至此,我们简单分析一下目前的步骤:参数accss_token和template_id已经在上述获取到,data只需注意格式,即可。

接下来,需要获取服务端openId,这个我个人也总结了方法,参考:获取openId

请求参数Param如下:
{
	"touser": "接收者(用户)的 openid",
	"template_id": "订阅模板id",
	"data": {
		"name1": {
			"value": "李佳琪"
		},
		"thing2": {
			"value": "请求组队"
		}
	}
}

特别说明,这个data的name1、thing2。需自行查看小程序的模板详情:
【实战】微信小程序订阅消息,后台SpringBoot实现_第4张图片
到目前为止,整个基本流程,我们已经走通。

4. 项目实践(小程序端 + Java服务端SpringBoot)

//Java服务端SpringBoot
public static void SendMsg(){
        String openId = "o3v...0Mg";
        System.out.println("openId获取成功 == " + openId);
        String accessToken = GetAccessToken.getAccessToken();
        System.out.println("access_token获取成功 == " + accessToken);
        String templateId = StaticData.templateId;
        System.out.println("template_id获取成功 == " + templateId);
		//这三个怎么获取,前面已经说了
		
        Map<String,Object> param = new HashMap<>();
        param.put("touser", openId);
        param.put("template_id", templateId);

        //data
        Map<String,Object> data = new HashMap<>();
        data.put("name1", getDataMap("李如瑾"));
        data.put("name2", getDataMap("raft项目组"));
        data.put("date3", getDataMap("2020-08-23 00:24:54"));
        data.put("name4", getDataMap("李佳琪"));

        param.put("data", data);
        System.out.println(param);
        System.out.println("param转字符串: " + JSON.toJSONString(param));

        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;

        String sendPost = GetOpenId.sendPost(url, JSON.toJSONString(param));
        System.out.println(sendPost);
        // 解析相应内容(转换成json对象)
        JSONObject json = JSON.parseObject(sendPost);

        System.out.println(json);
        System.out.println("发送成功");
    }

	public static Map<String, String> getDataMap(String msg){
        Map<String, String> map = new HashMap<>();
        map.put("value", msg);
        return map;
    }

	/**
     * 向指定 URL 发送POST方法的请求
     *
     * @param url
     *            发送请求的 URL
     * @param paramUrl
     *            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
     * @return 所代表远程资源的响应结果
     */
    public static String sendPost(String url, String paramUrl) {
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            JSONObject param = new JSONObject(paramUrl);
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常!" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输出流、输入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;
    }
//小程序JS
b1: function(){
	//接受者必须同意接受订阅消息
    wx.requestSubscribeMessage({
      tmplIds: ['HxYbL...yohFk'],			//模板ID
      success(res) {
        console.log('已授权接收订阅消息')
      },
      complete(res) {
        console.log(res)
      }
    })
  },

  b2: function(){
    wx.request({
      url: 'http://19...02:81/send',		//本机ip,不要用localhost访问
      success(res) {
        console.log(res.data)
      },
      complete(res) {
        console.log(res)
      }
    })
  }

服务端打印
【实战】微信小程序订阅消息,后台SpringBoot实现_第5张图片
【实战】微信小程序订阅消息,后台SpringBoot实现_第6张图片
【实战】微信小程序订阅消息,后台SpringBoot实现_第7张图片
开发心得,说几点踩到的坑和需要注意的地方。

  1. 不能用模拟器测试,必须真机调式
  2. 如果服务端在本地,不要用localhost,用本机IPv4访问
  3. 检查好appId、appSecret、template_id是不是来自同一个微信小程序,并且这些信息尽量不要外泄、暴露
  4. 真机调式遇到这个问题{errMsg: “request:fail -102:net::ERR_CONNECTION_REFUSED”}时
    (1). 看下上述说的第二点改过来没有
    (2). 在小程序开发工具找到代理设置那一项,勾选“不使用任何代理,使用网络直连”
    (3). 修改电脑本机IPv4 DNS 服务器为114.114.114.114。怎么改?百度就好了
  5. 上述实例应用的代码中,接受者openId、access_token、template_id代码不全,但是上面都写了怎么获取它们

最后,有个很实质的问题也是无法改变的。大部分服务类目的订阅信息的性质是一次性订阅

  1. 只有当用户运行接受一次订阅信息后,才能接受一次消息,再次发生,收不到(痛点)。
  2. 如果勾选“总是保持以上选择,不再询问”,这个授权框再不会弹出来了。但是还是需要点下权限按钮,触发下,只是不弹框,这样就能再次收到消息。

你可能感兴趣的:(SpringBoot,小程序开发指南,小程序,java)