写这篇文章之前先给大家看张图片,这种图是不是很熟悉,用过微信关注过某些公众号的用户应该都见过,没错,是微信公众号推送出来的。通过消息推送,可以为用户发送预约服务,购买提醒服务,发货服务,提现提醒服务等消息通知。

现在,我们也要做这样一件事,不过我的业务场景是这样的:现在有一个公众号,还有一个小程序,我需要给指定的已关注我公众号的用户推送一条模板消息,当用户点击模板消息后可以调到我小程序中的指定页面。(本文以此业务场景记录开发过程)
不了解微信公众号或者没有相关经验的开发人员可能有点懵,因为不知道怎么下手,究竟该如何推送模板消息呢,其实很简单,我们只需要根据微信公众平台技术文档去仔细阅读接口调用方式并按照规范调用即可完成推送。附:模板消息推送接口
大致阅读文档后,我们知道需要具备以下条件:
- 有一个微信公众号,并且知道公众号的appid和appSecret
- 有一个小程序,并且知道小程序的appid
- 需要跳转到的小程序的页面(不知道的话可以问前端开发人员要)
- 知道就模板消息接受者的openid(接受者已关注微信公众号)
以下附上我们需要用到的两个官方接口:
//微信获取ACCESS_TOKEN APIpublic static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET";//微信模板消息推送 APIpublic static final String MESSAGE_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";第一个接口为获取access_token,它是调用接口的凭证,在调用其他接口前必须先调用此接口获取access_token(请将自己的微信公众号的appid和appSecret替换接口地址中的APPID和SECRET)。此接口返回示例如下(返回结果是一个json数据包,注意access_token的有效期为7200s,也就是2小时,且此接口的调用频次为日限2000,如果你的推送用户量大,请把返回的access_token放到缓存中缓存一段时间,节约网络资源,从我做起。:)。另外,调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。
{"access_token":"25_vdgBGnGbfY1bE9e0xOw-mHTROXh6xUXwCANb49jjEQ_Eu4iyhW2MqSmA1UZf7oGelW9AjLGzICvhOJcAqLaxDRVBLmwkeLDEPwQ0YbDN7-wdYza47x-WYDY0lvbFJX3ejIMu6xwnQZGlN2Um1SXHTXSeAD5AHLG","expires_in":7200}第二个接口即为推送消息模板的关键性接口,模板消息的推送就是依靠此接口完成的,但是注意,需要将第一个接口返回的access_token解析拿出来替换这个接口地址中的ACCESS_TOKEN。(以下为此接口的请求体,封装成一个数据传输对象dto,然后转成json)
{ "touser":"OPENID", // 接收模板消息的用户 "template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY", //公众号中配置的消息模板对应的模板ID "url":"http://weixin.qq.com/download", //想要跳转的路径 "miniprogram":{ //需要跳转的小程序的appid和页面 "appid":"xiaochengxuappid12345", "pagepath":"index?foo=bar" }, "data":{ // 封装模板消息数据 "first": { "value":"恭喜你购买成功!", "color":"#173177" }, "keyword1":{ "value":"巧克力", "color":"#173177" }, "keyword2": { "value":"39.8元", "color":"#173177" }, "keyword3": { "value":"2014年9月22日", "color":"#173177" }, "remark":{ "value":"欢迎再次购买!", "color":"#173177" } } }注意:请务必根据微信公众平台的技术文档按照规范封装数据请求体。
注:url和miniprogram都是非必填字段,若都不传则模板无跳转;若都传,会优先跳转至小程序。开发者可根据实际需要选择其中一种跳转方式即可。当用户的微信客户端版本不支持跳小程序时,将会跳转至url。
这就结束了?并没有!如果我没有公众号我想练练手怎么办呢?当然有办法,我们可以申请测试号直接测试!nice!
微信公众平台接口测试帐号申请,点此申请测试号!!!

现在公众号的appid和appSecret有了!

关注二维码,相当于关注了上面的测试号,这样我们就拿到了自己的openid!

增加一个模板消息,现在模板ID也有了!
根据上面的步骤,一步步来,直接放张结果图吧!大功告成!
(本文最终推送的结果与实际场景有出入,因为本测试号无法关联我要跳转的小程序,所以...)

文章最后抛出一些注意点:
- 如果要在微信公众号内推送消息,其分为微信消息与小程序消息,微信消息可以主动推送,小程序消息的推送依赖于用户主动点击小程序内产生的formid,两者是有差别的,注意区分。(本文推送的是微信消息,所以跟formid没关系)
- 如果要推送消息并且跳转对应小程序,需要小程序已审核发布,并且已关联本公众号,否则推送不出来!
- 本文发送http请求依赖了hutool工具包,自行pom文件引入调用即可。
- 多看文档介绍,调用接口推送完毕回返回状态码,根据状态码自行检查错误。全局返回码说明、
附上本文demo代码:
public class WxTemplateMessage { public static void main(String[] args) { final Logger logger = LoggerFactory.getLogger(WxTemplateMessage.class); logger.info("========微信模板消息定时推送开始========"); String appId = "wxf8daaba119674993ae"; // 公众号appid String appSecret = "2a996e13386170a817c42d611a8b26ce49"; // 公众号appSecret String miniAppId = "wx37e7dd9d2312bfb19d4"; // 要跳转的小程序appid String pagePath = "pages/common/Outpatient-pay?type=clinic"; // 要跳转的小程序页面 String accessToken = JSONObject.fromObject(HttpUtil.get(WeChatMessagePushConstants.ACCESS_TOKEN_URL.replace("APPID", appId).replace("SECRET", appSecret))).getString("access_token"); WechatTemplate template = new WechatTemplate(); template.setTemplate_id("a5kTd_SzwgUhwRrh21PF7eYIcouGUNaQ8tdRoHgNTC5GE"); // 模板消息ID template.setTouser("olfZfwadJ24NRAm7YS2fESXYuFVqmM"); // 接收者openid Miniprogram miniprogram = new Miniprogram(); miniprogram.setAppid(miniAppId); miniprogram.setPagepath(pagePath); template.setMiniprogram(miniprogram); Map<String, TemplateData> mapdata = new HashMap<>(); TemplateData first = new TemplateData("您好,您有一笔订单产生!"); mapdata.put("first", first); TemplateData keyword1 = new TemplateData("测试用户"); // 用户名 mapdata.put("keyword1", keyword1); TemplateData keyword3 = new TemplateData("100.00"); // 订单金额 mapdata.put("keyword3", keyword3); TemplateData keyword4 = new TemplateData("小米手机"); // 商品信息 mapdata.put("keyword4", keyword4); TemplateData remark = new TemplateData("更多详细内容点击查看"); // 备注 mapdata.put("remark", remark); template.setData(mapdata); logger.info("推送内容:[{}]", com.alibaba.fastjson.JSONObject.toJSONString(template)); JSONObject pushResult = JSONObject.fromObject(HttpUtil.post(WeChatMessagePushConstants.MESSAGE_TEMPLATE_URL.replace("ACCESS_TOKEN", accessToken), com.alibaba.fastjson.JSONObject.toJSONString(template))); int result = 0; if (null != pushResult) { if (0 != pushResult.getInt("errcode")) { result = pushResult.getInt("errcode"); logger.info("推送结果:[推送失败],错误代码:[{}],错误原因:[{}]。", pushResult.getInt("errcode"), pushResult.getString("errmsg")); } else { logger.info("推送结果:[推送成功]。"); } } logger.info("========微信模板消息定时推送结束========"); }}public class WeChatMessagePushConstants { //微信获取ACCESS_TOKEN API public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET"; //微信模板消息推送 API public static final String MESSAGE_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";}public class Miniprogram { private String appid; private String pagepath; public String getAppid() { return appid; } public void setAppid(String appid) { this.appid = appid; } public String getPagepath() { return pagepath; } public void setPagepath(String pagepath) { this.pagepath = pagepath; }}public class WechatTemplate { private String touser; private String template_id; private Miniprogram miniprogram; private String url; private Map<String, TemplateData> data; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Miniprogram getMiniprogram() { return miniprogram; } public void setMiniprogram(Miniprogram miniprogram) { this.miniprogram = miniprogram; } public String getTouser() { return touser; } public void setTouser(String touser) { this.touser = touser; } public String getTemplate_id() { return template_id; } public void setTemplate_id(String template_id) { this.template_id = template_id; } public Map<String, TemplateData> getData() { return data; } public void setData(Map<String, TemplateData> data) { this.data = data; }}下期再见!













