SpringBoot 2.X 发送微信公众平台模板消息
1、微信模板消息官方文档
http://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=tmplmsg/faq_tmpl
2、申请微信公众平台接口测试账号
http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

关注测试号二维码

新增测试模板


3、配置和代码
①新建配置类WeChatConfig,和微信支付用了同一个文件
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Component
@Configuration
@PropertySource(value = "classpath:wxpay.properties")
public class WeChatConfig {
// 获取accessToken的接口
public static final String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
// 发送消息的接口
public static final String PUSH_MESSAGE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=%s";
@Value("${wechat.appid}")
private String appId;
@Value("${wechat.appsecret}")
private String appsecret;
@Value("${wechat.templateId}")
private String templateId;
@Value("${wechat.forwardUrl}")
private String forwardUrl;
// 发送消息的接口的访问凭证
private String accessToken;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppsecret() {
return appsecret;
}
public void setAppsecret(String appsecret) {
this.appsecret = appsecret;
}
public String getTemplateId() {
return templateId;
}
public void setTemplateId(String templateId) {
this.templateId = templateId;
}
public String getForwardUrl() {
return forwardUrl;
}
public void setForwardUrl(String forwardUrl) {
this.forwardUrl = forwardUrl;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
}
②配置wxpay.properties
#Wechat发送微信模板消息 wechat.appid=wxcc6efdba2803335c wechat.appsecret= #模板消息id wechat.templateId=Ef3UeXmHSU8FY9eiHHE7FY559vNZ2zs1gHKfv8EowEE #跳转地址 wechat.forwardUrl=http://www.baidu.com
③注入Http请求对象
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
④微信接口凭证返回实体类
import com.fasterxml.jackson.annotation.JsonProperty;
public class AccessToken {
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("expires_in")
private Long expiresIn;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public Long getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Long expiresIn) {
this.expiresIn = expiresIn;
}
}
⑤定时获取微信接口凭证的token
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
public class WeChatAccessTokenTask {
@Autowired
private RestTemplate restTemplate;
@Autowired
private WeChatConfig weChatConfig;
/**
* initialDelay: 初始化2s后执行第一次
* fixedDelay:微信默认Token过期时间为7200s,这里定时7100s执行一次定时任务
*/
@Scheduled(initialDelay = 2000, fixedDelay = 7100 * 1000)
public void refreshToken() {
// 请求方式: GET
// URL:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
AccessToken accessToken = restTemplate.getForObject(
String.format(WeChatConfig.GET_TOKEN_URL, weChatConfig.getAppId(), weChatConfig.getAppsecret()),
AccessToken.class);
// 将获取的accessToken注入wechatConf
weChatConfig.setAccessToken(accessToken.getAccessToken());
}
}
⑥发送模板消息的请求参数封装类
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
public class WeChatTemplate {
@JsonProperty("touser")
private String toUser;
@JsonProperty("template_id")
private String templateId;
private String url;
private Map<String,String> miniprogram;
private Map<String, Map<String, String>> data = new HashMap<>();
public static Map<String, String> initData(String value, String color) {
HashMap<String, String> data = new HashMap<String, String>();
data.put("value", value);
data.put("color", color);
return data;
}
public String getToUser() {
return toUser;
}
public void setToUser(String toUser) {
this.toUser = toUser;
}
public String getTemplateId() {
return templateId;
}
public void setTemplateId(String templateId) {
this.templateId = templateId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, String> getMiniprogram() {
return miniprogram;
}
public void setMiniprogram(Map<String, String> miniprogram) {
this.miniprogram = miniprogram;
}
public Map<String, Map<String, String>> getData() {
return data;
}
public void setData(Map<String, Map<String, String>> data) {
this.data = data;
}
}
⑦发送模板消息的返回参数封装类
public class WeiXinResponse {
private Integer errcode;
private String errmsg;
private Long msgid;
public Integer getErrcode() {
return errcode;
}
public void setErrcode(Integer errcode) {
this.errcode = errcode;
}
public String getErrmsg() {
return errmsg;
}
public void setErrmsg(String errmsg) {
this.errmsg = errmsg;
}
public Long getMsgid() {
return msgid;
}
public void setMsgid(Long msgid) {
this.msgid = msgid;
}
}
⑧发送微信模板消息接口
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Objects;
@RestController
@CrossOrigin
public class WeChatMessage {
@Autowired
private RestTemplate restTemplate;
@Autowired
private WeChatConfig weChatConfig;
@RequestMapping("/sendWxMessage")
public Object sendMessage(@RequestBody String json) {
Map<String, Object> map = JSONObject.parseObject(json, Map.class);
WeChatTemplate weChatTemplate = new WeChatTemplate();
// 设置模板id
weChatTemplate.setTemplateId(weChatConfig.getTemplateId());
//获取openid
String openid = map.get("openid").toString();
// 设置接收用户openId
weChatTemplate.setToUser(openid);
//点击详情跳转的地址
weChatTemplate.setUrl(weChatConfig.getForwardUrl());
//设置模板dada参数
LocalDate date = LocalDate.now();
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
weChatTemplate.getData().put("date", WeChatTemplate.initData(date.getMonthValue()+"月"+date.getDayOfMonth()+"日"+"\n",""));
weChatTemplate.getData().put("topic", WeChatTemplate.initData("店长您好,有一笔新的让利单,需要您的确认。"+"\n", "#0000EE"));
weChatTemplate.getData().put("time", WeChatTemplate.initData(time, ""));
//调用微信接口,发送模板消息
WeiXinResponse result = restTemplate.postForObject(String.format(WeChatConfig.PUSH_MESSAGE_URL, weChatConfig.getAccessToken()),
weChatTemplate, WeiXinResponse.class);
if (Objects.nonNull(result)){
return ResultJson.ok();
}
return ResultJson.failure(ResultCode.SEND_MESSAGE_FAIL);
}
}
⑨后端返回统一格式
import lombok.Data;
import java.io.Serializable;
@Data
public class ResultJson<T> implements Serializable{
private static final long serialVersionUID = 783015033603078674L;
private int code;
private String msg;
private T data;
public static ResultJson ok() {
return ok("");
}
public static ResultJson ok(Object o) {
return new ResultJson(ResultCode.SUCCESS, o);
}
public static ResultJson failure(ResultCode code) {
return failure(code, "");
}
public static ResultJson failure(ResultCode code, Object o) {
return new ResultJson(code, o);
}
public ResultJson (ResultCode resultCode) {
setResultCode(resultCode);
}
public ResultJson (ResultCode resultCode,T data) {
setResultCode(resultCode);
this.data = data;
}
public void setResultCode(ResultCode resultCode) {
this.code = resultCode.getCode();
this.msg = resultCode.getMsg();
}
@Override
public String toString() {
return "{" +
"\"code\":" + code +
", \"msg\":\"" + msg + '\"' +
", \"data\":\"" + data + '\"'+
'}';
}
}
⑩返回状态码
public enum ResultCode {
/*
请求返回状态码和说明信息
*/
SUCCESS(200, "成功"),
SEND_MESSAGE_FAIL(401, "消息发送失败"),
NOT_FOUND(404, "请求的资源不存在"),
OPERATE_ERROR(405, "操作失败,请求操作的资源不存在"),
TIME_OUT(408, "请求超时"),
SERVER_ERROR(500, "服务器内部错误"),
;
private int code;
private String msg;
ResultCode(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
4、访问接口进行测试,公众号收到信息


浙公网安备 33010602011771号