微信公众号消息模板发送
程序员文章站
2023-12-28 17:32:34
...
微信公众号消息模板发送
微信公众号消息模板群发功能
开通申请模板消息功能
1 >> 在微信公众平台的后台,依次进入“功能->添加功能插件->模板消息”,即可申请模板消息(模板消息的申请需账号已经开通微信支付权限)
2 >> 点击申请
3 >> 申请时,选择2个和自己相关的行业即可
4 >> 提交并且申请通过后,可以在模板库中看到模板消息列表,选择一个匹配自己的模板消息点击详情添加(微信提供的模板消息里面基本已经涵盖的很全了,如果没有找到合适自己的也可以自己申请创建一个新的模板消息,我这里用的是一个现成的模板消息)
5 >> 申请完后就可以在自己的模板消息查看你要用的模板消息的id
获取模板消息发送所需参数
- 获取access_token
- 获取模板消息 template_id
- 获取标签 tagid 通过tagid 获取标签下用户的 openid
- 根据 申请审核的消息模板 创建 data
"data":{
"first": {
"value":"您好!课程测试消息!",
"color":"#173177"
},
"keyword1":{
"value":"课程001",
"color":"#173177"
},
"keyword2": {
"value":"张老师",
"color":"#173177"
},
"remark":{
"value":"望准时参加",
"color":"#173177"
}
};
AccessToken pojo类
package net.mingsoft.common.wx.pojo;
import java.util.Calendar;
public class AccessToken {
// 获取到的凭证
private String token;
// 凭证有效时间,单位:秒
private Calendar expiresIn;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Calendar getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(Calendar expiresIn) {
this.expiresIn = expiresIn;
}
}
TemplateData pojo类
package net.mingsoft.common.wx.pojo;
/**
* 模板数据
* @author CLiang
*/
public class TemplateData {
private String value;//模板显示值
private String color;//模板显示颜色
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
WxTagsEntity pojo类
package net.mingsoft.common.wx.pojo;
public class WxTagsEntity {
private String id; //标签id
private String name; //标签名称
private String count; //此标签下粉丝数
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCount() {
return count;
}
public void setCount(String count) {
this.count = count;
}
}
WxTemplate pojo类
package net.mingsoft.common.wx.pojo;
import java.util.Map;
/**
* 模板基类
* @author CLiang
*
*/
public class WxTemplate {
private String template_id;//模板ID
private Integer tagid;//模板ID
private Map<String,TemplateData> data;//模板里的数据
public String getTemplate_id() {
return template_id;
}
public void setTemplate_id(String template_id) {
this.template_id = template_id;
}
public Integer getTagid() {
return tagid;
}
public void setTagid(Integer tagid) {
this.tagid = tagid;
}
public Map<String, TemplateData> getData() {
return data;
}
public void setData(Map<String, TemplateData> data) {
this.data = data;
}
}
WxTemplateEntity pojo类
package net.mingsoft.common.wx.pojo;
public class WxTemplateEntity {
private String template_id; //模板ID
private String title; //模板标题
private String primary_industry; //模板所属行业的一级行业
private String deputy_industry; //模板所属行业的二级行业
private String content; //模板内容
private String example; //模板示例
public String getTemplate_id() {
return template_id;
}
public void setTemplate_id(String template_id) {
this.template_id = template_id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getPrimary_industry() {
return primary_industry;
}
public void setPrimary_industry(String primary_industry) {
this.primary_industry = primary_industry;
}
public String getDeputy_industry() {
return deputy_industry;
}
public void setDeputy_industry(String deputy_industry) {
this.deputy_industry = deputy_industry;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getExample() {
return example;
}
public void setExample(String example) {
this.example = example;
}
}
微信公众号消息模板工具类
package net.mingsoft.common.wx.wxUtils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.convert.WritingConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONException;
import net.mingsoft.common.wx.pojo.AccessToken;
import net.mingsoft.common.wx.pojo.JsApiTicket;
import net.mingsoft.common.wx.pojo.TemplateData;
import net.mingsoft.common.wx.pojo.WxTemplate;
/**
* 公众平台通用接口工具类
*
*/
@Component
public class WeixinUtil {
private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);
@Value("${weixin.AppId}")
private String APPID;
@Value("${weixin.AppSecret}")
private String AppSecret;
@Value("${weixin.domain}")
private String domain;
@Autowired
private RestTemplate rest;
//创建一个静态map缓存
private static Map<String,Object> weixinCache = new HashMap<String, Object>();
/**
* 发起https请求并获取结果
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf);
httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect();
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
log.error("Weixin server connection timed out.");
} catch (Exception e) {
log.error("https request error:{}", e);
}
return jsonObject;
}
// 获取access_token的接口地址(GET) 限200(次/天)
public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
/**
* 获取access_token
*
* @param appid 凭证
* @param appsecret **
* @return
*/
public static AccessToken getAccessToken(String appid, String appsecret) {
AccessToken accessToken = (AccessToken) weixinCache.get("accessToken");
String request_url = access_token_url.replace("APPID", appid).replace("APPSECRET", appsecret);
if(accessToken != null){
Calendar current = Calendar.getInstance();
Calendar ever = accessToken.getExpiresIn();
if(ever.before(current)){
JSONObject jsonObject = WeixinUtil.httpRequest(request_url, "GET", null);
weixinCache.remove("accessToken");
accessToken = new AccessToken();
long etime1=System.currentTimeMillis()+(jsonObject.getInteger("expires_in")*1000);//延时函数,单位毫秒,这里是延时了5分钟
Date expiresIn = new Date(etime1);
Calendar ever1 = Calendar.getInstance();
ever1.setTime(expiresIn);
accessToken.setExpiresIn(ever1);
accessToken.setToken(jsonObject.getString("access_token"));
weixinCache.put("accessToken", accessToken);
log.info("accessToken is invalid : --------------------------------"+accessToken.getToken(),WeixinUtil.class);
}
log.info("accessToken is available : --------------------------------"+accessToken.getToken(),WeixinUtil.class);
}else{
//同上面的else里的code
JSONObject jsonObject = WeixinUtil.httpRequest(request_url, "GET", null);
if(jsonObject.containsKey("errcode")&&jsonObject.getInteger("errcode")!=0) {
log.info("获取access_token 失败:"+jsonObject.toJSONString(),WeixinUtil.class);
return null;
}
weixinCache.remove("accessToken");
accessToken = new AccessToken();
accessToken.setToken(jsonObject.getString("access_token"));
long etime1=System.currentTimeMillis()+(jsonObject.getInteger("expires_in")*1000);//延时函数,单位毫秒,这里是延时了5分钟
Date expiresIn = new Date(etime1);
Calendar ever1 = Calendar.getInstance();
ever1.setTime(expiresIn);
accessToken.setExpiresIn(ever1);
weixinCache.put("accessToken", accessToken);
log.info("accessToken is null : --------------------------------"+accessToken.getToken(),WeixinUtil.class);
}
return accessToken;
}
public static JsApiTicket getJsApiTicket(String accessToken){
String ticket_str = null;
JsApiTicket jsApiTicket = (JsApiTicket) weixinCache.get("ticket");
if(jsApiTicket != null){
Calendar ever = jsApiTicket.getExpiresIn();
Calendar current = Calendar.getInstance();
if(ever.before(current)){
String request_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi".replace("ACCESS_TOKEN", accessToken);
JSONObject jsonObject = WeixinUtil.httpRequest(request_url, "GET", null);
ticket_str = jsonObject.getString("ticket");
weixinCache.remove("ticket");
jsApiTicket = new JsApiTicket();
Calendar ever1 = Calendar.getInstance();
long etime1=System.currentTimeMillis()+(jsonObject.getInteger("expires_in")*1000);//延时函数,单位毫秒,这里是延时了5分钟
Date date=new Date(etime1);
ever1.setTime(date);
jsApiTicket.setExpiresIn(ever1);
jsApiTicket.setTicket(ticket_str);
weixinCache.put("ticket", jsApiTicket);
}
}else{
//同上面的else里的code
String request_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi".replace("ACCESS_TOKEN", accessToken);
JSONObject jsonObject = WeixinUtil.httpRequest(request_url, "GET", null);
ticket_str = jsonObject.getString("ticket");
weixinCache.remove("ticket");
jsApiTicket = new JsApiTicket();
Calendar ever1 = Calendar.getInstance();
long etime1=System.currentTimeMillis()+(jsonObject.getInteger("expires_in")*1000);//延时函数,单位毫秒,这里是延时了5分钟
Date date=new Date(etime1);
ever1.setTime(date);
jsApiTicket.setExpiresIn(ever1);
jsApiTicket.setTicket(ticket_str);
weixinCache.put("ticket", jsApiTicket);
}
return jsApiTicket;
}
//微信分享签名算法;
public static Map<String, Object> sign(String jsapi_ticket, String url) {
Map<String, Object> ret = new HashMap<String, Object>();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + jsapi_ticket +
"&noncestr=" + nonce_str +
"×tamp=" + timestamp +
"&url=" + url;
System.out.println(string1);
try
{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8")); //对string1 字符串进行SHA-1加密处理
signature = byteToHex(crypt.digest()); //对加密后字符串转成16进制
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("jsapi_ticket", jsapi_ticket);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
return ret;
}
private static String byteToHex(final byte[] hash) {
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
//生成随机字符串
private static String create_nonce_str() {
return UUID.randomUUID().toString().replace("-", "");
}
//生成时间戳字符串
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
/**
* 获取网页授权凭证
*
*
* @param code
* @return WeixinAouth2Token
*/
public JSONObject getOauth2AccessToken(String code) {
JSONObject jsonObject = null;
try {
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+APPID+"&secret="+AppSecret+"&code="+code+"&grant_type=authorization_code";
//String requestUrl = "https://api.weixin.qq.com/sns/jscode2session?appid="+APPID+"&secret="+APPSECRET+"&js_code="+code+"&grant_type=authorization_code";
String resStr = rest.getForObject(requestUrl, String.class);
jsonObject=JSON.parseObject(resStr,JSONObject.class);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return jsonObject;
}
/**
* 获取网页授权凭证
*
*
* @param code
* @return WeixinAouth2Token
*/
public String getOauth2Code(HttpServletRequest request) {
try {
// String uri = urlEncodeUTF8("http://6gsw4p.natappfree.cc/weChat/loginByWX");
String uri = urlEncodeUTF8("http://"+domain+"/weChat/loginByWX");
// 拼接请求地址
String requestUrl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="+APPID+"&redirect_uri="+uri+"&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect";
return requestUrl;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return null;
}
/**
* URL编码(utf-8)
*
* @param source
* @return
*/
public static String urlEncodeUTF8(String source) {
String result = source;
try {
result = java.net.URLEncoder.encode(source, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
/**
* 发送模板消息前获取token
* @param template_id_short 模板库中模板的编号
* @param t
* @param m
*/
// public static void sendMessageBefore(String template_id_short,WxTemplate t,Map<String,TemplateData> m){
// AccessToken token = null;
// token = AccessTokenInfo.accessToken;
//// if(template_id_short!=null&&!"".equals(template_id_short)){
//// WxTemplate template = WeixinUtil.getTemplate(template_id_short,token.getToken());
//// t.setTemplate_id(template.getTemplate_id());
//// }
// t.setData(m);
// sendMessage(t,token.getAccessToken());
// }
/**
* 发送模板消息
* @param t
* @param accessToken
* @return
*/
public int sendMessage(WxTemplate t,String accessToken) {
int result = 0;
// 拼装创建菜单的url
String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN".replace("ACCESS_TOKEN", accessToken);
// 将菜单对象转换成json字符串
String jsonMenu = JSONObject.toJSONString(t);
// 调用接口创建菜单
JSONObject jsonObject = WeixinUtil.httpRequest(url, "POST", jsonMenu);
//JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);
if (null != jsonObject) {
if (0 != jsonObject.getIntValue("errcode")) {
result = jsonObject.getIntValue("errcode");
System.out.println("发送模板消息失败 errcode:{"
+jsonObject.getIntValue("errcode")+"} errmsg:{"+jsonObject.getString("errmsg")+"}");
}
}
return result;
}
}
消息模板群发消息
@ApiOperation(value = "消息模板群发消息")
@RequestMapping("/groupSending")
@ResponseBody
public Result groupSending(@RequestBody(required=false) WxTemplate wxTemplate,HttpServletRequest request,HttpServletResponse response,HttpSession session){
Integer tagid=wxTemplate.getTagid();
String templateId=wxTemplate.getTemplate_id();
Map<String, TemplateData> map = wxTemplate.getData();
TemplateData templateData = map.get("first");
System.out.println("value= "+templateData.getValue());
System.out.println("color= "+templateData.getColor());
if(tagid==null) {
return Result.build(false, "9999","tagid不能为空");
}
if(StringUtils.isBlank(templateId)) {
return Result.build(false, "9999","templateId不能为空");
}
try {
Thread thread=new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
AccessToken accessToken = WeixinUtil.getAccessToken(APPID,AppSecret);
String url="https://api.weixin.qq.com/cgi-bin/user/tag/get?access_token="+accessToken.getToken();
JSONObject postData = new JSONObject();
postData.put("tagid", tagid);
JSONObject body = rest.postForEntity(url, postData, JSONObject.class).getBody();
System.out.println(body.getJSONObject("data").getString("openid"));
String[] list =body.getJSONObject("data").getObject("openid", String[].class);
url="https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+accessToken.getToken();
postData.remove("tagid");
postData.put("data", map);
postData.put("template_id", templateId);
postData.put("url", "");
for(String openid:list) {
postData.put("touser", openid);
String result = rest.postForEntity(url, postData, String.class).getBody();
log.info("模板消息发送:openid is "+openid+" and result is "+result,WeChatAction.class);
}
}
};
thread.start();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return Result.error();
}
return Result.ok("您的消息已发送成功");
}