发布时间:2025-06-24 18:22:16  作者:北方职教升学中心  阅读量:656


首先需要在微信公众号上把测试的环境弄好。
图文/视频推送就稍微麻烦些步骤分为 上传素材到临时/永久库->上传图文消息->消息推送。请求参数Stringparams ="{\n"+" \"touser\":\""+openId +"\",\n"+" \"template_id\":\""+wxMagPushReq.getTemplateId()+"\",\n"+" \"data\":"+wxMagPushReq.getData()+// " \"data\":{\n" +// " \"tel\":{n" +// " "value":"18700000000"n" +// "}\n" +// " }\n" +" }";HttpRequestrequest =HttpUtil.createPost(url);request.body(params);Stringstr =request.execute().body();JSONObjectjson =JSONObject.parseObject(str);Integererrcode =json.getInteger("errcode");if(0!=errcode){list.add(openId);}}returnResult.ok(list);}//用于生成认证token privateStringgetToken(){Stringurl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid +"&secret="+secret;//2、文本推送

这里文本推送,可以采取模板和自定义推送内容
在这里插入图片描述

WxMagPushAllReq

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

WxTuwen

@DatapublicclassWxTuwen{//图片media_id@Schema(description ="图片media_id")privateStringthumb_media_id;@Schema(description ="图文消息的作者")//图文消息的作者privateStringauthor;@Schema(description ="标题")//标题privateStringtitle;@Schema(description ="在图文消息页面点击“阅读原文”后的页面,受安全限制,如需跳转Appstore,可以使用itun.es或appsto.re的短链服务,并在短链后增加 #wechat_redirect 后缀")//在图文消息页面点击“阅读原文”后的页面,受安全限制,如需跳转Appstore,可以使用itun.es或appsto.re的短链服务,并在短链后增加 #wechat_redirect 后缀。
贴几个官方文档,有总比没有好。

@Value("${weixin.msg.secret}")privateStringsecret;@Value("${weixin.msg.appid}")privateStringappid;privatestaticWxTokenwxToken =newWxToken();@OverridepublicResultwxMsgPush(WxMagPushReqwxMagPushReq){//1、

注:上传素材的时候会返回media_id这个ID在上传图文消息的时候需要用到,上传图文消息的时候也会返回media_id,这个ID在消息推送的时候也会用到。
之前看社区说永久的素材库不能使用,下面的示例采用的是临时库privateStringcontent_source_url;@Schema(description ="图文消息页面的内容")//图文消息页面的内容,支持HTML标签。存储到WxToken对象里wxToken.setAccessToken(accessToken);wxToken.setExpire(expiresIn);//4、基于doGet方法,调用地址获取TokenHttpRequestrequest =HttpUtil.createGet(url);StringresultJSON =request.execute().body();JSONObjectjsonObject =JSONObject.parseObject(resultJSON);StringaccessToken =jsonObject.getString("access_token");LongexpiresIn =jsonObject.getLong("expires_in");//3、上传素材到临时/永久库->上传图文消息->消息推送。里面会有appID/appsecret,用户,模板以及能体验接口的信息,没有认证的微信号,有些接口是没有权限的,而且部分接口在没有认证的情况下每天都会有调用次数限制。返回TokenreturnwxToken.getAccessToken();}publicStringgetTokenString(){// 从对象中获取accessTokenStringaccessToken =wxToken.getAccessToken();// 获取的accessToken为null,可能之前没获取,可能过期了if(accessToken ==null){// 加锁synchronized(wxToken){// 再次判断if(wxToken.getAccessToken()==null){getToken();}}}returnwxToken.getAccessToken();}

代码中注释掉的tel就是模板中对应的参数名称。返回TokenreturnwxToken.getAccessToken();}publicStringgetTokenString(){// 从对象中获取accessTokenStringaccessToken =wxToken.getAccessToken();// 获取的accessToken为null,可能之前没获取,可能过期了if(accessToken ==null){// 加锁synchronized(wxToken){// 再次判断if(wxToken.getAccessToken()==null){getToken();}}}returnwxToken.getAccessToken();}


群发推送官网文档: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

一、
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
添加依赖,因为项目里面用了自己的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信息privateStringaccessToken;// 10:00:00// 12:00:00// 存储当前的token有效期到什么时间点privateLongexpire;publicStringgetAccessToken(){// 获取token之前,需要先判断生存时间到了没returnexpire ==null||expire <(System.currentTimeMillis()/1000)?null:this.accessToken;}publicvoidsetAccessToken(StringaccessToken){this.accessToken =accessToken;}publicLonggetExpire(){returnexpire;}publicvoidsetExpire(Longexpire){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")privateStringtemplateId;@Schema(description ="模板需填充的值,keyword1就是模板里面需替换的参数名 如:{"+"                   \"keyword1\":{n"+"                       "value":"巧克力"n"+"},\n"+"                   \"keyword2\": {n"+"                       "value":"39.8元"n"+"},\n"+"                   }")@NotBlank(message ="模板需填充的值不能为空")privateStringdata;}

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

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

service

ResultwxMsgPush(WxMagPushReqwxMagPushReq);

serviceImpl
因为未认证的微信群发接口无法请求,采用循环发送的方式
文本推送很好理解,可以用模板消息以及自定义消息推送。

网上那些都是零零碎碎的,不完整,重新整理下,代码可直接使用。以下是模板方式推送,在图片/视频推送中会使用自定义内容推送演示。privateStringcontent;@Schema(description ="图文消息的描述")//图文消息的描述,如本字段为空privateStringdigest;@Schema(description ="是否显示封面,1为显示,0为不显示")//是否显示封面,1为显示,0为不显示privateIntegershow_cover_pic;}

controller

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

serveice

ResultwxMsgPushAll(WxMagPushAllReqwxMagPushAllReq);ResultaddMaterial(MultipartFilemedia,Stringtype);Resultuploadnews(WxTuwenwxTuwen);

serviceImpl

@Value("${weixin.msg.secret}")privateStringsecret;@Value("${weixin.msg.appid}")privateStringappid;privatestaticWxTokenwxToken =newWxToken();@OverridepublicResultwxMsgPushAll(WxMagPushAllReqwxMagPushAllReq){Stringlist =getWxUserOpenid(getTokenString(),"","");if(StringUtils.isEmpty(list)){returnResult.ok();}Stringurl ="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();Stringparams ="";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"+"}";}HttpRequestrequest =HttpUtil.createPost(url);request.body(params);Stringstr =request.execute().body();System.out.println(str);returnResult.ok(str);}@OverridepublicResultaddMaterial(MultipartFilemedia,Stringtype){try{StringmediaId =uploadFile(transferToFile(media),getTokenString(),type);returnResult.ok(mediaId);}catch(Exceptione){thrownewRuntimeException(e);}}@OverridepublicResultuploadnews(WxTuwenwxTuwen){//如需处理多个图文,修改为循环处理Stringurl ="https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token="+getTokenString();Stringstr =JSONObject.toJSONString(wxTuwen);str ="{"+""articles":["+str +"]"+"}";HttpRequestrequest =HttpUtil.createPost(url);request.body(str);Stringbody =request.execute().body();JSONObjectjsonObject =JSONObject.parseObject(body);returnResult.ok(jsonObject.getString("media_id"));}//将类型MultipartFile转为file类型publicFiletransferToFile(MultipartFilefile){try{FileconvFile =newFile(file.getOriginalFilename());convFile.createNewFile();InputStreamin =file.getInputStream();OutputStreamout =newFileOutputStream(convFile);byte[]bytes =newbyte[1024];intread;while((read =in.read(bytes))!=-1){out.write(bytes,0,read);}returnconvFile;}catch(Exceptione){thrownewRuntimeException();}}//上传到临时库,返回一个IDpublicStringuploadFile(Filefile,StringaccessToken,Stringtype)throwsException{//临时素材地址Stringurl1 ="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("文件不存在!");}URLurlObj =newURL(url1);//连接HttpURLConnectionconn =(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);StringBuildersb =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");//输出流OutputStreamout =newDataOutputStream(conn.getOutputStream());out.write(head);//文件正文部分DataInputStreamin =newDataInputStream(newFileInputStream(file));intbytes =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();//获取响应StringBufferbuffer =newStringBuffer();BufferedReaderreader =null;Stringresult =null;reader =newBufferedReader(newInputStreamReader(conn.getInputStream()));Stringline =null;while((line =reader.readLine())!=null){buffer.append(line);}if(result ==null){result =buffer.toString();}reader.close();//需要添加json-lib  jar包JSONObjectjson =JSONObject.parseObject(result);System.out.println(json);StringmediaId =json.getString("thumb_media_id");returnresult;}//根据appid和secretaccess_tokenprivateStringgetToken(){Stringurl ="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid +"&secret="+secret;//2、
微信公众号消息推送大致分为两类,一是文本推送,二是带图片/视频推送。拿到请求路径Stringurl ="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(StringopenId :collect){//2、图文推送

把图片/视频称为素材,带素材推送步骤。
在这里插入图片描述
启动项目就可以测试了,请求成功,所关注的公众号会发一条推送的信息过来。
点击开发者工具->公众平台测试账号
在这里插入图片描述

二、进去创建好对应的消息模板以及关注该测试的公众号。存储到WxToken对象里wxToken.setAccessToken(accessToken);wxToken.setExpire(expiresIn);//4、基于doGet方法,调用地址获取TokenHttpRequestrequest =HttpUtil.createGet(url);StringresultJSON =request.execute().body();JSONObjectjsonObject =JSONObject.parseObject(resultJSON);StringaccessToken =jsonObject.getString("access_token");LongexpiresIn =jsonObject.getLong("expires_in");//3、