消息能力是小程序能力中的重要组成,以便实现服务的闭环和更优的体验。 此前的小程序模板消息接口于2020年1月10日下线(我们开发者太难了…),2.10.0 版本开始,开发版和体验版小程序将禁止使用模板消息 fomrId。开发者可使用订阅消息功能。订阅消息特点:
订阅消息的使用主要以下几个步骤:
根据我的理解和结合系统,对订阅消息功能总结了一个流程图,下面会有每一步的详细操作及代码
在微信公众平台手动配置获取模板 ID,登录 微信开发者平台 获取模板,如果没有合适的模板,可以申请添加新模板,审核通过后可使用。
一次性订阅和长期订阅
- 一次性订阅消息用于解决用户使用小程序后,后续服务环节的通知问题。用户自主订阅后,开发者可不限时间地下发一条对应的服务消息;每条消息可单独订阅或退订。
- 一次性订阅消息可满足小程序的大部分服务场景需求,但线下公共服务领域存在一次性订阅无法满足的场景,如航班延误,需根据航班实时动态来多次发送消息提醒。为便于服务,我们提供了长期性订阅消息,用户订阅一次后,开发者可长期下发多条消息。目前长期性订阅消息仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。
了解更多,自行参考官方文档的消息
订阅消息的发送同时还需要用户同意接收,否则用户将不会收到消息。在小程序中需要调用下面API,调起客户端小程序订阅消息界面。
由于开发文档写的细节不够到位,可能会遇到许多坑。
- 这个发送订阅消息的API无法在模拟器上生效,必须是真机调式状态下才可正常使用。
- 需要写回调函数才能生效。原理自己去了解吧…
wx.requestSubscribeMessage({
// 你理解为询问用户,愿不愿意接收这个模板的订阅消息
tmplIds: [''], //模板ID
success(res) {
console.log('已授权接收订阅消息')
},
complete(res) {
console.log(res)
}
})
参考下发订阅消息官方文档:发送订阅消息
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
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。需自行查看小程序的模板详情:
到目前为止,整个基本流程,我们已经走通。
//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)
}
})
}
最后,有个很实质的问题也是无法改变的。大部分服务类目的订阅信息的性质是一次性订阅