背景
最近做个排号叫号的微信小程序,想用户在微信小程序上进行排号,商家在小程序上进行叫号,叫号的通知发送到用户微信里.这里就要用到订阅消息.
先看效果图
1.创建模板
登录微信公众平台
创建一个自己需要的模版,具体创建请自行查阅,今天的重点不在这里…
发送订阅消息的三个步骤.
一.获取用户的openid
用户的openid的获取,我是在用户使用微信登录时进行获取的,具体可以查看我微信登录的文章:传送门
二.获取access_token
我们首先来看看access_token是什么,官方的说明是:
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。
简单来说,access_token就是小程序官方给我们提供的一个凭证,如果要调用官方的接口,就必须先获取凭证,所以我们先来谈谈怎么获取access_token.
还是先看官方文档
从官方文档我们可以看到,我们需要以下几个参数
参数 | 是否必须 | 说明 |
---|---|---|
grant_type | 是 | 获取access_token填写client_credential |
appid | 是 | 第三方用户唯一凭证 |
secret | 是 | 第三方用户唯一凭证密钥,即appsecret |
grant_type是一个固定的值,appid和secret是需要我们填入的,这两个值在我们的小程序后台就可以拿到,具体可以查看我微信登录的文章:传送门
可以在微信小程序后台去寻找appid和secret
Java代码部分
具体思路就是先向微信提供的这个url发送一个get请求.
微信官方给的正常返回值为
新建一个pojo来存储返回的参数
import lombok.Data;
/**
* @author zty
* @date 2020/4/23 下午2:26
* @description:
*/
@Data
public class getAccessTokenModel {
private String access_token;
private Integer expires_in;
}
然后利用一个http工具类发送请求,一个json转model工具类来转换一下.这两个工具类具体代码后面贴出
这就是我们获取access_token的代码了,获取之后将他存入redis,设置有效时间,超时就重新获取.
/**
* 获取AccessToken
*
* @return
*/
public String getAccessToken() {
if (redisUtil.get("access_token") != null) {
return (String) redisUtil.get("access_token");
}
//GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
String url = "https://api.weixin.qq.com/cgi-bin/token";
Map<String, String> param = new HashMap<>();
param.put("grant_type", "client_credential");
param.put("appid", "wx7e139fc4dec9fd08");
param.put("secret", "d483c36cf9f93500a17aa0cb788a0f48");
String vxResult = HttpClientUtil.doGet(url, param);
log.info(vxResult);
getAccessTokenModel accessTokenModel = JsonUtils.jsonToPojo(vxResult, getAccessTokenModel.class);
redisUtil.set("access_token", accessTokenModel.getAccess_token(), accessTokenModel.getExpires_in());
return accessTokenModel.getAccess_token();
//POST https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN
}
使用这两个工具类需要引入俩个maven依赖
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
package com.vx.utils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
/**
* @author zty
*/
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}
package com.vx.utils;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
*
* @Title: JsonUtils.java
* @Package com.lee.utils
* @Description: 自定义响应结构, 转换类
* Copyright: Copyright (c) 2016
* Company:Nathan.Lee.Salvatore
*
* @author zty
* @date 2020年4月15日 下午11:05:03
* @version V1.0
*/
public class JsonUtils {
// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
* <p>Title: pojoToJson</p>
* <p>Description: </p>
* @param data
* @return
*/
public static String objectToJson(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData json数据
* @param clazz 对象中的object类型
* @return
*/
public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
* <p>Title: jsonToList</p>
* <p>Description: </p>
* @param jsonData
* @param beanType
* @return
*/
public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List<T> list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
好了,这样我我们就获取到了access_token,注意我是存在redis里的
三.发送消息到微信
小程序端获取授权我这里就不在介绍了,具体看微信的官方文档
我们通过上面第二步,成功的获取到了access_token。下面就要调用小程序官方为我们提供的发送消息的接口了,先看下官方文档。
上面有些参数是我们发送消息所需要的.
这里需要注意的是,我们要向用户发送订阅消息,小程序端就要去诱导用户授权
正常来说,一次授权只允许发送一条消息.
java代码
先创建一个类,注意Thing要与你模板对应
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author zty
* @date 2020/4/23 下午8:31
* @description:
*/
@Data
public class SubscribeMessageVO {
//具体的订阅消息的key {{thing4.DATA}} 则key为thing4
private Thing4 thing4;
private Thing6 thing6;
private Thing7 thing7;
@Data
@AllArgsConstructor
public static class Thing4{
private String value;
}
@Data
@AllArgsConstructor
public static class Thing6{
private String value;
}
@Data
@AllArgsConstructor
public static class Thing7{
private String value;
}
}
我这里发送用到了消息队列,可以忽略
/**
* 发送订阅消息
*
* @param peopleSet
*/
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,//创建临时队列
exchange = @Exchange(value = "updateHistory", type = "fanout")
)
})
public void sendMessage(String peopleSet) {
String[] temp;
temp = peopleSet.split("\\+");
String p1 = temp[0];
Long p2 = Long.valueOf(temp[1]);
Long p3 = Long.valueOf(temp[2]);
ActivityUserHistory history = activityUserHistoryMapper.selectByOpenId2(getUserIdByOpenId(p1), p2, p3);
SubscribeMessageVO bean = new SubscribeMessageVO();
bean.setThing4(new SubscribeMessageVO.Thing4(history.getActivityName() + ":" + history.getName()));
bean.setThing6(new SubscribeMessageVO.Thing6(history.getAddress()));
bean.setThing7(new SubscribeMessageVO.Thing7("请到服务处联系工作人员"));
WxMssVO wxMssVO = new WxMssVO();
wxMssVO.setTouser(p1);
wxMssVO.setTemplate_id("***");//模板id号码
wxMssVO.setData(bean);
push(wxMssVO);
}
public void push(WxMssVO wxMssVO) {
String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + getAccessToken();
String json = JsonUtils.objectToJson(wxMssVO);
String vxResult = HttpClientUtil.doPostJson(url, json);
log.info("返回的内容:" + vxResult);
}
将信息加入SubscribeMessageVO这个类的一个对象中,然后转为json,继续调用我们刚刚的http工具类发送.这样就能得到我们开始时的订阅消息了