在我们做微信公众号开发时,发送模板消息往往是必不可少的功能。今天我们就来说说吧!
1、申请模板消息
首先我们应该知道,模板消息是需要申请的。这个申请就其本身来说是很easy的(我前一天晚上申请的,显示需要2--3个工作日,结果第二天早上就发现已经开通了,所以说腾讯官方还是比较给力的哈)。
但是我们在申请时还是有一些东西要注意,这个在官方的文档有非常详细的说明。
这个我建议你好好看看。选择行业的时候可要谨慎些,因为这个一个月只可以修改一次。
那么,我们来看看在哪里申请?
这里我已经申请过了。
申请之后就耐心等待,审核通过之后再功能这一栏里就会出现模板消息的菜单。你可以看看我上面的截图,就在第三项。
2、添加模板消息
审核通过之后,我们就可以添加模板消息,进行开发了。
这个很简单:
我们点击模板消息进入后,直接在模板库中选择你需要的消息模板添加就可以了,添加之后就会在我的模板中。会有一个模板id,这个模板id在我们发送消息的时候会用到。
3、消息发送功能开发
接下来我们就看看如何发送模板消息:
这个是官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277
我呢,也来说说我的实现吧。为了更方便,我会直接将相关代码贴出来。
文档中我们可以看到接口地址如下:
http请求方式: POST
https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN
这里我们首先需要的就是access_token了,这个在这里就不多说了。通过你的appid和secret就可以获取。
【获取access_token : https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183】
关于相关参数,我直接就将官方文档贴来了(文档写的很清楚):
POST数据示例如下:
{
"touser":"OPENID",
"template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
"url":"http://weixin.qq.com/download",
"miniprogram":{
"appid":"xiaochengxuappid12345",
"pagepath":"index?foo=bar"
},
"data":{
"first": {
"value":"恭喜你购买成功!",
"color":"#173177"
},
"keyword1":{
"value":"巧克力",
"color":"#173177"
},
"keyword2": {
"value":"39.8元",
"color":"#173177"
},
"keyword3": {
"value":"2014年9月22日",
"color":"#173177"
},
"remark":{
"value":"欢迎再次购买!",
"color":"#173177"
}
}
}
参数说明
注:url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。
返回码说明
在调用模板消息接口后,会返回JSON数据包。正常时的返回JSON数据包示例:
{
"errcode":0,
"errmsg":"ok",
"msgid":200228332
}
相信看完以上文档,基本上没有什么问题了。
以下是我的部分代码:
微信模板消息
package com.learn.wecath.bean;
import java.util.HashMap;
/**
* 微信模板消息
*/
public class WechatTemplateMessage {
private String touser; //接收者openid
private String template_id; //模板ID
private String url; //模板跳转链接
private HashMap> data; //data数据
/**
* 参数
* @param value
* @param color 可不填
* @return
*/
public static HashMap item(String value, String color) {
HashMap params = new HashMap();
params.put("value", value);
params.put("color", color);
return params;
}
public String getTouser() {
return touser;
}
public void setTouser(String touser) {
this.touser = touser;
}
public String getTemplate_id() {
return template_id;
}
public void setTemplate_id(String template_id) {
this.template_id = template_id;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public HashMap> getData() {
return data;
}
public void setData(HashMap> data) {
this.data = data;
}
}
/**
* @param userList 接收消息的集合
* @param gongdan 工单的信息 消息内容对象
* @throws Exception
*/
@Override
public void pushGongdanToInstall(List userList, Gongdan gongdan) throws Exception {
logger.info("推送信息给申请人;");
//安装地点
String content = "";
Date now = new Date();
//模板消息
WechatTemplateMessage templateMessage = new WechatTemplateMessage();
//参数
HashMap> data = new HashMap<>(16);
//根据具体模板参数组装
String black = WechatTemplateColourEnum.BLACK.getDesc();
String firstStr = "详细内容:你有一个新的订单需要处理,请及时确认。";
data.put(WechatTemplateRowEnum.FIRST.getDesc(), WechatTemplateMessage.item(firstStr, black));
data.put(WechatTemplateRowEnum.KEYWORD1.getDesc(), WechatTemplateMessage.item(gongdan.getOrderNum(), black));
data.put(WechatTemplateRowEnum.KEYWORD2.getDesc(), WechatTemplateMessage.item(DateUtil.dateTime(now), black));
data.put(WechatTemplateRowEnum.KEYWORD3.getDesc(), WechatTemplateMessage.item(content, black));
String remarkStr = "感谢您的配合!";
data.put(WechatTemplateRowEnum.REMARK.getDesc(), WechatTemplateMessage.item(remarkStr, black));
//设置参数
templateMessage.setData(data);
//设置模板id
templateMessage.setTemplate_id(PropertiesUtil.getProperty("subscriberTemplateId"));
for (User user : userList){
logger.info("推送用户id:" + user.getUserId());
//设置推送用户
templateMessage.setTouser(user.getOpenId());
//推送消息
WechatTemplateMessageUtil.sendTemplateMessage(templateMessage);
}
logger.info("推送信息给申请人结束:" + gongdan.getId());
}
WechatTemplateMessageUtil 发送模板工具类
package com.learn.wecath.util;
import com.learn.wecath.bean.WechatTemplateMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 微信发送模板工具类
*/
public class WechatTemplateMessageUtil {
private static final Logger logger = LoggerFactory.getLogger(WechatTemplateMessageUtil.class);
//发送模板消息url
public static final String TEMPLATE_SEND_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
/**
* @Author: feijq
* @Description:模板发送工具类
* @Date: 2019/11/26 14:56
**/
public static String sendTemplateMessage(WechatTemplateMessage templateMessage) throws Exception{
String data = JsonUtil.toJson(templateMessage);
String url = TEMPLATE_SEND_URL.replace("ACCESS_TOKEN", WechatAccessTokenUtil.getAccessToken());
logger.info("发送模板消息,发送数据:" + data);
String rtn = HttpClient.httpsRequest(url, "POST", data);
logger.info("发送模板消息返回参数:" + rtn);
return rtn;
}
}
微信请求令牌工具栏
package com.learn.wecath.util;
import com.alibaba.fastjson.JSONObject;
import com.learn.wecath.config.RedisHelper;
import com.learn.wecath.config.WechatHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 微信请求令牌工具栏
*/
public class WechatAccessTokenUtil {
private static final Logger logger = LoggerFactory.getLogger(WechatAccessTokenUtil.class);
private static final String TOKENURL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+
WechatHelper.getWechatHelper().getAppId()+"&secret="+ WechatHelper.getWechatHelper().getAppSecret();
//access_token在redis中的key
private static final String WECHAT_ACCESS_TOKEN_REDIS_KEY = "wechat_access_token_redis_key";
//access_token有效时间1.5小时,微信为2小时
private static final int WECHAT_ACCESS_TOKEN_EXPIRE_SECONDS = 5400;
/**
* 获取access_token,先从redis中查,查到返回,
* 未查到去微信获取存到redis中,有效期1.5小时
* @return
*/
public static String getAccessToken(){
String token = RedisHelper.getRedisHelper().getEntity(WECHAT_ACCESS_TOKEN_REDIS_KEY);
if(!StringUtil.isEmpty(token)){
return token;
}
String response = HttpClient.httpClientGet(TOKENURL);
logger.info("获取accesstoken,url = " + TOKENURL);
logger.info("获取accesstoken response==" + response);
JSONObject rtnJson = JSONObject.parseObject(response);
token = rtnJson.getString("access_token");
RedisHelper.getRedisHelper().setExEntity(WECHAT_ACCESS_TOKEN_REDIS_KEY,WECHAT_ACCESS_TOKEN_EXPIRE_SECONDS,token);
return token;
}
}
RedisHelper 工具
package com.learn.wecath.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
/**
* @author lzj on 2017/2/21.
*/
public class RedisHelper {
private static final Logger logger = LoggerFactory.getLogger(RedisHelper.class);
private static ExtRedisPool redisHelper = null;
private static ExtRedisPool tokenRedisHelper = null;
/**
* 失效监听数据库
*/
private static ExtRedisPool expireListenerRedisHelper = null;
public static void init(String path) {
try {
redisHelper = new ExtRedisPool(path);
int tokenDbIndex = Integer.parseInt(redisHelper.getInitProperties().getProperty("redis.tokenDbIndex", "0"));
int expireListenerDbIndex = Integer.parseInt(redisHelper.getInitProperties().getProperty("redis.expireListenerDbIndex", "0"));
if (tokenDbIndex > 0) {
tokenRedisHelper = new ExtRedisPool(path);
tokenRedisHelper.setDbIndex(tokenDbIndex);
}
if (expireListenerDbIndex > 0) {
expireListenerRedisHelper = new ExtRedisPool(path);
expireListenerRedisHelper.setDbIndex(expireListenerDbIndex);
}
logger.info("RedisHelper redis pool 初始化OK");
} catch (Exception ex) {
logger.error("RedisHelper redis pool 初始化 error ", ex);
}
}
public static void init(InputStream path) {
try {
path.mark(0);
redisHelper = new ExtRedisPool(path);
int tokenDbIndex = Integer.parseInt(redisHelper.getInitProperties().getProperty("redis.tokenDbIndex", "0"));
int expireListenerDbIndex = Integer.parseInt(redisHelper.getInitProperties().getProperty("redis.expireListenerDbIndex", "0"));
if (tokenDbIndex > 0) {
tokenRedisHelper = new ExtRedisPool(path);
tokenRedisHelper.setDbIndex(tokenDbIndex);
}
if (expireListenerDbIndex > 0) {
path.reset();
expireListenerRedisHelper = new ExtRedisPool(path);
expireListenerRedisHelper.setDbIndex(expireListenerDbIndex);
}
logger.info("RedisHelper redis pool 初始化OK");
} catch (Exception ex) {
logger.error("RedisHelper redis pool 初始化 error ", ex);
}
}
/**
* 根据用户设备iid返回 数据库
*
* @return DAOOperator
*/
public static ExtRedisPool getRedisHelper() {
return redisHelper;
}
public static ExtRedisPool getTokenRedisHelper() {
return tokenRedisHelper;
}
public static ExtRedisPool getExpireListenerRedisHelper() {
return expireListenerRedisHelper;
}
public static void registeRedisErrorHandler(RedisErrorHandler redisErrorHandler) {
if (null != redisHelper) {
redisHelper.setRedisErrorHandler(redisErrorHandler);
}
}
}
http请求工具类:
package car.repair.common.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
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.util.EntityUtils;
import java.io.IOException;
/**
* HttpClient工具类
*/
@Slf4j
public class HttpClientUtils {
/**
* 发起https请求并获取结果
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return String
*/
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
StringBuffer buffer = new StringBuffer();
String result = "";
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new CxX509TrustManager()};
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
result = buffer.toString();
} catch (ConnectException ce) {
logger.error("Weixin server connection timed out.", ce);
} catch (Exception e) {
logger.error("Whttps request error:{}", e);
}
return result;
}
/**
* 以jsonString形式发送HttpPost的Json请求,String形式返回响应结果
*
* @param url
* @param jsonString
* @return
*/
public static String sendPostJsonStr(String url, String jsonString) throws IOException {
if (jsonString == null || jsonString.isEmpty()) {
return sendPost(url);
}
String resp = "";
StringEntity entityStr = new StringEntity(jsonString,
ContentType.create("text/plain", "UTF-8"));
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(entityStr);
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
resp = EntityUtils.toString(entity, "UTF-8");
EntityUtils.consume(entity);
} catch (ClientProtocolException e) {
log.error(e.getMessage());
} catch (IOException e) {
log.error(e.getMessage());
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
if (resp == null || resp.equals("")) {
return "";
}
return resp;
}
/**
* 发送不带参数的HttpPost请求
*
* @param url
* @return
*/
public static String sendPost(String url) throws IOException {
// 1.获得一个httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
// 2.生成一个post请求
HttpPost httppost = new HttpPost(url);
CloseableHttpResponse response = null;
try {
// 3.执行get请求并返回结果
response = httpclient.execute(httppost);
} catch (IOException e) {
log.error(e.getMessage());
}
// 4.处理结果,这里将结果返回为字符串
HttpEntity entity = response.getEntity();
String result = null;
try {
result = EntityUtils.toString(entity);
} catch (ParseException | IOException e) {
log.error(e.getMessage());
}
return result;
}
}
收到消息,我就不自己弄图了。这里附上官方图片一张: