0


SpringBoot(JAVA)整合微信公众号消息推送

网上那些都是零零碎碎的,不完整,重新整理下,代码可直接使用。
微信公众号消息推送大致分为两类,一是文本推送,二是带图片/视频推送
文本推送很好理解,可以用模板消息以及自定义消息推送。
图文/视频推送就稍微麻烦些步骤分为 上传素材到临时/永久库->上传图文消息->消息推送
贴几个官方文档,有总比没有好。
群发推送官网文档https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html
上传素材官方文档https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html
官方素材上传调试平台https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type=%E5%9F%BA%E7%A1%80%E6%94%AF%E6%8C%81&form=%E5%A4%9A%E5%AA%92%E4%BD%93%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E6%8E%A5%E5%8F%A3%20/media/upload

一、文本推送

这里文本推送,可以采取模板和自定义推送内容。以下是模板方式推送,在图片/视频推送中会使用自定义内容推送演示。

首先需要在微信公众号上把测试的环境弄好。
点击开发者工具->公众平台测试账号。进去创建好对应的消息模板以及关注该测试的公众号。里面会有appID/appsecret,用户,模板以及能体验接口的信息,没有认证的微信号,有些接口是没有权限的,而且部分接口在没有认证的情况下每天都会有调用次数限制
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
添加依赖,因为项目里面用了自己的http封装类,需替换下

<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>5.0.0-alpha.14</version></dependency><dependency><groupId>com.squareup.okio</groupId><artifactId>okio</artifactId><version>3.6.0</version></dependency>

WxToken
用于生成请求接口token

/**
 * 存储微信公众号Token的POJO类
 *
 * @author zjw
 * @description
 */publicclassWxToken{// 存储token信息privateString accessToken;// 10:00:00// 12:00:00// 存储当前的token有效期到什么时间点privateLong expire;publicStringgetAccessToken(){// 获取token之前,需要先判断生存时间到了没return expire ==null|| expire <(System.currentTimeMillis()/1000)?null:this.accessToken;}publicvoidsetAccessToken(String accessToken){this.accessToken = accessToken;}publicLonggetExpire(){return expire;}publicvoidsetExpire(Long expire){this.expire =System.currentTimeMillis()/1000+ expire;}}

WxMagPushReq
这里要注意下模板的填充值data的格式,keyword1就是你在模板里面所需要替换的参数名称,后面value就是参数的值,模板里面的参数是和传入的参数需一一对应

@Data@Schema(description ="微信消息推送部分用户实体")@JsonInclude(JsonInclude.Include.NON_NULL)//这个注解是用于实体转JSON的时候,空值就在转换的时候排除掉publicclassWxMagPushReq{@Schema(description ="用户openid")privateList<String> openIdList;@NotBlank(message ="模板id不能为空")@Schema(description ="模板id")privateString templateId;@Schema(description ="模板需填充的值,keyword1就是模板里面需替换的参数名 如:{"+"                   \"keyword1\":{\n"+"                       \"value\":\"巧克力\"\n"+"                   },\n"+"                   \"keyword2\": {\n"+"                       \"value\":\"39.8元\"\n"+"                   },\n"+"                   }")@NotBlank(message ="模板需填充的值不能为空")privateString data;}

controller
Result 是自定义的返回类,换成自己的即可

/**
     * 微信公众号消息推送--需选用户openId发送
     *
     * @return
     */@Operation(summary ="微信公众号消息推送--需选用户openId发送")@PostMapping("/wxMsgPush")publicResultwxMsgPush(@RequestBody@ValidWxMagPushReq wxMagPushReq){return messageService.wxMsgPush(wxMagPushReq);}

service

ResultwxMsgPush(WxMagPushReq wxMagPushReq);

serviceImpl
因为未认证的微信群发接口无法请求,采用循环发送的方式

@Value("${weixin.msg.secret}")privateString secret;@Value("${weixin.msg.appid}")privateString appid;privatestaticWxToken wxToken =newWxToken();@OverridepublicResultwxMsgPush(WxMagPushReq wxMagPushReq){//1、拿到请求路径String url ="https://api.weixin.qq.com/cgi-bin/message/template/send?access_token="+getTokenString();if(wxMagPushReq ==null|| wxMagPushReq.getOpenIdList().size()==0||StringUtils.isEmpty(wxMagPushReq.getData())||StringUtils.isEmpty(wxMagPushReq.getTemplateId())){throwServiceException.error(ErrorCode.PARAM_EXCEPTION,"参数为空");}//装推送失败的openIdList<String> list =newLinkedList<>();//传入的openId去重List<String> collect = wxMagPushReq.getOpenIdList().stream().distinct().collect(Collectors.toList());for(String openId : collect){//2、请求参数String params ="{\n"+"           \"touser\":\""+ openId +"\",\n"+"           \"template_id\":\""+ wxMagPushReq.getTemplateId()+"\",\n"+"            \"data\":"+ wxMagPushReq.getData()+//                    "           \"data\":{\n" +//                    "                   \"tel\":{\n" +//                    "                       \"value\":\"18700000000\"\n" +//                    "                   }\n" +//                        "           }\n" +"       }";HttpRequest request =HttpUtil.createPost(url);
            request.body(params);String str = request.execute().body();JSONObject json =JSONObject.parseObject(str);Integer errcode = json.getInteger("errcode");if(0!= errcode){
                list.add(openId);}}returnResult.ok(list);}//用于生成认证token    privateStringgetToken(){String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appid +"&secret="+ secret;//2、基于doGet方法,调用地址获取TokenHttpRequest request =HttpUtil.createGet(url);String resultJSON = request.execute().body();JSONObject jsonObject =JSONObject.parseObject(resultJSON);String accessToken = jsonObject.getString("access_token");Long expiresIn = jsonObject.getLong("expires_in");//3、存储到WxToken对象里
        wxToken.setAccessToken(accessToken);
        wxToken.setExpire(expiresIn);//4、返回Tokenreturn wxToken.getAccessToken();}publicStringgetTokenString(){// 从对象中获取accessTokenString accessToken = wxToken.getAccessToken();// 获取的accessToken为null,可能之前没获取,可能过期了if(accessToken ==null){// 加锁synchronized(wxToken){// 再次判断if(wxToken.getAccessToken()==null){getToken();}}}return wxToken.getAccessToken();}

代码中注释掉的tel就是模板中对应的参数名称。
在这里插入图片描述
启动项目就可以测试了,请求成功,所关注的公众号会发一条推送的信息过来。
在这里插入图片描述

二、图文推送

把图片/视频称为素材,带素材推送步骤。上传素材到临时/永久库->上传图文消息->消息推送。
之前看社区说永久的素材库不能使用,下面的示例采用的是临时库

注:上传素材的时候会返回media_id这个ID在上传图文消息的时候需要用到,上传图文消息的时候也会返回media_id,这个ID在消息推送的时候也会用到。
在这里插入图片描述

WxMagPushAllReq

@Data@Schema(description ="微信消息推送全部用户实体")@JsonInclude(JsonInclude.Include.NON_NULL)publicclassWxMagPushAllReq{@Schema(description ="1-文本,2图文,当发送图文消息时,mediaId不能为空")privateString type;//    @NotBlank(message = "消息内容不能为空")@Schema(description ="图文mediaId")privateString mediaId;//    @NotBlank(message = "消息内容不能为空")@Schema(description ="消息内容")privateString content;}

WxTuwen

@DatapublicclassWxTuwen{//图片media_id@Schema(description ="图片media_id")privateString thumb_media_id;@Schema(description ="图文消息的作者")//图文消息的作者privateString author;@Schema(description ="标题")//标题privateString title;@Schema(description ="在图文消息页面点击“阅读原文”后的页面,受安全限制,如需跳转Appstore,可以使用itun.es或appsto.re的短链服务,并在短链后增加 #wechat_redirect 后缀")//在图文消息页面点击“阅读原文”后的页面,受安全限制,如需跳转Appstore,可以使用itun.es或appsto.re的短链服务,并在短链后增加 #wechat_redirect 后缀。privateString content_source_url;@Schema(description ="图文消息页面的内容")//图文消息页面的内容,支持HTML标签。privateString content;@Schema(description ="图文消息的描述")//图文消息的描述,如本字段为空privateString digest;@Schema(description ="是否显示封面,1为显示,0为不显示")//是否显示封面,1为显示,0为不显示privateInteger show_cover_pic;}

controller

@Operation(summary ="微信公众号消息推送--推送全部用户")@PostMapping("/wxMsgPushAll")publicResultwxMsgPushAll(@RequestBody@ValidWxMagPushAllReq wxMagPushAllReq){return messageService.wxMsgPushAll(wxMagPushAllReq);}@Operation(summary ="微信公众号消息推送--上传临时素材")@PostMapping("/addMaterial")publicResultaddMaterial(@RequestParam("media")MultipartFile media,@RequestParam("type")String type){return messageService.addMaterial(media,type);}@Operation(summary ="微信公众号消息推送--上传图文消息素材")@PostMapping("/uploadnews")publicResultuploadnews(@RequestBody@ValidWxTuwen wxTuwen){return messageService.uploadnews(wxTuwen);}

serveice

ResultwxMsgPushAll(WxMagPushAllReq wxMagPushAllReq);ResultaddMaterial(MultipartFile media,String type);Resultuploadnews(WxTuwen wxTuwen);

serviceImpl

@Value("${weixin.msg.secret}")privateString secret;@Value("${weixin.msg.appid}")privateString appid;privatestaticWxToken wxToken =newWxToken();@OverridepublicResultwxMsgPushAll(WxMagPushAllReq wxMagPushAllReq){String list =getWxUserOpenid(getTokenString(),"","");if(StringUtils.isEmpty(list)){returnResult.ok();}String url ="https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token="+getTokenString();//预览接口,可以看推送的效果//        String url = " https://api.weixin.qq.com/cgi-bin/message/mass/preview?access_token=" + getTokenString();String params ="";if("1".equals(wxMagPushAllReq.getType())){//自定义推送内容
            params ="{\n"+"           \"touser\":["+ list.substring(0, list.length()-1)+"],\n"+"           \"msgtype\": \"text\",\n"+"           \"text\": { \"content\": \""+ wxMagPushAllReq.getContent()+"\"}"+"       }";}else{//带图文推送消息
            params ="{\n"+"   \"touser\":["+ list.substring(0, list.length()-1)+"],\n"+"   \"mpnews\":{\n"+"      \"media_id\":\""+ wxMagPushAllReq.getMediaId()+"\"\n"+"   },\n"+"    \"msgtype\":\"mpnews\",\n"+"    \"send_ignore_reprint\":0\n"+"}";}HttpRequest request =HttpUtil.createPost(url);
        request.body(params);String str = request.execute().body();System.out.println(str);returnResult.ok(str);}@OverridepublicResultaddMaterial(MultipartFile media,String type){try{String mediaId =uploadFile(transferToFile(media),getTokenString(), type);returnResult.ok(mediaId);}catch(Exception e){thrownewRuntimeException(e);}}@OverridepublicResultuploadnews(WxTuwen wxTuwen){//如需处理多个图文,修改为循环处理String url ="https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token="+getTokenString();String str =JSONObject.toJSONString(wxTuwen);
        str ="{"+"\"articles\":["+ str +"]"+"}";HttpRequest request =HttpUtil.createPost(url);
        request.body(str);String body = request.execute().body();JSONObject jsonObject =JSONObject.parseObject(body);returnResult.ok(jsonObject.getString("media_id"));}//将类型MultipartFile转为file类型publicFiletransferToFile(MultipartFile file){try{File convFile =newFile(file.getOriginalFilename());
            convFile.createNewFile();InputStream in = file.getInputStream();OutputStream out =newFileOutputStream(convFile);byte[] bytes =newbyte[1024];int read;while((read = in.read(bytes))!=-1){
                out.write(bytes,0, read);}return convFile;}catch(Exception e){thrownewRuntimeException();}}//上传到临时库,返回一个IDpublicStringuploadFile(File file,String accessToken,String type)throwsException{//临时素材地址String url1 ="https://api.weixin.qq.com/cgi-bin/media/upload?access_token="+ accessToken +"&type="+ type;//永久素材的地址//        String url1 = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=" + accessToken + "&type=" + type;if(!file.exists()||!file.isFile()){thrownewIOException("文件不存在!");}URL urlObj =newURL(url1);//连接HttpURLConnection conn =(HttpURLConnection) urlObj.openConnection();

        conn.setRequestMethod("POST");
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);//请求头
        conn.setRequestProperty("Connection","Keep-Alive");
        conn.setRequestProperty("Charset","UTF-8");//conn.setRequestProperty("Content-Type","multipart/form-data;");//设置边界StringBOUNDARY="----------"+System.currentTimeMillis();
        conn.setRequestProperty("Content-Type","multipart/form-data;boundary="+BOUNDARY);StringBuilder sb =newStringBuilder();
        sb.append("--");
        sb.append(BOUNDARY);
        sb.append("\r\n");
        sb.append("Content-Disposition:form-data;name=\"file\";filename=\""+ file.getName()+"\"\r\n");
        sb.append("Content-Type:application/octet-stream\r\n\r\n");System.out.println(sb);byte[] head = sb.toString().getBytes("UTF-8");//输出流OutputStream out =newDataOutputStream(conn.getOutputStream());

        out.write(head);//文件正文部分DataInputStream in =newDataInputStream(newFileInputStream(file));int bytes =0;byte[] bufferOut =newbyte[1024];while((bytes = in.read(bufferOut))!=-1){
            out.write(bufferOut,0, bytes);}
        in.close();//结尾byte[] foot =("\r\n--"+BOUNDARY+"--\r\n").getBytes("utf-8");
        out.write(foot);
        out.flush();
        out.close();//获取响应StringBuffer buffer =newStringBuffer();BufferedReader reader =null;String result =null;

        reader =newBufferedReader(newInputStreamReader(conn.getInputStream()));String line =null;while((line = reader.readLine())!=null){
            buffer.append(line);}if(result ==null){
            result = buffer.toString();}
        reader.close();//需要添加json-lib  jar包JSONObject json =JSONObject.parseObject(result);System.out.println(json);String mediaId = json.getString("thumb_media_id");return result;}//根据appid和secretaccess_tokenprivateStringgetToken(){String url ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+ appid +"&secret="+ secret;//2、基于doGet方法,调用地址获取TokenHttpRequest request =HttpUtil.createGet(url);String resultJSON = request.execute().body();JSONObject jsonObject =JSONObject.parseObject(resultJSON);String accessToken = jsonObject.getString("access_token");Long expiresIn = jsonObject.getLong("expires_in");//3、存储到WxToken对象里
        wxToken.setAccessToken(accessToken);
        wxToken.setExpire(expiresIn);//4、返回Tokenreturn wxToken.getAccessToken();}publicStringgetTokenString(){// 从对象中获取accessTokenString accessToken = wxToken.getAccessToken();// 获取的accessToken为null,可能之前没获取,可能过期了if(accessToken ==null){// 加锁synchronized(wxToken){// 再次判断if(wxToken.getAccessToken()==null){getToken();}}}return wxToken.getAccessToken();}
标签: java spring boot 微信

本文转载自: https://blog.csdn.net/weixin_46204877/article/details/140371691
版权归原作者 刻苦的刘同学 所有, 如有侵权,请联系我们删除。

“SpringBoot(JAVA)整合微信公众号消息推送”的评论:

还没有评论