1.注册微信小程序app,然后登陆微信公众平台开通“消息推送”,配置url、token等参数2.平台——模板消息——我的模板申请模板,获取模板id、配置的字段名3.在外网80/443的接口中放一个可以验证签名的接口(详情见下文)。4.Java后台写发送消息的工具类5.小程序写一个可以获取formId、openid的页面6.用真机调试中获取到的参数在java工具类中main方法测试发送通知。成功!
0、登录微信公众平台,开启小程序“消息推送”功能
开发——开发设置——消息推送
注意:URL填写的是:要在外网能访问的接口(需要80或者443端口才行),该接口需要包括能够验证签名的方法,具体方法如下:只需要把下面接口中的WECHAT_TOKEN:改成消息推送的"Token(令牌)"值一样即可。
importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.PrintWriter;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmException;/微信授权/@Controller@Slf4j@RequestMapping("api/wx")publicclassAPIWxController{//与接口配置信息中的Token要一致privatestaticStringWECHAT_TOKEN="WECHAT_TOKEN";@RequestMapping("/checkToken")publicvoidget(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{log.info("========checkTokenController=========");booleanisGet=request.getMethod().toLowerCase().equals("get");PrintWriterprint;if(isGet){//微信加密签名Stringsignature=request.getParameter("signature");//时间戳Stringtimestamp=request.getParameter("timestamp");//随机数Stringnonce=request.getParameter("nonce");//随机字符串Stringechostr=request.getParameter("echostr");//通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败if(signature!=null&&checkSignature(signature,timestamp,nonce)){try{print=response.getWriter();print.write(echostr);print.flush();log.info("========checkTokensuccess=========");}catch(IOExceptione){e.printStackTrace();}}else{log.error("========checkTokenfailed=========");}}else{log.error("========checkTokenfailed:OnlysupportGetMethod=========");}}/验证签名@paramsignature@paramtimestamp@paramnonce@return/publicstaticbooleancheckSignature(Stringsignature,Stringtimestamp,Stringnonce){String[]arr=newString[]{WECHAT_TOKEN,timestamp,nonce};//将token、timestamp、nonce三个参数进行字典序排序//Arrays.sort(arr);sort(arr);StringBuildercontent=newStringBuilder();for(inti=0;iarr.length;i++){content.append(arr[i]);}MessageDigestmd=null;StringtmpStr=null;try{md=MessageDigest.getInstance("SHA-1");//将三个参数字符串拼接成一个字符串进行sha1加密byte[]digest=md.digest(content.toString().getBytes());tmpStr=byteToStr(digest);}catch(NoSuchAlgorithmExceptione){e.printStackTrace();}content=null;//将sha1加密后的字符串可与signature对比,标识该请求来源于微信returntmpStr!=null?tmpStr.equals(signature.toUpperCase()):false;}/将字节数组转换为十六进制字符串@parambyteArray@return/privatestaticStringbyteToStr(byte[]byteArray){StringstrDigest="";for(inti=0;ibyteArray.length;i++){strDigest+=byteToHexStr(byteArray[i]);}returnstrDigest;}/将字节转换为十六进制字符串@parammByte@return/privatestaticStringbyteToHexStr(bytemByte){char[]Digit={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};char[]tempArr=newchar[2];tempArr[0]=Digit[(mByte4)&0X0F];tempArr[1]=Digit[mByte&0X0F];Strings=newString(tempArr);returns;}publicstaticvoidsort(Stringa[]){for(inti=0;ia.length-1;i++){for(intj=i+1;ja.length;j++){if(a[j].compareTo(a[i])0){Stringtemp=a[i];a[i]=a[j];a[j]=temp;}}}}}在模板消息——我的模板中创建一个模板消息,获取模板ID

1、java后台创建小程序Vo类,用于封装传送的参数。TemplateDataVo.java
importlombok.Data;/设置推送的文字和颜色/@DatapublicclassTemplateDataVo{//字段值例如:keyword1:订单类型,keyword2:下单金额,keyword3:配送地址,keyword4:取件地址,keyword5备注privateStringvalue;//依次排下去//privateStringcolor;//字段颜色(微信官方已废弃,设置没有效果)}WxMssVo.java
importlombok.Data;importjava.util.Map;/小程序推送所需数据/@DatapublicclassWxMssVo{privateStringtouser;//用户openidprivateStringtemplate_id;//模版idprivateStringpage="pages/index/index";//默认跳到小程序首页privateStringform_id;//收集到的用户formid//privateStringemphasis_keyword="keyword1.DATA";//放大那个推送字段privateMapString,TemplateDataVodata;//推送文字}2、java后台创建发送消息推送类WeChatService.java该类包括以下主要方法:1、推送通知的主要方法类:/@paramaccess_tokenapp的token@paramopenid用户openid@paramformId表单ID@paramtemplateId模板ID@paramkeywords{与模板字段一一对应}@return/publicStringpushOneUser(Stringaccess_token,Stringopenid,StringformId,StringtemplateId,String[]keywords)2、获取app的access_token方法/获取access_tokenappid和appsecret到小程序后台获取,当然也可以让小程序开发人员给你传过来/publicStringgetAccess_token()
importcom.alibaba.fastjson.JSONObject;importcom.weixin.demo.entity.TemplateDataVo;importcom.weixin.demo.entity.WxMssVo;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.http.ResponseEntity;importorg.springframework.stereotype.Service;importorg.springframework.util.StringUtils;importorg.springframework.web.client.RestTemplate;importjava.util.HashMap;importjava.util.Map;@Slf4j@ServicepublicclassWeChatService{@BeanpublicRestTemplaterestTemplate(){returnnewRestTemplate();}@AutowiredprivateRestTemplaterestTemplate;/@paramaccess_tokenapp的token@paramopenid用户openid@paramformId表单ID@paramtemplateId模板ID@paramkeywords{与模板字段一一对应}@return/publicStringpushOneUser(Stringaccess_token,Stringopenid,StringformId,StringtemplateId,String[]keywords){//如果access_token为空则从新获取if(StringUtils.isEmpty(access_token)){access_token=getAccess_token();}Stringurl="https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send"+"?access_token="+access_token;//拼接推送的模版WxMssVowxMssVo=newWxMssVo();wxMssVo.setTouser(openid);//用户openidwxMssVo.setForm_id(formId);//formIdwxMssVo.setTemplate_id(templateId);//模版idMapString,TemplateDataVom=newHashMap();//封装数据if(keywords.length0){for(inti=1;i=keywords.length;i++){TemplateDataVokeyword=newTemplateDataVo();keyword.setValue(keywords[i-1]);m.put("keyword"+i,keyword);}wxMssVo.setData(m);}else{log.error("keywords长度为空");returnnull;}if(restTemplate==null){restTemplate=newRestTemplate();}ResponseEntityStringresponseEntity=restTemplate.postForEntity(url,wxMssVo,String.class);log.error("小程序推送结果={}",responseEntity.getBody());returnresponseEntity.getBody();}/获取access_tokenappid和appsecret到小程序后台获取,当然也可以让小程序开发人员给你传过来/publicStringgetAccess_token(){//获取access_tokenStringappid="wxb1abfc1724c5ee8b";Stringappsecret="c88a5dd3c3af8228a389d778fce3e32b";Stringurl="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"+"&appid="+appid+"&secret="+appsecret;if(restTemplate==null){restTemplate=newRestTemplate();}Stringjson=restTemplate.getForObject(url,String.class);JSONObjectmyJson=JSONObject.parseObject(json);returnmyJson.get("access_token").toString();}publicstaticvoidmain(String[]args){System.out.println(newWeChatService().getAccess_token());WeChatServiceweChatUtil=newWeChatService();Stringvalues[]={"Jack方","2019-5-810:10:10","xxx有限公司","JAVA开发","xx区xx广场xx号","请带好入职材料"};weChatUtil.pushOneUser(weChatUtil.getAccess_token(),"o_fh25E0IufW7NIpezUReODfVH68","ec76b8b81cd04cf6b464bb0adf309d3b","zv0IsYDpJxgKWLHGUy8FEv0ajtJqkfhWTsFWiM7zzSU",values);}}3、微信小程序(小程序申请什么的就自己去操作吧)我们在本地的小程序开发工具构建一个这样的页面来测试下:为什么是这样的页面?主要是为了方便获取openid、formId

index.wxml只有元素中加上report-submit=“true”才能获取得到formId,formId有效期为7天,一个formId只能发送一次通知,发完就不能再用了,formId是发送推送信息的必要条件。
viewclass="container"formbind:submit='getOpenIdTap'report-submit="true"buttonformType='submit'获取用户唯一标识openid/buttonviewclass='widget'textclass='column'openid:{{openid}}/text/viewviewclass='widget'textclass='column'session_key:{{session_key}}/text/viewviewclass='widget'textclass='column'formId:{{formId}}/text/view/formformbind:submit="testSubmit"report-submit="true"buttonformType="submit"发送模板消息/buttonviewclass='widget'textclass='column'errcode:{{errcode}}/text/viewviewclass='widget'textclass='column'errmsg:{{errmsg}}/text/view/formviewclass='moto-container'bindtap='bindViewTap'textclass='moto'获取列表/text/view/viewindex.js将index.js中的下面几个参数换成你的即可constAPP_ID='APP_ID';//输入小程序appidconstAPP_SECRET='APP_SECRET';//输入小程序app_secret
//index.js//获取应用实例constapp=getApp()constAPP_ID='APP_ID';//输入小程序appidconstAPP_SECRET='APP_SECRET';//输入小程序app_secretvarOPEN_ID=''//储存获取到openidvarSESSION_KEY=''//储存获取到session_kvarFORM_ID=''//储存获取到的formIdPage({data:{motto:'HelloWorld',userInfo:{},hasUserInfo:false,canIUse:wx.canIUse('button.open-type.getUserInfo')},//事件处理函数bindViewTap:function(){wx.navigateTo({url:'../list/list'})},onLoad:function(){if(app.globalData.userInfo){this.setData({userInfo:app.globalData.userInfo,hasUserInfo:true})}elseif(this.data.canIUse){//由于getUserInfo是网络请求,可能会在Page.onLoad之后才返回//所以此处加入callback以防止这种情况app.userInfoReadyCallback=res={this.setData({userInfo:res.userInfo,hasUserInfo:true})}}else{//在没有open-type=getUserInfo版本的兼容处理wx.getUserInfo({success:res={app.globalData.userInfo=res.userInfothis.setData({userInfo:res.userInfo,hasUserInfo:true})}})}},getUserInfo:function(e){console.log(e)app.globalData.userInfo=e.detail.userInfothis.setData({userInfo:e.detail.userInfo,hasUserInfo:true})},getOpenIdTap:function(e){varthat=this;FORM_ID=e.detail.formId;//获取到formIdconsole.log("formId1:"+FORM_ID)that.setData({formId:FORM_ID})wx.login({success:function(res){wx.request({//获取openid接口url:'https://api.weixin.qq.com/sns/jscode2session',data:{appid:APP_ID,secret:APP_SECRET,js_code:res.code,grant_type:'authorization_code'},method:'GET',success:function(res){OPEN_ID=res.data.openid;//获取到的openidSESSION_KEY=res.data.session_key;//获取到session_keyconsole.log("openid:"+OPEN_ID)console.log("session_key:"+SESSION_KEY)that.setData({openid:OPEN_ID,session_key:SESSION_KEY})}})}})},testSubmit:function(e){varthat=this;wx.request({url:'http://127.0.0.1:80/pushMsg',method:'POST',data:{access_token:null,openid:OPEN_ID,formid:FORM_ID},success:function(res){that.setData({errcode:res.data.errcode,errmsg:res.data.errmsg})console.log(res)},fail:function(err){console.log('requestfail',err);},complete:function(res){console.log("requestcompleted!");}})}})运行小程序,点击获取openid、formId如果出现如下图所示:formIdisamockone。说明需要我们用真机测试才能获取formId
点击“真机调试”,用手机扫描二维码
拿到我们需要的openId、formId
回到java后台WeChatService.java用main方法测试一下。推送成功!
看下手机端展示:

为什么不直接在小程序中直接发送“模板消息”?答:主要是因为真机测试时,手机不能调用本地的接口,所以不好测试,我们用真机测试获取formId,在放在java后台,模拟调用接口测试















