- 1. 公众号申请和配置
- 首先!!记得设置白名单
- 2. 依赖
- 2.1 父子工程聚合
- 2.2 独立的单独工程
- 3. 工具类
- 3.1 HttpUtil http请求工具类
- 3.2 RestTemplateConfig 配置类
- 3.3 WeChatTemplate 短信模板实体类
- 3.4 WxConstant 微信相关参数封装类
- 3.5 WxGetTokenUtil 生成 access_token
- 3.7 WxMpConfiguration 微信server的配置类,不写这个server就没法用
- 3.8 WxResult 微信返回错误码封装类
- 3.9 WxSendUtill 发送消息工具类
- 4. 调用(controller)
2.2 独立的单独工程3.0.0 com.github.binarywang weixin-java-mp ${weixin-java-mp.version} com.alibaba fastjson ${fastjson.version} com.github.binarywang weixin-java-mp com.alibaba fastjson
3. 工具类com.github.binarywang weixin-java-mp 3.0.0 com.alibaba fastjson 1.2.47
目录结构:
- util.wx文件夹
- HttpUtil.java 类
- RestTemplateConfig.java 类
- WeChatTemplate 类
- WxConstant.java 类
- WxGetTokenUtil.java 类
- WxResult.java 类
- WxSendUtil.java 类
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
@Slf4j
public class HttpUtil {
protected static final String POST_METHOD = "POST";
private static final String GET_METHOD = "GET";
static {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
log.debug("ClientTrusted");
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
log.debug("ServerTrusted");
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}};
HostnameVerifier doNotVerify = (s, sslSession) -> true;
try {
SSLContext sc = SSLContext.getInstance("SSL", "SunJSSE");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(doNotVerify);
} catch (Exception e) {
log.error("Initialization https impl occur exception : {}", e);
}
}
private static String HttpDefaultExecute(String url, String method, Map map, String data) {
String result = "";
try {
url = setParmas(url, map, null);
result = defaultConnection(url, method, data);
} catch (Exception e) {
log.error("出错参数 {}", map);
}
return result;
}
public static String httpGet(String url, Map map) {
return HttpDefaultExecute(url, GET_METHOD, map, null);
}
public static String httpPost(String url, Map map, String data) {
return HttpDefaultExecute(url, POST_METHOD, map, data);
}
private static String HttpsDefaultExecute(String url, String method, Map map, String data) {
try {
url = setParmas(url, map, null);
log.info(data);
return defaultConnection(url, method, data);
} catch (Exception e) {
log.error("出错参数 {}", map);
}
return "";
}
public static String doGet(String url, Map map) {
return HttpsDefaultExecute(url, GET_METHOD, map, null);
}
public static String doPost(String url, Map map, String data) {
return HttpsDefaultExecute(url, POST_METHOD, map, data);
}
private static String defaultConnection(String path, String method, String data) throws Exception {
if (StringUtils.isBlank(path)) {
throw new IOException("url can not be null");
}
String result = null;
URL url = new URL(path);
HttpURLConnection conn = getConnection(url, method);
if (StringUtils.isNotEmpty(data)) {
OutputStream output = conn.getOutputStream();
output.write(data.getBytes(StandardCharsets.UTF_8));
output.flush();
output.close();
}
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream input = conn.getInputStream();
result = IOUtils.toString(input, StandardCharsets.UTF_8);
input.close();
conn.disconnect();
}
return result;
}
//待改进
protected static HttpURLConnection getConnection(URL url, String method) throws IOException {
HttpURLConnection conn;
if (StringUtils.equals("https", url.getProtocol())) {
conn = (HttpsURLConnection) url.openConnection();
} else {
conn = (HttpURLConnection) url.openConnection();
}
if (conn == null) {
throw new IOException("connection can not be null");
}
conn.setRequestProperty("Pragma", "no-cache");// 设置不适用缓存
conn.setRequestProperty("Cache-Control", "no-cache");
conn.setRequestProperty("Connection", "Close");// 不支持Keep-Alive
conn.setUseCaches(false);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setInstanceFollowRedirects(true);
conn.setRequestMethod(method);
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
return conn;
}
//待改进
protected static HttpURLConnection getConnection(URL url, boolean isFile) throws IOException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn == null) {
throw new IOException("connection can not be null");
}
//设置从httpUrlConnection读入
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
//如果是上传文件,则设为POST
if (isFile) {
conn.setRequestMethod(POST_METHOD); //GET和 POST都可以 文件略大改成POST
}
// 设置请求头信息
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Charset", String.valueOf(StandardCharsets.UTF_8));
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
return conn;
}
public static String setParmas(String url, Map map, String charset) throws Exception {
String result = StringUtils.EMPTY;
boolean hasParams = false;
if (StringUtils.isNotEmpty(url) && map != null && !map.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey().trim();
String value = entry.getValue().trim();
if (hasParams) {
builder.append("&");
} else {
hasParams = true;
}
if (StringUtils.isNotEmpty(charset)) {
builder.append(key).append("=").append(URLEncoder.encode(value, charset));
} else {
builder.append(key).append("=").append(value);
}
}
result = builder.toString();
}
URL u = new URL(url);
if (StringUtils.isEmpty(u.getQuery())) {
if (url.endsWith("?")) {
url += result;
} else {
url = url + "?" + result;
}
} else {
if (url.endsWith("&")) {
url += result;
} else {
url = url + "&" + result;
}
}
log.debug("request url is {}", url);
return url;
}
}
3.2 RestTemplateConfig 配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
return new RestTemplate(factory);
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setConnectTimeout(15*1000);
factory.setReadTimeout(60*60*1000);
return factory;
}
}
3.3 WeChatTemplate 短信模板实体类
import lombok.Data;
import java.util.TreeMap;
@Data
public class WeChatTemplate {
private String touser; //接收者openid
private String template_id; //模板ID
private String url; //URL置空,则在发送后,点击模板消息会进入一个空白页面(ios),或无法点击(android)
private TreeMap> data; //data数据
public static TreeMap item(String value, String color) {
TreeMap params = new TreeMap();
params.put("value", value);
params.put("color", color);
return params;
}
@Override
public String toString() {
return "WechatTemplate" +
"{" +
"openId='" + touser + "'," +
"template_id='" + template_id + "'," +
"url='" + url + "'," +
"data=" + data +
'}';
}
}
3.4 WxConstant 微信相关参数封装类
public class WxConstant {
public static final String appId="xxxxxxxxxxxxxxxxx";
public static final String appSecret="xxxxxxxxxxxxxxxxxxxxxxxx";
public static final String templateId="xxxxxxxxxxxxxxxxxxxx";
}
3.5 WxGetTokenUtil 生成 access_token
import com.alibaba.fastjson.JSONObject;
import java.util.HashMap;
import java.util.Map;
public class WxGetTokenUtil {
public static String getAccessToken() {
String appSecret = WxConstant.appSecret;
String appId = WxConstant.appId;
String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
Map params = new HashMap<>();
params.put("appid", appId);
params.put("secret", appSecret);
params.put("grant_type", "client_credential");
String response = HttpUtil.doGet(tokenUrl, params);
System.out.println("WxGetTokenUtil.getAccessToken()返回值:" + response);//此处可注释,调试时解开,防止code出报错找不到原因
JSONObject accessTokenObject = JSONObject.parseObject(response);
return accessTokenObject.getString("access_token");
}
}
3.7 WxMpConfiguration 微信server的配置类,不写这个server就没法用
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(WxMpService.class)
public class WxMpConfiguration {
@Bean
@ConditionalOnMissingBean
public WxMpConfigStorage configStorage() {
WxMpInMemoryConfigStorage configStorage = new WxMpInMemoryConfigStorage();
configStorage.setAppId(WxConstant.appId);
configStorage.setSecret(WxConstant.appSecret);
// configStorage.setToken(WxConstant.token);
// configStorage.setAesKey(WxConstant.key);
return configStorage;
}
@Bean
@ConditionalOnMissingBean
public WxMpService wxMpService(WxMpConfigStorage configStorage) {
WxMpService wxMpService = new me.chanjar.weixin.mp.api.impl.WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(configStorage);
return wxMpService;
}
}
3.8 WxResult 微信返回错误码封装类
import lombok.Data;
@Data
public class WxResult {
private String errcode;
private String errmsg;
private String msgid;
@Override
public String toString() {
return "WxError{errcode='" + errcode + "',errmsg='" + errmsg + "',msgid='" + msgid + "'}";
}
}
3.9 WxSendUtill 发送消息工具类
注意,参数是根据你自己的模板进行组装的,我的模板有以下参数
{first,keyword1,keyword2,keyword3,keyword4,keyword5,remark}
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TreeMap;
@Slf4j
@Component //使用方式2,需要用到这个注解,并且需要用到WxMapConfiguration。
public class WxSendUtil {
public static WxResult send(String openId, String data, String title) {
String url = "https://www.baidu.com";
String templateUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send";
String time = parseDate3String(new Date());
String accessToken = WxGetTokenUtil.getAccessToken();
log.info("获取access_token,微信平台接口返回{}", accessToken);
TreeMap> params = new TreeMap>();
//根据具体模板参数的 添加数据
params.put("first", WeChatTemplate.item(title, "#000000"));//一般是标题
params.put("keyword1", WeChatTemplate.item("项目名称", "#ff0000"));//项目名称,字体颜色
params.put("keyword2", WeChatTemplate.item(time, "#000000"));
params.put("keyword3", WeChatTemplate.item(time, "#000000"));
params.put("keyword4", WeChatTemplate.item(time, "#000000"));
params.put("keyword5", WeChatTemplate.item(time, "#000000"));
params.put("remark", WeChatTemplate.item(data, "#000000"));
WeChatTemplate wechatTemplate = new WeChatTemplate();
wechatTemplate.setTemplate_id(WxConstant.templateId);//模板id
wechatTemplate.setTouser(openId);// 接收者openid
wechatTemplate.setUrl(url); // 模板跳转链接
wechatTemplate.setData(params);
JSONObject json = JSONObject.fromObject(wechatTemplate);//将java对象转换为json对象
String sendData = json.toString();//将json对象转换为字符串
log.info("板参数组装{}", sendData);
TreeMap treeMap = new TreeMap();
treeMap.put("access_token", accessToken);
String retInfo = HttpUtil.doPost(templateUrl, treeMap, sendData);
if (!StringUtils.isBlank(retInfo)) {
return JSON.parseObject(retInfo, WxResult.class);
}
log.info("消息模板返回{}", retInfo);
return null;
}
@Autowired
private WxMpService wxService;
public Boolean sendWxMsg(String openId, String url, String title) {
String time = parseDate3String(new Date());
// 发送模板消息接口
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
// 接收者openid
.toUser(openId)
// 模板id
.templateId(WxConstant.templateId)
// 模板跳转链接
.url(url)
.build();
// 添加模板数据
templateMessage.addData(new WxMpTemplateData("first", title, "#FF00FF"))
.addData(new WxMpTemplateData("keyword1", "紧急", "#A9A9A9"))
.addData(new WxMpTemplateData("keyword2", time, "#A9A9A9"))
.addData(new WxMpTemplateData("remark", url, "#FF00FF"));
String msgId = null;
try {
// 发送模板消息
msgId = this.wxService.getTemplateMsgService().sendTemplateMsg(templateMessage);
} catch (WxErrorException e) {
e.printStackTrace();
}
log.warn("·==++--·推送微信模板信息:{}·--++==·", msgId != null ? "成功" : "失败");
return msgId != null;
}
//时间转化成字符串,格式 yyyy-MM-dd HH:mm:ss
private static String parseDate3String(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
}
4. 调用(controller)
发消息得有发送对象—用户,,要获取到用户的openId,然后才能进行发送
import com.alibaba.fastjson.JSONObject;
import com.sq.statistics.utils.wx.WxGetTokenUtil;
import com.sq.statistics.utils.wx.WxSendUtil;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/wx")
public class WXController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/open-id/list")
public Map getOpenIdList(@RequestParam(required = false) String next_openid) {
String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token={access_token}";
Map param = new HashMap<>();
param.put("next_openid", next_openid);
param.put("access_token", WxGetTokenUtil.getAccessToken());
HttpHeaders headers = new HttpHeaders();
ResponseEntity entity =
restTemplate.exchange(
url,
HttpMethod.GET,
new HttpEntity(headers),
String.class,
param);
String result = entity.getBody();
System.out.println("WXController.getOpenIdList()返回值:" + result);//此处可注释,调试时解开,防止code出报错
if (result != null && result.contains("count")) {//返回值可能有两种,一种是报错的,一种是正常返回
Map resultData = JSONObject.parseObject(result, Map.class);
// return resultData;//可以直接返回,也可以自己抽取,,我只需要openId和next_openId
String next_openId = resultData.get("next_openid").toString(); //这个是下一组数据需要的传参
Map data = JSONObject.parseObject(resultData.get("data").toString(), Map.class);
List openidList = JSONObject.parseArray(data.get("openid").toString(), String.class);
Map map = new HashMap<>();
map.put("next_openId", next_openId);
map.put("openidList", openidList);
return map;
}
return null;//返回的是tokenList,如果想要下一组数据,最后传的
}
@PostMapping("/send/message")
public void sendMessage(String message, String title, String openId) {
WxSendUtil.send(openId, message, title);
}
}



