模板根据需求设置(注意:参数长度不能超出微信规定,否则将发送失败)
依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.54version>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>4.5.6version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpclientartifactId>
<version>4.5version>
dependency>
<dependency>
<groupId>org.apache.httpcomponentsgroupId>
<artifactId>httpcoreartifactId>
<version>4.4.1version>
dependency>
<dependency>
<groupId>commons-httpclientgroupId>
<artifactId>commons-httpclientartifactId>
<version>3.1version>
dependency>
请求地址、appid、密钥等信息;信息配置到application配置中,方便统一管理;
package com.andon.wxPush.conf;
import lombok.Data;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Value;
/**
* @Description: 读取 Properties配置信息的类
*/
@Service("wxConfiguration")
@Data
public class WxConfiguration {
//获取微信access_token请求地址
@Value("${wxConfig.url.WX_ACCESS_TOKEN_URL}")
private String wxAccessTokenUrl;
//发送微信公众号模板消息请求地址
@Value("${wxConfig.url.SEND_MESSAGE_TEMPLATE_URL}")
private String sendMessageTemplateUrl;
//小程序唯一凭证,即 AppID
@Value("${wxConfig.appid}")
private String appId;
//小程序唯一凭证密钥,即 AppSecret
@Value("${wxConfig.secret}")
private String secret;
//模板id
@Value("${wxConfig.templateId}")
private String templateId;
}
发送请求工具类
package com.andon.wxPush.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.andon.wxPush.template.HttpResult;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
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.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Map;
import static org.apache.tomcat.util.file.ConfigFileLoader.getURI;
@Component
@Slf4j
public class HttpUtils {
public HttpResult stringPostJson(String path, String content) throws Exception{
return stringPost(path, null, content, "utf-8", "utf-8", "application/json");
}
public HttpResult stringPost(String path, Map<String,String> headerMap, String content, String contentencode, String encode, String contentType) throws Exception{
StringEntity entity = new StringEntity(content, contentencode);
entity.setContentType(contentType);
return post(path, headerMap, entity, encode);
}
private HttpResult post(String path, Map<String,String> headerMap, HttpEntity entity, String encode){
HttpResult httpResult = new HttpResult();
CloseableHttpClient httpClient = null;
CloseableHttpResponse response = null;
try{
HttpPost httpPost = new HttpPost(getURI(path));
LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
httpClient = HttpClientBuilder.create().setRedirectStrategy(redirectStrategy).build();
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(120000)
.setConnectTimeout(120000)
.setConnectionRequestTimeout(120000)
.setCircularRedirectsAllowed(true)
.setRedirectsEnabled(true)
.setMaxRedirects(5)
.build();
httpPost.setConfig(requestConfig);
// httpPost.setHeader("User-Agent", header);
if(headerMap != null && headerMap.size() > 0){
for(String name:headerMap.keySet()) {
httpPost.addHeader(name, headerMap.get(name));
}
}
httpPost.setEntity(entity);
response = httpClient.execute(httpPost);
httpResult.setStatus(response.getStatusLine().getStatusCode());
if(httpResult.getStatus() == 200){
HttpEntity resEntity = response.getEntity();
httpResult.setBody(EntityUtils.toString(resEntity, encode));
}
}catch(Exception ex){
log.error("post请求出错", ex);
}finally{
try{
if(response != null){
response.close();
}
if(httpClient != null){
httpClient.close();
}
}catch(Exception ex) {
log.error("post请求关闭资源出错", ex);
}
}
return httpResult;
}
public JSONObject stringGetJson(String url) {
JSONObject jsonObject = null;
//通过HttpClients.createDefault()获取到CloseableHttpClient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
//创建HttpGet 对象,参数为请求的地址
HttpGet method = new HttpGet(url);
try {
//执行方法请求接口得到响应
HttpResponse response = httpclient.execute(method);
//解析响应数据
String content = EntityUtils.toString(response.getEntity(), "UTF-8");
//转为json格式
jsonObject = JSON.parseObject(content);
EntityUtils.consume(response.getEntity());//完全消耗
} catch (Exception e) {
log.error("get请求出错", e);
e.printStackTrace();
} finally {
try {
//释放连接
method.releaseConnection();
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return jsonObject;
}
}
json处理工具类
package com.andon.wxPush.utils;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.text.SimpleDateFormat;
@Slf4j
public class JsonUtils {
private static ObjectMapper json;
static {
json = new ObjectMapper();
json.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
json.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/**
* 序列化为JSON字符串
*/
public static String ObjectToString(Object object) {
try {
return (json.writeValueAsString(object));
} catch (Exception e) {
log.error("序列化为JSON字符串出错",e);
}
return null;
}
public static <T> T getObject(String jsonString, Class<T> clazz) {
if (StringUtils.isEmpty(jsonString))
return null;
try {
return json.readValue(jsonString, clazz);
} catch (Exception e) {
log.error("将JSON字符串转化为Map出错",e);
return null;
}
}
}
消息模板
package com.andon.wxPush.template;
import lombok.Data;
import java.util.Map;
import java.util.TreeMap;
@Data
public class WxSendMessageTemplate {
/**
* 接收者openId
*/
private String touser;
/**
* 模板ID
*/
private String template_id;
/**
* 模板跳转链接
*/
private String url;
/**
* 模板跳转链接
*/
private Map<String,String> miniprogram;
/**
* 消息data
*/
private TreeMap<String, TreeMap<String, String>> data;
/**
* 参数
*
* @param value 值
* @param color 颜色
* @return params
*/
public static TreeMap<String, String> item(String value, String color) {
TreeMap<String, String> params = new TreeMap<String, String>();
params.put("value", value);
params.put("color", color);
return params;
}
}
结果模板
package com.andon.wxPush.template;
import lombok.Data;
@Data
public class HttpResult {
private Integer status = 601;
private String body;
}
消息推送
package com.andon.wxPush.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.andon.util.DateUtil;
import com.andon.wxPush.conf.WxConfiguration;
import com.andon.wxPush.service.WxSendMsgService;
import com.andon.wxPush.template.HttpResult;
import com.andon.wxPush.template.WxSendMessageTemplate;
import com.andon.wxPush.utils.HttpUtils;
import com.andon.wxPush.utils.JsonUtils;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;
@Service
@Slf4j
public class WxSendMsgServiceImpl implements WxSendMsgService {
//凭证有效时间
private static final String KEY_EXPIRES_IN = "expiresIn";
//
private static final String KEY_TOKEN = "accessToken";
//拼接缓存前缀
private static final String ACCESS_TOKEN_KEY_PREFIX = "AccessToken_";
@Resource
private WxConfiguration wxConfiguration;
@Resource
private HttpUtils httpUtils;
/**
* accessToken缓存
*/
private static Map<String, Map<String, Object>> accessTokenMap = Maps.newConcurrentMap();
/**
* 推送模板
* @param templateMsg
* @return
*/
@Override
public JSONObject noticeTemplate(JSONObject templateMsg) {
// 模版ID
String templateId = wxConfiguration.getTemplateId();
TreeMap<String, TreeMap<String, String>> params = new TreeMap<>();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//微信公众号根据具体模板参数组装
params.put("keyword4", WxSendMessageTemplate.item(sdf.format(new Date()), "#000000")); //告警时间
params.put("keyword3", WxSendMessageTemplate.item(templateMsg.getString("andonName"), "#000000")); //报警类型
params.put("keyword1", WxSendMessageTemplate.item(templateMsg.getString("equipmentName"), "#000000")); //设备名称
params.put("keyword2", WxSendMessageTemplate.item(templateMsg.getString("installPlace"), "#000000")); //设备位置
params.put("first", WxSendMessageTemplate.item(templateMsg.getString("andonContent"), "#000000")); //告警内容
WxSendMessageTemplate wxTemplateMsg = new WxSendMessageTemplate();
// 模版ID
wxTemplateMsg.setTemplate_id(templateId);
// openId
wxTemplateMsg.setTouser(templateMsg.getString("openId"));
// 关键字赋值
wxTemplateMsg.setData(params);
String data = JsonUtils.ObjectToString(wxTemplateMsg);
System.out.println(data);
return this.handleSendMsgLog(data);
}
//捕获异常
private JSONObject handleSendMsgLog(String data) {
JSONObject resultDto = new JSONObject();
try {
resultDto = this.sendTemplateMsg(data);
System.out.println("handleSendMsgLog:" + resultDto.toJSONString());
} catch (Exception exception) {
log.error("发送微信模版失败", exception);
}
// TODO 可以记录一下发送记录的日志
return resultDto;
}
//发送模板
public JSONObject sendTemplateMsg(String data) throws Exception {
// 获取token
JSONObject accessTokenRes = this.getAccessToken();
String accessToken = accessTokenRes.getString("access_token");
// 发送消息
HttpResult httpResult = null;
try {
httpResult = httpUtils.stringPostJson(wxConfiguration.getSendMessageTemplateUrl() + accessToken, data);
} catch (Exception e) {
e.printStackTrace();
}
return JSON.parseObject(httpResult.getBody());
}
/**
* 获取全局access_token
*/
public JSONObject getAccessToken() {
//拼接获取缓存key
String key = ACCESS_TOKEN_KEY_PREFIX.concat(wxConfiguration.getAppId()).concat(wxConfiguration.getSecret());
Map<String, Object> accessTokenJson = accessTokenMap.get(key);
if (accessTokenJson != null) {
long expiresIn = (Long) accessTokenJson.get(KEY_EXPIRES_IN);
if (System.currentTimeMillis() < expiresIn) {
return (JSONObject) accessTokenJson.get(KEY_TOKEN);
}
accessTokenMap.remove(key);
}
synchronized (this) {
accessTokenJson = accessTokenMap.get(key);
// 多线程环境下,其他线程可能已经获得最新corpAccessToken,直接返回
if (accessTokenJson != null) {
return (JSONObject) accessTokenJson.get(KEY_TOKEN);
}
String url = String.format(wxConfiguration.getWxAccessTokenUrl(), wxConfiguration.getAppId(), wxConfiguration.getSecret());
JSONObject resJson = httpUtils.stringGetJson(url);
if (resJson != null && StringUtils.isNotEmpty(resJson.getString("access_token"))) {
accessTokenJson = this.seAccessToken(resJson);
}
if (accessTokenJson == null) {
try {
throw new Exception();
} catch (Exception e) {
log.error("设置access_token缓存失败");
e.printStackTrace();
}
}
//放入缓存中
accessTokenMap.put(key, accessTokenJson);
JSONObject accessTokenJsonRes = (JSONObject) accessTokenJson.get(KEY_TOKEN);
if (accessTokenJsonRes == null) {
try {
throw new Exception();
} catch (Exception e) {
log.error("获取access_token错误");
e.printStackTrace();
}
}
return accessTokenJsonRes;
}
}
//设置access_token
private Map<String, Object> seAccessToken(JSONObject jsonObject) {
Map<String, Object> token = Maps.newHashMap();
// 减去3分钟,以免过时
token.put(KEY_EXPIRES_IN, (jsonObject.getLongValue("expires_in") - 3 * 60) * 1000 + System.currentTimeMillis());
token.put(KEY_TOKEN, jsonObject);
return token;
}
}