一、前言

在实际项目开发中,需要实现消息中心向关注微信公众号的指定用户发送消息通知,在翻阅了网上很多资料及微信官方开发文档后,最终顺利完成功能开发,但是其中走过的路艰辛且曲折,因此特将开发过程中踩过的坑及心得记录下来,以期给他人带来方便。

二、公众号消息通知开发

1.微信公众号开发配置

实现消息发送前,需要提前在业务系统关联的公众号上,做一些基础配置。配置如下所示:
(1)基础配置
开发者ID开发者密码不必多说,懂者自懂,IP白名单设置部署业务系统的IP地址,否则,微信的回调请求地址无法响应请求。服务器地址配置的是微信验证Token是否生效的接口,秘钥自己生成,加解密方式自己根据业务场景选择。这些都设置好后,就万事具备了接下来进入开发环节。

(2)消息模板配置
这里根据自己的业务场景合理选择消息模板,用于后续的消息通知生成

(3)服务器连通性测试配置
这里必须要把图片中提到的txt文件放到服务器的目录下,确保外部可以访问到。

在这里设置微信回调地址分服务器域名,否则调用微信授权认证接口后,会报以下错误:

2.编码实现

这里实现前文中提到的服务器配置中设置的服务器URL请求接口,验证配置的令牌是否生效。

/*** @author zhaolc* @version 1.0* @description TODO* @createTime 2021年02月26日 13:36:00*/
@Slf4j
@RestController
public class WeChatController {private static final String TOKEN = "cc2fb962b7bb37833008d65e905614c8";private static final String APPID="wxf1e42af3ecad28c7";private static final String APPSECRET="cc2fb962b7bb37833008d65e905614c8";/*** 微信验证token** @param signature* @param timestamp* @param nonce* @param echostr* @return*/@GetMapping("/checkTokenNotice.do")public String checkToken(@RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce, @RequestParam("echostr") String echostr) {//排序String[] arr = {TOKEN, timestamp, nonce};Arrays.sort(arr);StringBuilder content = new StringBuilder();for (int i = 0; i < arr.length; i++) {content.append(arr[i]);}//sha1Hex 加密MessageDigest md = null;String temp = null;try {md = MessageDigest.getInstance("SHA-1");byte[] digest = md.digest(content.toString().getBytes());temp = byteToStr(digest);log.info("加密后的token:" + temp);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}if ((temp.toLowerCase()).equals(signature)) {return echostr;}return null;}private static String byteToStr(byte[] byteArray){String strDigest = "";for (int i = 0; i < byteArray.length; i++) {strDigest += byteToHexStr(byteArray[i]);}return strDigest;}private static String byteToHexStr(byte mByte){char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B', 'C', 'D', 'E', 'F' };char[] tempArr = new char[2];tempArr[0] = Digit[(mByte >>> 4)& 0X0F];tempArr[1] = Digit[mByte & 0X0F];String s = new String(tempArr);return s;}}

这个接口是微信请求用户授权地址,具体如何触发授权,这个需由具体业务需求而定,这里不做赘述。因为我只需要用到微信用户的openID,所以本文中用到的是静默授权登录(scope=snsapi_base),即不需要微信用户显式授权,不足之处是获取的用户信息较少。所以这里需要具体业务做选择。

    @Overridepublic String weChatAuth(SendSmsParam req) {//这个url的域名必须要进行在公众号中进行注册验证,这个地址是成功后的回调地址String callBackUrl = "";if ("uat".equals(mark)) {callBackUrl = backUrl + "/uat/api/installment/vip/login/getOpenid.pub?mobile=" + req.getMobile();} else {callBackUrl = backUrl + "/api/installment/vip/login/getOpenid.pub?mobile=" + req.getMobile();}// 静默授权登录(scope=snsapi_base),一种为非静默授权登录(scope=snsapi_userinfo)String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appID + "&redirect_uri=" + URLEncoder.encode(callBackUrl) + "&response_type=code"+ "&scope=snsapi_base" + "&state=STATE#wechat_redirect";log.info("forward重定向地址{" + url + "}");return "redirect:" + url;}

这个接口是微信授权成功后的回调地址,用户授权后,获得授权码,再请求获取openId接口,得到openId,进行进一步的业务操作。

 @Overridepublic void getOpenidNotice(HttpServletRequest request, HttpServletResponse response) {String code = request.getParameter("code");String mobile = request.getParameter("mobile");log.info("手机号码[{}]", mobile);try {request.setCharacterEncoding("UTF-8");//通过获取access_token获得openid和access_tokenString backResult = HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/access_token?appid="+ appID + "&secret=" + appSecret + "&code=" + code +"&grant_type=authorization_code");//根据用户Access_token和openid获取用户信息log.info("用户openId[{}]", backResult);}}

调用消息发送接口发送微信消息到指定用户。

 public Boolean sendWeChatMsg(List<MessageRecordDTO> messageRecordList) {Boolean sendFlag = true;//从redis中获取accessTokenString accessToken = redisCacheService.getMap(MAP_NAME, MAP_KEY);if (StrUtil.isBlank(accessToken)) {accessToken = this.getAndSetAccessToken();}for (MessageRecordDTO messageRecordDTO : messageRecordList) {UserPO userPO = userManager.getById(messageRecordDTO.getUserId());if (ObjectUtil.isNull(userPO)) {continue;}// 根据配置的消息模板构建消息体发送。Map<String, MsgElement> dataMap = getMsgElementMap(messageRecordDTO);String extend2 = userPO.getExtend2();if (StrUtil.isNotBlank(extend2)) {WeChatMsgDTO weChatMsgDTO = new WeChatMsgDTO(extend2, msgTemplateId, dataMap);String json = JSON.toJSONString(weChatMsgDTO);String result = "";try {result = HttpUtil.post("https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken, json);} catch (Exception e) {log.error("发送微信消息失败", e);sendFlag = false;messageRecordDTO.setSendState("2");if (messageRecordDTO.getFirstFlag() == null) {DelayTaskScheduler.putWithDefaultStrategy(new SyncRetryWechatNoticeTask(), new SyncRetryWechatNoticeMessage(messageRecordDTO, weChatMsgService), null);}}log.info("消息记录[{}],微信消息发送结果[{}]", messageRecordDTO.getId(), result);// 发送成功后,可继续进行业务操作}}return sendFlag;}private Map<String, MsgElement> getMsgElementMap(MessageRecordDTO messageRecordDTO) {Map<String, MsgElement> elementMap = new HashMap<>();elementMap.put("first", new MsgElement("亲爱的用户,您有一条新的消息", "#000000"));elementMap.put("keyword1", new MsgElement(ENMessageTypeEnum.getLabelByValue(messageRecordDTO.getMsgType()), "#000000"));elementMap.put("keyword2", new MsgElement(this.getSendMsg(messageRecordDTO), "#000000"));elementMap.put("keyword3", new MsgElement(DateTimeUtil.getDateTimeFormat(messageRecordDTO.getCreateTime()), "#000000"));return elementMap;}

这里涉及到每次发送时,需要accessToken,微信设置的过期时间是2小时,我的处理方式是将accessToken存放到redis中,淘汰时间设置同微信官方过期时间,交由redis来维护accessToken。实现代码如下所示:

 private String getAndSetAccessToken() {String accessToken = "";String backResult = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appID + "&secret=" + appSecret);if (StrUtil.isNotBlank(backResult)) {WeChatTokenDTO weChatTokenDTO = JSON.parseObject(backResult, WeChatTokenDTO.class);accessToken = weChatTokenDTO.getAccess_token();redisCacheService.setMap(MAP_NAME, MAP_KEY, accessToken, weChatTokenDTO.getExpires_in());}return accessToken;}

发送微信消息流程图如下所示:

三、总结

关于微信公众号发送用户消息,微信官方文档已经很详细了,但是笔者在实践过程中,还是遇到了很多坑,前后用时几天,才将整个流程串通,因此将心路历程记录下来。结合官方文档及笔者的总结后,基本不会再遇到太大的坑了。

实现微信公众号发送消息给指定用户相关推荐

  1. python训练营微信广告发送机_python实现给微信公众号发送消息的方法

    本文实例讲述了python实现给微信公众号发送消息的方法.分享给大家供大家参考,具体如下: 现在通过发微信公众号信息来做消息通知和告警已经很普遍了.最常见的就是运维通过zabbix调用shell脚本给 ...

  2. 微信公众号怎么推送消息_微信公众号发送消息

    A.模板消息发送 模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等.不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息. 备注 ...

  3. Java微信公众号发送消息-保姆级教程附源码

    目录 1. 概念说明: 2. 开发准备: 3. 测试demo(更改配置信息即可使用) 3.1. 服务器配置 3.1.1.配置填写说明 3.1.2.校验服务器有效性: 3.1.3.URL后端接口代码和校 ...

  4. 微信公众号发消息给关注用户

    前一段时间项目中遇到一个稍微麻烦一点的问题. 即客户要求,他在后台编辑好文章后要主动给每个用户都发送消息,并可以让用户点击直接进入文章页面. 于是乎,当时脑子一热,想着没什么大的问题,so easy. ...

  5. 微信公众号-根据openID给指定用户发送信息

    微信公众号-给指定用户发送信息 文章目录 微信公众号-给指定用户发送信息 前言 一.开通模板消息 二.项目使用步骤 1.引入库 2.直接上代码 controller service 其他一些封装的类 ...

  6. 小程序发送订阅消息,微信公众号发送消息模板

    首先讲两个注意事项 1.小程序和微信公众号的用户openid是不同的. 2.小程序需要用户手动授权订阅消息通知(一次性订阅是订阅一次发一次,长期订阅可以多发). 关于小程序和公众号AppID和AppS ...

  7. 微信公众号发送消息通知

    微信申请测试公众号 申请测试号 微信公众平台接口调试工具 接口调试工具 如何获取用户的openid 获取用户的openid 4.通过微信公众号后台聊天获取用户的openid 测试平台获取appID,a ...

  8. 微信公众号发送消息 Java

    首先申请一个公众号订阅号(个人测试账号)企业可申请服务号 在开发者工具里面申请一个测试账号 url 提供自己服务器地址+通信接口路由  或者在本人博客资源下载个natapp 内网穿透有工具 代理个ht ...

  9. 微信公众号 java发送消息_微信公众号发送消息模板(java)

    这段时间接触公众号开发,写下向用户发送消息模板的接口调用 先上接口代码 1 public staticJSONObject sendModelMessage(ServletContext contex ...

最新文章

  1. 你有没有想过你的上级为什么让你干这件事情,他想干什么
  2. c++ #define 预处理器
  3. springboot集成themeleaf报Namespace 'th' is not bound
  4. Win32汇编笔记-消息基础
  5. Android 根证书管理与证书验证
  6. 打印product所assign的product category和hierarchy的小工具
  7. 华章7-8月份新书简介(2015年)
  8. python 多线程读写文件错误_python多线程老是报错。大神帮忙看看哈?
  9. vscode设置中文,设置中文不成功问题
  10. LeetCode 1903. 字符串中的最大奇数
  11. 一步一步写算法(之线性队列)
  12. eclipse无法启动的各种解决方法
  13. ipv6 华为交换机 路由配置_华为路由器单臂路由的配置方法及小案例
  14. nrf52832-定时器例程
  15. 软件开发过程与项目管理(9.软件项目配置管理计划)
  16. 【抽象代数】半群、子群、商群
  17. 解读测试能力素质模型(Job Model)
  18. 怎么进入计算机配置文件,老司机教你如何查看电脑配置
  19. Java图形界面编程--漫天繁星
  20. 实数 有理数 无理数

热门文章

  1. 基于adams与simulink的七自由度机械臂模型与控制仿真
  2. 5.1使用css修改网页元素
  3. 晏殊 天涯 青砚1989
  4. 简单易懂的特征值与特征向量
  5. 在php页面出现乱码的原因,造成网页乱码的根本性原因是什么
  6. android 清理缓存动画,android 清理缓存
  7. 让你满头黑线的错误,这么看
  8. 华山论剑(nyoj 856)
  9. EasyGUI-4:选择函数
  10. 苹果M1实力太强了,通过.ipa文件的方式在M1 MacBook Air上运行那些尚未在Mac App Store上架的iOS/iPadOS应用程序,Windows用户纷纷种草