微信公众号消息模板发送
微信公众号消息模板发送
- 微信公众号消息模板群发功能
- 开通申请模板消息功能
- 获取模板消息发送所需参数
- AccessToken pojo类
- TemplateData pojo类
- WxTagsEntity pojo类
- WxTemplate pojo类
- WxTemplateEntity pojo类
- 微信公众号消息模板工具类
- 消息模板群发消息
微信公众号消息模板群发功能
开通申请模板消息功能
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;//模板IDprivate Integer tagid;//模板IDprivate 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;@Autowiredprivate 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 exceptione.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 exceptione.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;// 拼装创建菜单的urlString 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")@ResponseBodypublic 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() {@Overridepublic void run() {// TODO Auto-generated method stubAccessToken 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 exceptione.printStackTrace();return Result.error();}return Result.ok("您的消息已发送成功");}
微信公众号消息模板发送相关推荐
- 微信公众号消息模板开发
为什么80%的码农都做不了架构师?>>> ##背景 新需求,需要在订单的时候给用户,商家,配送员发送想对于的微信消息模板,之前没有做过微信公众号相关的开发,这次就一并熟悉吧 # ...
- 微擎微信公众号消息模板
sendTplNotice() 说明 sendTplNotice($touser, $template_id, $postdata, $url = '', $topcolor = '#FF683F') ...
- 微信公众号网页-模板发送失败:40001
记录下遇到的问题 模板发送失败:40001:获取 access_token 时 AppSecret 错误,或者 access_token 无效.请开发者认真比对 AppSecret 的正确性,或查看是 ...
- php 公众号 模板消息id如何获取_微信公众号后台模板消息如何实现发送的功能...
在公众平台实现发送模板消息功能,只能通过公众平台的接口开发实现,或者通过第三方平台微号帮功能模板消息群发实现,均能为微信公众号发送模板消息功能,模板消息发送不占用公众号每月的群发次数,模板消息仅用于公 ...
- php微信公众号向指定客服发信息,微信公众号给用户发送一条消息 客服消息
可以用客服消息接口或模板消息接口实现.但是需要认证公众号才能有权限. 认证服务号可以发送客服接口消息[需要对应openid24小时内有互动]和模板消息,订阅号则没有模板消息权限. 下面是发送客服消息的 ...
- 微信小程序使用微信公众号的模板消息进行消息推送开发流程
微信小程序使用微信公众号的模板消息进行消息推送开发流程 微信公众号服务号,微信公众号订阅号,微信公众号开发者平台,微信小程序 这些的账号都是独立的不能共用 微信开放平台开发者资质认证审核费用为300元 ...
- 微信公众号消息推送开发(模板消息):点击推送消息跳转到网页或者小程序(三)
需求场景: 点击推送的消息后跳转到小程序中的某个页面,或者跳转到某一个网页上,例如有这样一个需求,在商城平台购买产品后,需要通过公众号给用户推送订单详细并且点击消息可以看到对应的订单详情.以下我将在微 ...
- 微信公众号消息增加跳转链接
微信公众号消息增加跳转链接 背景: 用户在首次关注公众号后会弹出一条欢迎消息.给这条消息增加跳转的链接,使得用户在点击之后可以跳转 到一个你希望用户访问的页面. 解决方案: 根据微信开发者文档,找到了 ...
- 微信公众号消息通知手把手教程
项目需求:微信公众号消息推送 首先你需要我给你提供以下地址 方便你进行开发 准备阶段: a 模版消息推送开发文档 链接 b 微信公众号测试号地址 链接 c 微信公众平台接口调试工具 链接 ...
最新文章
- 面试官问:什么是布隆过滤器?
- 笔记本输入法, u、i 等字母变成了数字
- 把Hybris安装时输出的日志重定向到一个本地文件中
- 前端学习(545):node的系统模块require
- Leetcode分类
- leetcode: 451. Sort Characters By Frequency
- CentOS 安装JDK跟TOMCAT
- lg g2 android 5.0 rom,LG G2(D802)升级Flyme4.5图文教程
- 【C语言】如何用C语言画一个哆啦A梦(附源代码)
- 简单典型二阶系统_关于石墨烯结构的典型拉曼光谱特征,这一篇讲得非常透彻!...
- Unrecoverable error: corrupted cluster config file.
- VMware Workstation Pro的安装详细过程
- 让人心疼的12句话。。哪句说到你的痛了?
- 360手机卫士企业版下载
- mybatis数组越界异常 Error preparing statement
- 关于在word中插入页码以及目录的操作
- HDU 5117 Fluorescent
- java 入门专题 字符缓冲输入流HashMap 集合的简单综合应用:文本排序
- BIG ENDIAN V.S. LITTER ENDIAN
- 用Keil C编制单片机高级语言程序,KeilC单片机C语言与研究.doc
热门文章
- Comic Life 3 for Mac(漫画创作软件)内附安装教程需要 macOS 11.x系统
- 12月2日科技资讯|微信回应发原图泄露位置信息;Linux Kernel 5.4.1 发布
- LinuxC学习保姆级教程(李慧芹课程笔记)
- 对于2019全国高速公路视频联网工作实施方案的理解:视频上云网关与省级视频云平台
- Windows 7使用宝典安装技巧篇之——如何在Win7桌面上显示“我的电脑”
- vue-路由篇页面跳转和页面参数传递
- 【EARLIER/EARLIEST函数】引用不存在的更早的行上下文 报错解决
- utf8汉字编码16进制对照
- flex effect
- 【电子电路计算公式】 导线流过电流计算工具,我已经做成一个小工具了(源代码)