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、访问接口进行测试,公众号收到信息