微信公众号开发模板消息管理发送
一. 能力开通
微信文档参考: https://developers./doc/offiaccount/Message_Management/Template_Message_Interface.html
1. 登录微信公众平台:https://mp./
2. 按照图示依次进入
如果是未开通能力的账户, 在未开通里面寻找,根据提示填入相关信息即可
进入模板库,添加符合自己需求的模板消息, 在我的模板中查看模板id,如图:
查看模板消息的内容,以第四个为例
至此模板消息的功能开通完成!
二. 表结构设计以及java代码编写
1. 表结构设计
1.1 根据添加的消息模板和文档里面所需的参数设计表
消息内容
表结构(根据功能需求设计, 不必完全一样)
2. java实现该功能
2.1 模板消息实体类
package net.huashitong.ssydt.wechat.entity;import io.swagger.annotations.ApiModelProperty;import net.huashitong.ssydt.base.entity.BaseEntity;import net.huashitong.ssydt.base.entity.LongEntity;/*** @author xiazz* @Date /11/3* @Description: t_template_message 公众号模板消息*/public class TemplateMessage extends BaseEntity {@ApiModelProperty("id")private String id;@ApiModelProperty("模板id")private String templateId;@ApiModelProperty("推送时间")private String pushTime;@ApiModelProperty("副标题")private String firstData;@ApiModelProperty("关键词1")private String keyword1;@ApiModelProperty("关键词2")private String keyword2;@ApiModelProperty("关键词3")private String keyword3;@ApiModelProperty("消息跳转的url")private String referUrl;@ApiModelProperty("关联的appid")private String relateAppid;@ApiModelProperty("备注")private String remark;@ApiModelProperty("用户的openid")private String openid;@ApiModelProperty("推送类型: 1所有用户 2单个指定用户")private Integer pushType;@ApiModelProperty("推送状态: 0未推送 1已推送")private Integer pushStatus;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getTemplateId() {return templateId;}public void setTemplateId(String templateId) {this.templateId = templateId;}public String getPushTime() {return pushTime;}public void setPushTime(String pushTime) {this.pushTime = pushTime;}public String getFirstData() {return firstData;}public void setFirstData(String firstData) {this.firstData = firstData;}public String getKeyword1() {return keyword1;}public void setKeyword1(String keyword1) {this.keyword1 = keyword1;}public String getKeyword2() {return keyword2;}public void setKeyword2(String keyword2) {this.keyword2 = keyword2;}public String getKeyword3() {return keyword3;}public void setKeyword3(String keyword3) {this.keyword3 = keyword3;}public String getReferUrl() {return referUrl;}public void setReferUrl(String referUrl) {this.referUrl = referUrl;}public String getRelateAppid() {return relateAppid;}public void setRelateAppid(String relateAppid) {this.relateAppid = relateAppid;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}public String getOpenid() {return openid;}public void setOpenid(String openid) {this.openid = openid;}public Integer getPushType() {return pushType;}public void setPushType(Integer pushType) {this.pushType = pushType;}public Integer getPushStatus() {return pushStatus;}public void setPushStatus(Integer pushStatus) {this.pushStatus = pushStatus;}}
2.2 模板消息管理的增删改查功能(根据功能需求编写即可, 不赘述)
2.3 模板消息的发送功能(重点看!!!)
@ApiOperation("发送模板消息")@GetMapping("/send/{id}")public Result<String> sendMsg(@PathVariable("id") String id) {String colorBlue = "#173177";String colorRed = "#DC143C";// 根据id获取详细的模板消息参数TemplateMessage templateMessage = templateMessageService.info(id);if (Objects.isNull(templateMessage) || Objects.equals(1,templateMessage.getPushStatus())) {throw new ServiceException("消息不存在或者已推送");}// 模板参数Map<String, WeChatTemplateMsg> sendMsg = new HashMap<String, WeChatTemplateMsg>();sendMsg.put("first",new WeChatTemplateMsg(templateMessage.getFirstData()));sendMsg.put("keyword1",new WeChatTemplateMsg(templateMessage.getKeyword1(),colorRed)); // 考试名称sendMsg.put("keyword2",new WeChatTemplateMsg(templateMessage.getKeyword2())); // 考试单位sendMsg.put("keyword3",new WeChatTemplateMsg(templateMessage.getKeyword3(),colorRed)); // 考试时间sendMsg.put("remark",new WeChatTemplateMsg(templateMessage.getRemark(),colorBlue));String accessToken = getAccessToken().getAccess_token();// 测试: 向单个用户发送String send = send(accessToken,"oJcdptwBGx3bSa8-FbepyxB-2Okc", templateMessage.getTemplateId(),templateMessage.getReferUrl(),templateMessage.getRelateAppid(), sendMsg);JSONObject jsonObject = JSONObject.parseObject(send);Integer errcode = (Integer) jsonObject.get("errcode");if (Objects.equals(0,errcode)) {try {templateMessage.setPushStatus(1); // 设置为已推送templateMessageService.update(templateMessage);} catch (Exception e) {throw new ServiceException("消息状态更新失败,原因: " + e.getMessage());}}return ResultUtils.getSuccessResults("发送完成");// 线上:向所有已关注公众号的用户发送模板消息logger.info("模板消息开始发送......");long start = System.currentTimeMillis();List<String> openidList = new ArrayList<>();Integer total = getOpenIdList(openidList, accessToken, "");logger.info("关注该公众账号的总用户数:" + total);logger.info("获取到的公众号的总用户数:" + openidList.size());// 用户数量较多, 使用多线程发送if (total == openidList.size()) {ExecutorService executor = Executors.newFixedThreadPool(35);for (String openid : openidList) {executor.execute(new SendTask(accessToken, openid, templateMessage, sendMsg));}}long end = System.currentTimeMillis();// 更新消息状态try {templateMessage.setPushStatus(1); // 设置为已推送templateMessageService.update(templateMessage);} catch (Exception e) {throw new ServiceException("消息状态更新失败,原因: " + e.getMessage());}logger.info("模板消息发送完成,共耗时:" + (end - start) + "ms");return ResultUtils.getSuccessResults("发送完成");}
获取accessToken
public static AccessToken getAccessToken() {String sb = "https://api./cgi-bin/token?" +"grant_type=client_credential" +"&appid=" + "公众号appId" +"&secret=" + "公众号AppSecret";HttpRequest request = HttpRequest.get(sb);HttpResponse response = request.send();return JSON.parseObject(response.bodyText(),AccessToken.class);}
获取所有已关注的用户openid列表, 微信最多返回一万条, 这里采用递归取全部
/*** @date: /11/2* @description: 获取关注者的(openid列表)*/public static Integer getOpenIdList(List<String> openidList,String accessToken,String nextOpenid) {RestTemplate restTemplate = getInstance("UTF-8");Map<String, String> params = new HashMap<>();params.put("access_token", accessToken); //params.put("next_openid", nextOpenid); //ResponseEntity<String> responseEntity = restTemplate.getForEntity("https://api./cgi-bin/user/get?access_token={access_token}&next_openid={next_openid}", String.class, params);String body = responseEntity.getBody();JSONObject jsonObject = JSON.parseObject(body);// 解析成JSONObject 并获取(叫data的JSON对象)JSONObject// 获取 JSONObject 中的 JSONArrayJSONArray array = jsonObject.getJSONObject("data").getJSONArray("openid");List<String> currentOpenidList = array.toJavaList(String.class);openidList.addAll(currentOpenidList);Integer total = (Integer) jsonObject.get("total");if (openidList.size() < total) {nextOpenid = (String) jsonObject.get("next_openid");getOpenIdList(openidList,accessToken,nextOpenid);}return total;}
工作线程类
public class SendTask implements Runnable {private String accessToken;private String openid;private TemplateMessage templateMessage;private Map<String,WeChatTemplateMsg> dataMap;public SendTask(String accessToken,String openid,TemplateMessage templateMessage,Map<String,WeChatTemplateMsg> dataMap){this.accessToken = accessToken;this.openid = openid;this.templateMessage = templateMessage;this.dataMap = dataMap;}public SendTask() {}public String getAccessToken() {return accessToken;}public void setAccessToken(String accessToken) {this.accessToken = accessToken;}public String getOpenid() {return openid;}public void setOpenids(String openid) {this.openid = openid;}public TemplateMessage getTemplateMessage() {return templateMessage;}public void setTemplateMessage(TemplateMessage templateMessage) {this.templateMessage = templateMessage;}public Map<String,WeChatTemplateMsg> getDataMap() {return dataMap;}public void setDataMap(Map<String,WeChatTemplateMsg> dataMap) {this.dataMap = dataMap;}//多线程入口@Overridepublic void run() {TemplateMessage templateMessage1 = this.templateMessage;String send = send(this.accessToken, openid, templateMessage1.getTemplateId(), templateMessage1.getReferUrl(), templateMessage1.getRelateAppid(),this.dataMap);JSONObject jsonObject = JSONObject.parseObject(send);Integer errcode = (Integer) jsonObject.get("errcode");if (Objects.equals(0,errcode)) {logger.info("已向openid为" + openid + "发送通知");}}}
消息发送的send方法
/*** 2、发送模版消息* openId用户Id* templateId 模板Id* data 模板参数* @param data*/public static String send(String accessToken,String openId, String templateId, String referUrl,String relateAppid, Map<String, WeChatTemplateMsg> data) {RestTemplate restTemplate = getInstance("UTF-8");String sendUrl = "https://api./cgi-bin/message/template/send?access_token=ACCESS_TOKEN";String url = sendUrl.replace("ACCESS_TOKEN", accessToken);//拼接base参数Map<String, Object> sendBody = new HashMap<>();if (StringUtils.isNotBlank(relateAppid)) {Map<String, String> miniprogramMap = new HashMap<>();miniprogramMap.put("appid", relateAppid);miniprogramMap.put("pagepath", "/pages/index/index?foo=bar");sendBody.put("miniprogram",miniprogramMap);// 关联的小程序}if (StringUtils.isNotBlank(referUrl)) {sendBody.put("url", referUrl);// 点击模板信息跳转地址}sendBody.put("touser", openId);// openIdsendBody.put("data", data); // 模板参数sendBody.put("template_id", templateId);// 模板IdResponseEntity<String> forEntity = restTemplate.postForEntity(url, sendBody, String.class);return forEntity.getBody();}
三. 实现效果
管理后台(因功能需求有进行删减, 不必完全一致!!!)
收到的推送
四. 总结
过程中遇到了一些问题, 比如微信一次只给一万条的限制(使用递归获取前每发完一万条要重启一次服务), 使用单线程发送过慢的问题(亲测一万条数据花费1个半小时), 好在后面都解决了, 特此记录! 如果有什么不明白或者觉得说得不对的地方, 欢迎在评论区指正, 互相学习