实现微信公众号发送消息给指定用户
一、前言
在实际项目开发中,需要实现消息中心向关注微信公众号的指定用户发送消息通知,在翻阅了网上很多资料及微信官方开发文档后,最终顺利完成功能开发,但是其中走过的路艰辛且曲折,因此特将开发过程中踩过的坑及心得记录下来,以期给他人带来方便。
二、公众号消息通知开发
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;}
发送微信消息流程图如下所示:
三、总结
关于微信公众号发送用户消息,微信官方文档已经很详细了,但是笔者在实践过程中,还是遇到了很多坑,前后用时几天,才将整个流程串通,因此将心路历程记录下来。结合官方文档及笔者的总结后,基本不会再遇到太大的坑了。
实现微信公众号发送消息给指定用户相关推荐
- python训练营微信广告发送机_python实现给微信公众号发送消息的方法
本文实例讲述了python实现给微信公众号发送消息的方法.分享给大家供大家参考,具体如下: 现在通过发微信公众号信息来做消息通知和告警已经很普遍了.最常见的就是运维通过zabbix调用shell脚本给 ...
- 微信公众号怎么推送消息_微信公众号发送消息
A.模板消息发送 模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等.不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息. 备注 ...
- Java微信公众号发送消息-保姆级教程附源码
目录 1. 概念说明: 2. 开发准备: 3. 测试demo(更改配置信息即可使用) 3.1. 服务器配置 3.1.1.配置填写说明 3.1.2.校验服务器有效性: 3.1.3.URL后端接口代码和校 ...
- 微信公众号发消息给关注用户
前一段时间项目中遇到一个稍微麻烦一点的问题. 即客户要求,他在后台编辑好文章后要主动给每个用户都发送消息,并可以让用户点击直接进入文章页面. 于是乎,当时脑子一热,想着没什么大的问题,so easy. ...
- 微信公众号-根据openID给指定用户发送信息
微信公众号-给指定用户发送信息 文章目录 微信公众号-给指定用户发送信息 前言 一.开通模板消息 二.项目使用步骤 1.引入库 2.直接上代码 controller service 其他一些封装的类 ...
- 小程序发送订阅消息,微信公众号发送消息模板
首先讲两个注意事项 1.小程序和微信公众号的用户openid是不同的. 2.小程序需要用户手动授权订阅消息通知(一次性订阅是订阅一次发一次,长期订阅可以多发). 关于小程序和公众号AppID和AppS ...
- 微信公众号发送消息通知
微信申请测试公众号 申请测试号 微信公众平台接口调试工具 接口调试工具 如何获取用户的openid 获取用户的openid 4.通过微信公众号后台聊天获取用户的openid 测试平台获取appID,a ...
- 微信公众号发送消息 Java
首先申请一个公众号订阅号(个人测试账号)企业可申请服务号 在开发者工具里面申请一个测试账号 url 提供自己服务器地址+通信接口路由 或者在本人博客资源下载个natapp 内网穿透有工具 代理个ht ...
- 微信公众号 java发送消息_微信公众号发送消息模板(java)
这段时间接触公众号开发,写下向用户发送消息模板的接口调用 先上接口代码 1 public staticJSONObject sendModelMessage(ServletContext contex ...
最新文章
- 你有没有想过你的上级为什么让你干这件事情,他想干什么
- c++ #define 预处理器
- springboot集成themeleaf报Namespace 'th' is not bound
- Win32汇编笔记-消息基础
- Android 根证书管理与证书验证
- 打印product所assign的product category和hierarchy的小工具
- 华章7-8月份新书简介(2015年)
- python 多线程读写文件错误_python多线程老是报错。大神帮忙看看哈?
- vscode设置中文,设置中文不成功问题
- LeetCode 1903. 字符串中的最大奇数
- 一步一步写算法(之线性队列)
- eclipse无法启动的各种解决方法
- ipv6 华为交换机 路由配置_华为路由器单臂路由的配置方法及小案例
- nrf52832-定时器例程
- 软件开发过程与项目管理(9.软件项目配置管理计划)
- 【抽象代数】半群、子群、商群
- 解读测试能力素质模型(Job Model)
- 怎么进入计算机配置文件,老司机教你如何查看电脑配置
- Java图形界面编程--漫天繁星
- 实数 有理数 无理数
热门文章
- 基于adams与simulink的七自由度机械臂模型与控制仿真
- 5.1使用css修改网页元素
- 晏殊 天涯 青砚1989
- 简单易懂的特征值与特征向量
- 在php页面出现乱码的原因,造成网页乱码的根本性原因是什么
- android 清理缓存动画,android 清理缓存
- 让你满头黑线的错误,这么看
- 华山论剑(nyoj 856)
- EasyGUI-4:选择函数
- 苹果M1实力太强了,通过.ipa文件的方式在M1 MacBook Air上运行那些尚未在Mac App Store上架的iOS/iPadOS应用程序,Windows用户纷纷种草