获取公众号或小程序 AccessToken 地址 get请求
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET
推送公众号模板消息接口地址 POST请求
https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=AccessToken
注意:
-
如果根据小程序 openid 推送, 公众号与小程序必须要进行关联
-
发送时需要缓存 AccessToken , 因为每天2000次, 推送消息时必须是拿到的小程序的AccessToken
-
"touser":"oEqaV4uFSCSWzVeBBod24KwwbsTE" 可以是小程序或者公众号的 openid
公众号下发统一消息 数据格式
{
"touser":"oEqaV4uFSCSWzVeBBod24KwwbsTE",
"mp_template_msg":{
"appid":"wxa8955d5d443d3e9f",
"template_id":"ucOg12u77dYhFLSJr0uuhSkvUDZJROrNDiEE-Y4lnYM",
"url":"http://weixin.qq.com",
"miniprogram":{
"appid":"wx66622d29b6252da8",
"pagepath":"pages/parkingRecordId/parkingRecordId?scene=123"
},
"data":{
"first":{
"value":"恭喜你购买成功!",
"color":"#173177"
},
"keyword1":{
"value":"巧克力",
"color":"#173177"
},
"keyword2":{
"value":"39.8元",
"color":"#173177"
},
"remark":{
"value":"欢迎再次购买!",
"color":"#173177"
}
}
}
}
微信封装的发送方式-->基于公众号openid发送
String openid = "o20RG6sb4bBAlndFStIUiuy1WzeE";
WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
.toUser(openid)//要推送的用户openid
.templateId("1zQSJMyL6hACI5FCElehHE9IZgR3PO82saTBeFBUEFg")//模板id
.url("http://ggkt2.vipgz1.91tunnel.com/#/pay/"+5)//点击模板消息要访问的网址
.build();
//3,如果是正式版发送消息,,这里需要配置你的信息
templateMessage.addData(new WxMpTemplateData("first", "尊敬的用户,您的爱车已驶入无人收费停车场。该车场不支持电子发票、预约车位。", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword1", "渝D***36", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword2", "高科****业园", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword3", "2023-03-31 08:33:16", "#272727"));
templateMessage.addData(new WxMpTemplateData("keyword4", "你有2~5元停车立减券可换,本次停车可直接抵扣!点击查看>>>>", "#f1515c"));
templateMessage.addData(new WxMpTemplateData("remark", "感谢你使用公众号,如有疑问,随时咨询!", "#272727"));
System.err.println(templateMessage.toJson());
//发起推送
try {
String msg = wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
System.out.println("推送成功:" + msg);
} catch (Exception e) {
System.out.println("推送失败:" + e.getMessage());
e.printStackTrace();
}
自己封装的统一下发-->公众号小程序openid都可以发送-->推荐使用
ArrayList<GZHVo.Keyword> keywords = new ArrayList();
GZHVo.First first = new GZHVo.First("尊敬的用户,您的爱车已驶入无人收费停车场。", "#272727");
GZHVo.Keyword keyword1 = new GZHVo.Keyword(StringUtils.isEmpty(carPlate) ? "无" : carPlate.replace(carPlate.substring(2, 5), "****"), "#272727");//车牌号
GZHVo.Keyword keyword2 = new GZHVo.Keyword(StringUtils.isEmpty(carName) ? "无" : WxUtil.nameMask(carName), "#272727");//停车场
GZHVo.Keyword keyword3 = new GZHVo.Keyword(StringUtils.isEmpty(arriveTime) ? "无" : DateUtils.toyyyy_MM_dd_HHmmss(arriveTime), "#272727");//入场时间
GZHVo.Remark remark = new GZHVo.Remark("感谢你使用公众号,如有疑问,随时咨询!", "#f1515c");//备注
keywords.add(keyword1);
keywords.add(keyword2);
keywords.add(keyword3);
GZHVo.data data = new GZHVo.data(first, remark, keywords);
GZHVo.mp_template_msg mp_template_msg = new mp_template_msg()
.setAppid(WeChatConfig.WX_APPID)
.setTemplate_id(weChatConfig.getGo_templateId())
.setUrl("")
.setData(data)
.setMiniprogram(new Miniprogram(carowner.getAppid(), "pages/parkingRecordId/?scene=" + parkingRecord.getId()));
GZHJsonRootBean gzhJsonRootBean = new GZHJsonRootBean()
.setTouser(carowner.getOpenid())
.setMp_template_msg(mp_template_msg);
//发起推送
try {
Map<String, Object> sendMessage_wx = WxUtil.sendMessage(gzhJsonRootBean, WxUtil.reidsCacheAccessToken(redisTemplate));
msgGCH = true;
log.info("入场公众号消息内容: {}", gzhJsonRootBean.toString());
System.out.println("推送入场公众号消息 成功:" + sendMessage_wx);
} catch (Exception e) {
System.err.println("推送入场公众号消息 失败:" + e.getMessage());
e.printStackTrace();
}
}
推送公众号模板消息-->基于公众号openid发送
GZHVo.First first = new GZHVo.First("你在虚拟仿真系统有一笔消息待处理", "");
ArrayList<GZHVo.Keyword> keywords = new ArrayList();
GZHVo.Keyword keyword1 = new GZHVo.Keyword("测试名称", "#173177");
GZHVo.Keyword keyword2 = new GZHVo.Keyword("考试评价", "#173177");
GZHVo.Keyword keyword3 = new GZHVo.Keyword(LocalDateTime.now().toString(), "#173177");
GZHVo.Keyword keyword4 = new GZHVo.Keyword("考试成绩评分", "#173177");
keywords.add(keyword1);
keywords.add(keyword2);
keywords.add(keyword3);
keywords.add(keyword4);
GZHVo.Remark remark = new GZHVo.Remark("请及时前往处理", "");
GZHVo.data data = new GZHVo.data(first, remark, keywords);
JsonRootBean jsonRootBean = new JsonRootBean("o20RG6sb4bBAlndFStIUiuy1WzeE", "1zQSJMyL6hACI5FCElehHE9IZgR3PO82saTBeFBUEFg", "http://weixin.qq.com", new GZHVo.Miniprogram(), data);
String accessToken = WxUtil.getAccessToken(WeChatConfig.WX_APPID, WeChatConfig.WX_SECRET);
WxUtil.sendMessage2(accessToken,jsonRootBean);
}
参数封装代码
@Data
@Accessors(chain = true)
public class GZHVo {
//存储remark中的数据与颜色
@Data
@Accessors(chain = true)
public static class Remark {
private String value;
private String color;
public Remark(String value, String color) {
this.value = value;
this.color = color;
}
}
//Miniprogram //小程序相关数据,无小程序可以不填,或者填充空串
@Data
@Accessors(chain = true)
public static class Miniprogram {
private String appid;
private String pagepath;
public Miniprogram() { }
public Miniprogram(String appid, String pagepath) {
this.appid = appid;
this.pagepath = pagepath;
}
}
@Data
@Accessors(chain = true)
public static class mp_template_msg {
private String appid;
private String template_id;
private String url;
private GZHVo.data data;
/**
* 跳小程序所需数据,不需跳小程序可不用传该数据
*/
private GZHVo.Miniprogram miniprogram;
public mp_template_msg() { }
}
//Keyword,中间keyword部分
@Data
@Accessors(chain = true)
public static class Keyword {
private String value;
private String color;
public Keyword(String value, String color) {
this.value = value;
this.color = color;
}
public Keyword() { }
}
//First 首个数据
@Data
@Accessors(chain = true)
public static class First {
private String value;
private String color;
public First(String value, String color) {
this.value = value;
this.color = color;
}
}
//data 包含first、keyword、remark
@Data
@Accessors(chain = true)
public static class data {
private First first;
private Keyword keyword1;
private Keyword keyword2;
private Keyword keyword3;
private Keyword keyword4;
private Keyword keyword5;
private Keyword keyword6;
private Remark remark;
public data(First first, Remark remark, ArrayList<Keyword> keyword) {
this.first = first;
int count = 1;
for (Keyword keyword1 : keyword) {
if (count == 1) {
this.keyword1 = keyword1;
} else if (count == 2) {
this.keyword2 = keyword1;
} else if (count == 3) {
this.keyword3 = keyword1;
} else if (count == 4) {
this.keyword4 = keyword1;
} else if (count == 5) {
this.keyword5 = keyword1;
} else if (count == 6) {
this.keyword6 = keyword1;
}
count++;
}
this.remark = remark;
}
public data() {
super();
}
}
public GZHVo() {
super();
}
}
/**
* 公众号推送消息封装-->整合实体类所有数据
*/
@Data
@Accessors(chain = true)
public class GZHJsonRootBean {
/**
* 接收者openid
*/
private String touser;
/**
* 模板id
*/
private GZHVo.mp_template_msg mp_template_msg;
public GZHJsonRootBean() {
}
public GZHJsonRootBean(String touser, GZHVo.mp_template_msg mp_template_msg) {
this.touser = touser;
this.mp_template_msg=mp_template_msg;
}
}
util工具类
/**
* 系统常量-->及工具类
*/
@Slf4j
public class WxUtil {
//入场推送
public static final String GZH_PUSH_MSG_GO = "GZH_PUSH_MSG_GO:";
//出场推送
public static final String GZH_PUSH_MSG_OUT = "GZH_PUSH_MSG_OUT:";
//微信小程序 token
public static final String XCX_ACCESS_TOKEN = "XCX_ACCESS_TOKEN:";
public static final String XCX_EXPIRES_IN = "XCX_EXPIRES_IN:";
/**
* 用户姓名的打码隐藏加星号加*
*
* @return 处理完成的姓名
*/
public static String nameMask(String name) {
String res = "";
if (name.length() == 2) {
res = name.replaceAll("^.", "*");
} else if (name.length() <= 3) {
res = name.replaceAll("(?<=.).(?=.)", "*");
} else if (name.length() > 3) {
res = name.replaceAll("(?<=.).(?=..)", "*");
}
return res;
}
/**
* redis缓存小程序的 AccessToken 过期的前10分钟刷新
*
* @return
*/
public static String reidsCacheAccessToken(RedisTemplate redisTemplate) {
//如果不等于空 或者小于600秒
if (redisTemplate.getExpire(WxUtil.XCX_EXPIRES_IN) < 600 || StringUtils.isEmpty(redisTemplate.hasKey(WxUtil.XCX_ACCESS_TOKEN))) {
JSONObject json = WxUtil.getAccessToken(WeChatConfig.XIAOCX_APPID, WeChatConfig.XIAOCX_APPSECRET);
redisTemplate.opsForValue().set(WxUtil.XCX_ACCESS_TOKEN, json.get("access_token"), 7200, TimeUnit.SECONDS);
redisTemplate.opsForValue().set(WxUtil.XCX_EXPIRES_IN, json.get("expires_in"), 7200, TimeUnit.SECONDS);
log.info("更新redis accessToken: {}", json.get("access_token"));
log.info("过期时间为 expiresIn: {}", json.get("expires_in"));
}
String accessToken = redisTemplate.opsForValue().get(WxUtil.XCX_ACCESS_TOKEN).toString();
Long expiresIn = redisTemplate.getExpire(WxUtil.XCX_EXPIRES_IN);
log.info("redis取出 accessToken: {}", accessToken);
log.info("剩余过期时间 expiresIn: {}", expiresIn);
return accessToken;
}
// /**
// * 发送消息工具类-->公众号发送
// *
// * @return
// */
// public static Map<String, Object> sendMessage2(String AccessToken, JsonRootBean2 jsonRootBean2) {
// String json = JSON.toJSONString(jsonRootBean2);
// String info = HttpUtil.post("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + AccessToken, json);
// Map<String, Object> map = (Map) JSON.parse(info);
System.out.println(AccessToken);
// if (map.get("errcode").equals(new Integer(0))) {
// log.info("0,消息推送成功,用户openid: {},消息内容: {}", jsonRootBean2.getTouser(), json);
// } else if (map.get("errcode").equals(new Integer(40003))) {
// log.error("40003,推送消息的openid错误,用户openid: {},消息内容: {}", jsonRootBean2.getTouser(), json);
// } else if (map.get("errcode").equals(new Integer(43004))) {
// log.error("43004,该用户未关注 或 已取消关注公众号,用户openid: {},消息内容: {}", jsonRootBean2.getTouser(), json);
// } //else {
try {
throw new WxErrorException(WxError.fromJson(info, WxType.MP));
} catch (WxErrorException e) {
e.printStackTrace();
}
}
System.out.println(map);//{"errcode":0,"errmsg":"ok","msgid":2873391715469262849}
// return map;
// }
/**
* 发送公众号模板消息工具类-->统一下发消息
*
* @return
*/
public static Map<String, Object> sendMessage(GZHJsonRootBean gzhJsonRootBean, String AccessToken) {
String json = JSON.toJSONString(gzhJsonRootBean);
String info = HttpUtil.post("https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token=" + AccessToken, json);
Map<String, Object> map = (Map) JSON.parse(info);
// System.out.println(AccessToken);
if (map.get("errcode").equals(new Integer(0))) {
log.info("0,消息推送成功,用户openid: {},消息内容: {}", gzhJsonRootBean.getTouser(), json);
} else if (map.get("errcode").equals(new Integer(40003))) {
log.error("40003,推送消息的openid错误,用户openid: {},消息内容: {}", gzhJsonRootBean.getTouser(), json);
} else if (map.get("errcode").equals(new Integer(43004))) {
log.error("43004,该用户未关注 或 已取消关注公众号,用户openid: {},消息内容: {}", gzhJsonRootBean.getTouser(), json);
} else {
log.info("用户openid: {},消息内容: {}", gzhJsonRootBean.getTouser(), json);
// try {
// throw new WxErrorException(WxError.fromJson(info, WxType.MP));
// } catch (WxErrorException e) {
// e.printStackTrace();
// }
}
// System.out.println(map);//{"errcode":0,"errmsg":"ok","msgid":2873391715469262849}
return map;
}
/**
* 获取access_token, 返回值
*
* @return
*/
public static String getAccessToken2(String appid, String secret) {
StringBuffer buffer = new StringBuffer();
buffer.append("https://api.weixin.qq.com/cgi-bin/token");
buffer.append("?grant_type=client_credential");
buffer.append("&appid=%s");
buffer.append("&secret=%s");
//请求地址设置参数
String url = String.format(buffer.toString(), appid, secret);
String tokenString = HttpUtil.get(url);
String access_token = "";
if (tokenString != null) {
//获取access_token
JSONObject jsonObject = JSONObject.parseObject(tokenString);
access_token = jsonObject.getString("access_token");
}
return access_token;
}
/**
* 获取access_token,返回 JSONObject
*
* @param AppId
* @param AppSecret
* @return
* @throws Exception
*/
public static JSONObject getAccessToken(String AppId, String AppSecret) {
String access_tokenurl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
access_tokenurl = access_tokenurl.replace("APPID", AppId);
access_tokenurl = access_tokenurl.replace("APPSECRET", AppSecret);
String tokenString = HttpUtil.get(access_tokenurl);
JSONObject jsonObjectToken = null;
String access_token = "";
if (tokenString != null) {
//获取access_token
jsonObjectToken = JSONObject.parseObject(tokenString);
// access_token = jsonObject.getString("access_token");
}
return jsonObjectToken;
}
/**
* 将微信的XML解析成 MAP 结构
*
* @param request
* @return
* @throws Exception
*/
private Map<String, String> parseXml(HttpServletRequest request) throws Exception {
Map<String, String> map = new HashMap<String, String>();
InputStream inputStream = request.getInputStream();
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList) {
map.put(e.getName(), e.getText());
}
inputStream.close();
inputStream = null;
return map;
}
}
配置类
/**
* 读取 yml 配置
*/
@Data
@Slf4j
@Component
@ConfigurationProperties(prefix = "wechat")
public class WeChatConfig implements InitializingBean {
private String gzh_appid;
private String gzh_secret;
private String xcx_appid;
private String xcx_appSecret;
private String go_templateId;
private String out_templateId;
private String arrears_templateId;
public static String WX_APPID;
public static String WX_SECRET;
public static String XIAOCX_APPID;
public static String XIAOCX_APPSECRET;
@Override
public void afterPropertiesSet() throws Exception {
WX_APPID = gzh_appid;
WX_SECRET = gzh_secret;
XIAOCX_APPID = xcx_appid;
XIAOCX_APPSECRET = xcx_appSecret;
log.info("我初始化了属性值,成功了");
}
/**
* 微信 bean 配置
*/
@Component
@Configuration
public static class WeChatMpConfig {
@Bean
public WxMpConfigStorage wxMpConfigStorage() {
WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl();
wxMpConfigStorage.setAppId(WeChatConfig.WX_APPID);
wxMpConfigStorage.setSecret(WeChatConfig.WX_SECRET);
return wxMpConfigStorage;
}
@Bean
public WxMpService wxMpService() {
WxMpService wxMpService = new WxMpServiceImpl();
wxMpService.setWxMpConfigStorage(wxMpConfigStorage());
return wxMpService;
}
}
}