zoukankan      html  css  js  c++  java
  • Java微信公众平台开发_03_消息管理之被动回复消息

    GitHub源码:https://github.com/shirayner/weixin_gz

    一、本节要点

    1.回调url

    上一节,我们启用服务器配置的时候,填写了一个服务器地址(url),如下图,这个url就是回调url,是开发者用来接收微信消息和事件的接口URL 。也就是说,用户在微信公众号中发送的消息会被推送到这个回调url,而我们可以接收用户的消息,并进行回复。

    2.被动回复消息的流程

    官方文档:

    我们在上一节中设置的消息加解密方式是安全模式。因此在用户发给公众号的消息(接收消息)以及公众号被动回复用户消息(回复消息)都会加密,

    流程

    用户发送消息之后,微信服务器将消息传递给 第三方服务器,第三方服务器接收到消息后,再对消息做出相应的回复消息。

    接收消息:需先从request请求对象的输入流中获取请求参数和已加密的请求消息,再对已加密的请求消息进行解密操作,即可获得明文。

                      然后就行对明文消息的业务处理了。

    回复消息:封装好回复消息后,需先对回复消息进行加密,获得已已加密消息,然后再通过http请求调用被动回复消息的接口,来发送消息。

    3.被动回复消息加解密

    3.1接收消息的  解密

     (1)从请求的输入流中获取加密的请求消息

                //1.获取加密的请求消息:使用输入流获得加密请求消息postData
                ServletInputStream in = request.getInputStream();
                BufferedReader reader =new BufferedReader(new InputStreamReader(in));  
    
                String tempStr="";   //作为输出字符串的临时串,用于判断是否读取完毕  
                while(null!=(tempStr=reader.readLine())){  
                    postData+=tempStr;  
                }  
    
                logger.info("postData:"+postData);
    View Code

    (2)对加密的请求消息进行解密获得明文 

                    WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);
                    result=wxcpt.DecryptMsg(msgSignature, timeStamp, nonce, postData);
    View Code

    (3)解密算法

    直接调用微信官方的 WXBizMsgCrypt 类的 DecryptMsg(String, String, String, String) 方法即可。

    3.2 回复消息的加密

    直接用官方加解密工具类。

                    wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);
                    replyMsg=wxcpt.EncryptMsg(replyMsg, timeStamp, nonce);
    View Code

    4.消息对象的封装

    根据官方文档消息的xml传输格式,我们可以将消息封装成对象。请参见后面的代码实现部分

    5.数据传输—对象 转成  xml字符串

     根据官方文档,数据是以XML数据包的形式进行传输的。因此,我们需要

    (1)解析微信发来的请求(xmlStr),从xml字符串中获取需要的信息

    (2)回复消息时,将消息对象转成xml字符串。

    我们是使用dom4j,xstream来进行这个转换的,因此需要导入jar包,maven依赖如下:

    <!-- 7.XML解析 -->
            <dependency>
                <groupId>dom4j</groupId>
                <artifactId>dom4j</artifactId>
                <version>1.6.1</version>
            </dependency>
    
            <dependency>
                <groupId>com.thoughtworks.xstream</groupId>
                <artifactId>xstream</artifactId>
                <version>1.4.10</version>
            </dependency>

    具体请参见代码实现的 MessageUtil 部分。

    5.1  解析微信发来的请求(XML),获取请求参数 

      /**
         * @desc :1.解析微信发来的请求(XML),获取请求参数 
         *  
         * @param request
         * @return
         * @throws Exception Map<String,String>
         */
        public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {  
            // 将解析结果存储在HashMap中  
            Map<String, String> map = new HashMap<String, String>();  
      
            // 从request中取得输入流  
            InputStream inputStream = request.getInputStream();  
            // 读取输入流  
            SAXReader reader = new SAXReader();  
            Document document = reader.read(inputStream);  
            // 得到xml根元素  
            Element root = document.getRootElement();  
            // 得到根元素的所有子节点  
            List<Element> elementList = root.elements();  
      
            // 遍历所有子节点  
            for (Element e : elementList)  
                map.put(e.getName(), e.getText());  
      
            // 释放资源  
            inputStream.close();  
            inputStream = null;  
      
            return map;  
        } 
    View Code

    5.2 将文本消息转成xml字符串

      /** 
         * 2.文本消息对象转换成xml 
         *  
         * @param textMessage 文本消息对象 
         * @return xml 
         */  
        public static String textMessageToXml(TextMessage textMessage) {  
            xstream.alias("xml", textMessage.getClass());  
            return xstream.toXML(textMessage);  
        }  
    View Code

     二、代码实现

    1.微信配置类—Env.java

    微信公众号接入配置类

    package com.ray.weixin.gz.config;
    
    /**@desc  : 微信公众号接入配置
     * 
     * @author: shirayner
     * @date  : 2017年9月27日 下午4:57:36
     */
    
    public class Env {
    
        /**
         * 1. 企业应用接入秘钥相关
         */
       // public static final String APP_ID = "wx4ddse2334debebef2cc";
        //public static final String APP_SECRET = "068e2599abf88ba72frrgbfs6f3c56e";
      
        //测试号
        public static final String APP_ID = "wxa00642deff56g062";
        public static final String APP_SECRET = "fcc96fefdgdhtj1a46af7993784917";
        
    
        /**
         * 2.服务器配置:
         * 启用并设置服务器配置后,用户发给公众号的消息以及开发者需要的事件推送,将被微信转发到该URL中
         */
        public static final String TOKEN = "weixin";
        public static final String ENCODING_AES_KEY = "JvJ1Dww6tjUU2psC3pdewegreHfovfWP3LfX1xrriz1";
        
        
      
    
        
    }
    View Code

    2.HTTP请求工具类—HttpHelper.java

    package com.ray.weixin.gz.util;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    import org.apache.http.Consts;
    import org.apache.http.Header;
    import org.apache.http.HeaderElement;
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.HttpStatus;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.config.RequestConfig;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.entity.ContentType;
    import org.apache.http.entity.FileEntity;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.entity.mime.MultipartEntityBuilder;
    import org.apache.http.entity.mime.content.FileBody;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.protocol.BasicHttpContext;
    import org.apache.http.util.EntityUtils;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    
    
    /**
     * HTTP请求封装,建议直接使用sdk的API
     */
    public class HttpHelper {
    
        /**
         * @desc :1.发起GET请求
         *  
         * @param url
         * @return JSONObject
         * @throws Exception 
         */
        public static JSONObject doGet(String url) throws Exception {
    
            //1.生成一个请求
            HttpGet httpGet = new HttpGet(url);
            //2.配置请求的属性
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();//2000
            httpGet.setConfig(requestConfig);
    
            //3.发起请求,获取响应信息    
            //3.1 创建httpClient 
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = null;
            try {
                //3.2 发起请求,获取响应信息    
                response = httpClient.execute(httpGet, new BasicHttpContext());
    
                //如果返回结果的code不等于200,说明出错了  
                if (response.getStatusLine().getStatusCode() != 200) {
    
                    System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
                            + ", url=" + url);
                    return null;
                }
                //4.解析请求结果
                HttpEntity entity = response.getEntity();      //reponse返回的数据在entity中 
                if (entity != null) {
                    String resultStr = EntityUtils.toString(entity, "utf-8");  //将数据转化为string格式  
                    System.out.println("GET请求结果:"+resultStr);
                    JSONObject result = JSON.parseObject(resultStr);    //将String转换为 JSONObject
    
                    if(result.getInteger("errcode")==null) {
                        return result;
                    }else if (0 == result.getInteger("errcode")) {
                        return result;
                    }else {
                        System.out.println("request url=" + url + ",return value=");
                        System.out.println(resultStr);
                        int errCode = result.getInteger("errcode");
                        String errMsg = result.getString("errmsg");
                        throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                    }
                }
            } catch (IOException e) {
                System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
                e.printStackTrace();
            } finally {
                if (response != null) try {
                    response.close();                     //释放资源
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return null;
        }
    
    
        /** 2.发起POST请求
         * @desc :
         *  
         * @param url   请求url
         * @param data  请求参数(json)
         * @return
         * @throws Exception JSONObject
         */
        public static JSONObject doPost(String url, Object data) throws Exception {
            //1.生成一个请求
            HttpPost httpPost = new HttpPost(url);
    
            //2.配置请求属性
            //2.1 设置请求超时时间
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
            httpPost.setConfig(requestConfig);
            //2.2 设置数据传输格式-json
            httpPost.addHeader("Content-Type", "application/json");
            //2.3 设置请求实体,封装了请求参数
            StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");
            httpPost.setEntity(requestEntity);
    
            //3.发起请求,获取响应信息    
            //3.1 创建httpClient 
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = null;
    
            try {
    
    
                //3.3 发起请求,获取响应
                response = httpClient.execute(httpPost, new BasicHttpContext());
    
                if (response.getStatusLine().getStatusCode() != 200) {
    
                    System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
                            + ", url=" + url);
                    return null;
                }
    
                //获取响应内容
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String resultStr = EntityUtils.toString(entity, "utf-8");
                    System.out.println("POST请求结果:"+resultStr);
    
                    //解析响应内容
                    JSONObject result = JSON.parseObject(resultStr);
    
                    if(result.getInteger("errcode")==null) {
                        return result;
                    }else if (0 == result.getInteger("errcode")) {
                        return result;
                    }else {
                        System.out.println("request url=" + url + ",return value=");
                        System.out.println(resultStr);
                        int errCode = result.getInteger("errcode");
                        String errMsg = result.getString("errmsg");
                        throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                    }
                }
            } catch (IOException e) {
                System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
                e.printStackTrace();
            } finally {
                if (response != null) try {
                    response.close();              //释放资源
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return null;
        }
    
        /**
         * @desc : 3.上传文件
         *  
         * @param url   请求url
         * @param file  上传的文件
         * @return
         * @throws Exception JSONObject
         */
        public static JSONObject uploadMedia(String url, File file) throws Exception {
            HttpPost httpPost = new HttpPost(url);
            CloseableHttpResponse response = null;
            CloseableHttpClient httpClient = HttpClients.createDefault();
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();
            httpPost.setConfig(requestConfig);
            
            //2.3 设置请求实体,封装了请求参数
            HttpEntity requestEntity = MultipartEntityBuilder.create().addPart("media",
                    new FileBody(file, ContentType.create("multipart/form-data", Consts.UTF_8), file.getName())).build();
    
            //FileEntity requestEntity = new FileEntity(file,ContentType.MULTIPART_FORM_DATA);
    
    
            httpPost.setEntity(requestEntity);
    
            try {
                response = httpClient.execute(httpPost, new BasicHttpContext());
    
                if (response.getStatusLine().getStatusCode() != 200) {
    
                    System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
                            + ", url=" + url);
                    return null;
                }
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String resultStr = EntityUtils.toString(entity, "utf-8");
    
                    JSONObject result = JSON.parseObject(resultStr);
                    //上传临时素材成功
                    if (result.getString("errcode")== null) {
                        // 成功
                        //result.remove("errcode");
                        //result.remove("errmsg");
                        return result;
                    } else {
                        System.out.println("request url=" + url + ",return value=");
                        System.out.println(resultStr);
                        int errCode = result.getInteger("errcode");
                        String errMsg = result.getString("errmsg");
                        throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                    }
                }
            } catch (IOException e) {
                System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
                e.printStackTrace();
            } finally {
                if (response != null) try {
                    response.close();                  //释放资源
    
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return null;
        }
    
        /**
         * @desc : 上传PDF
         * 见微信电子发票章节
         * 9. 向用户提供发票或其它消费凭证PDF
         *  
         * @param url
         * @param file
         * @return
         * @throws Exception 
         *   JSONObject
         */
        public static JSONObject uploadPDF(String url, File file) throws Exception {
            HttpPost httpPost = new HttpPost(url);
            CloseableHttpResponse response = null;
            CloseableHttpClient httpClient = HttpClients.createDefault();
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build();
            httpPost.setConfig(requestConfig);
            
            //2.3 设置请求实体,封装了请求参数
            HttpEntity requestEntity = MultipartEntityBuilder.create().addPart("media",
                    new FileBody(file, ContentType.create("multipart/form-data", Consts.UTF_8), file.getName())).build();
    
            httpPost.setEntity(requestEntity);
    
            try {
                response = httpClient.execute(httpPost, new BasicHttpContext());
    
                if (response.getStatusLine().getStatusCode() != 200) {
    
                    System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()
                            + ", url=" + url);
                    return null;
                }
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String resultStr = EntityUtils.toString(entity, "utf-8");
    
                    JSONObject result = JSON.parseObject(resultStr);
                    //上传临时素材成功
                    if (result.getString("errcode")== null) {
                        // 成功
                        //result.remove("errcode");
                        //result.remove("errmsg");
                        return result;
                    } else {
                        System.out.println("request url=" + url + ",return value=");
                        System.out.println(resultStr);
                        int errCode = result.getInteger("errcode");
                        String errMsg = result.getString("errmsg");
                        throw new Exception("error code:"+errCode+", error message:"+errMsg); 
                    }
                }
            } catch (IOException e) {
                System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
                e.printStackTrace();
            } finally {
                if (response != null) try {
                    response.close();                  //释放资源
    
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return null;
        }
        /**
         * @desc : 4.下载文件 -get
         *  
         * @param url  请求url
         * @param fileDir  下载路径
         * @return
         * @throws Exception File
         */
        public static File downloadMedia(String url, String fileDir) throws Exception  {
            //1.生成一个请求
            HttpGet httpGet = new HttpGet(url);
            //2.配置请求属性
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
            httpGet.setConfig(requestConfig);
    
            //3.发起请求,获取响应信息    
            //3.1 创建httpClient 
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = null;
    
            //4.设置本地保存的文件  
            //File file = new File(fileDir);
            File file = null;
            try {
                //5. 发起请求,获取响应信息    
                response = httpClient.execute(httpGet, new BasicHttpContext());
                System.out.println("HttpStatus.SC_OK:"+HttpStatus.SC_OK);  
                System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());  
                System.out.println("http-header:"+JSON.toJSONString( response.getAllHeaders() ));  
                System.out.println("http-filename:"+getFileName(response) );  
    
                //请求成功  
                if(HttpStatus.SC_OK==response.getStatusLine().getStatusCode()){  
    
                    //6.取得请求内容  
                    HttpEntity entity = response.getEntity();  
    
                    if (entity != null) {  
                        //这里可以得到文件的类型 如image/jpg /zip /tiff 等等 但是发现并不是十分有效,有时明明后缀是.rar但是取到的是null,这点特别说明  
                        System.out.println(entity.getContentType());  
                        //可以判断是否是文件数据流  
                        System.out.println(entity.isStreaming());  
    
                        //6.1 输出流
                        //6.1.1获取文件名,拼接文件路径
                        String fileName=getFileName(response);
                        fileDir=fileDir+fileName;
                        file = new File(fileDir);
                        //6.1.2根据文件路径获取输出流
                        FileOutputStream output = new FileOutputStream(file);  
    
                        //6.2 输入流:从钉钉服务器返回的文件流,得到网络资源并写入文件  
                        InputStream input = entity.getContent();  
    
                        //6.3 将数据写入文件:将输入流中的数据写入到输出流
                        byte b[] = new byte[1024];  
                        int j = 0;  
                        while( (j = input.read(b))!=-1){  
                            output.write(b,0,j);  
                        }  
                        output.flush();  
                        output.close();   
                    }  
                    if (entity != null) {  
                        entity.consumeContent();  
                    }  
                }  
            } catch (IOException e) {
                System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
                e.printStackTrace();
            } finally {
                if (response != null) try {
                    response.close();                       //释放资源
    
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return file;
        }
    
    
        /**
         * @desc : 5.下载文件 - post
         *  
         * @param url  请求url
         * @param data  post请求参数
         * @param fileDir 文件下载路径
         * @return
         * @throws Exception File
         */
        public static File downloadMedia(String url, Object data, String fileDir) throws Exception  {
            //1.生成一个请求
            HttpPost httpPost = new HttpPost(url);
    
            //2.配置请求属性
            //2.1 设置请求超时时间
            RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(100000).setConnectTimeout(100000).build();
            httpPost.setConfig(requestConfig);
            //2.2 设置数据传输格式-json
            httpPost.addHeader("Content-Type", "application/json");
            //2.3 设置请求参数
            StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8");
            httpPost.setEntity(requestEntity);
    
            //3.发起请求,获取响应信息    
            //3.1 创建httpClient 
            CloseableHttpClient httpClient = HttpClients.createDefault();
            CloseableHttpResponse response = null;
    
            //4.设置本地保存的文件  
            //File file = new File(fileDir);
            File file = null;
            try {
                //5. 发起请求,获取响应信息    
                response = httpClient.execute(httpPost, new BasicHttpContext());
                System.out.println("HttpStatus.SC_OK:"+HttpStatus.SC_OK);  
                System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode());  
                System.out.println("http-header:"+JSON.toJSONString( response.getAllHeaders() ));  
                System.out.println("http-filename:"+getFileName(response) );  
    
                //请求成功  
                if(HttpStatus.SC_OK==response.getStatusLine().getStatusCode()){  
    
                    //6.取得请求内容  
                    HttpEntity entity = response.getEntity();  
    
                    if (entity != null) {  
                        //这里可以得到文件的类型 如image/jpg /zip /tiff 等等 但是发现并不是十分有效,有时明明后缀是.rar但是取到的是null,这点特别说明  
                        System.out.println(entity.getContentType());  
                        //可以判断是否是文件数据流  
                        System.out.println(entity.isStreaming());  
    
                        //6.1 输出流
                        //6.1.1获取文件名,拼接文件路径
                        String fileName=getFileName(response);
                        fileDir=fileDir+fileName;
                        file = new File(fileDir);
                        //6.1.2根据文件路径获取输出流
                        FileOutputStream output = new FileOutputStream(file);  
    
                        //6.2 输入流:从钉钉服务器返回的文件流,得到网络资源并写入文件  
                        InputStream input = entity.getContent();  
    
                        //6.3 将数据写入文件:将输入流中的数据写入到输出流
                        byte b[] = new byte[1024];  
                        int j = 0;  
                        while( (j = input.read(b))!=-1){  
                            output.write(b,0,j);  
                        }  
                        output.flush();  
                        output.close();   
                    }  
                    if (entity != null) {  
                        entity.consumeContent();  
                    }  
                }  
            } catch (IOException e) {
                System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
                e.printStackTrace();
            } finally {
                if (response != null) try {
                    response.close();                       //释放资源
    
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return file;
        }
    
    
        /** 5. 获取response header中Content-Disposition中的filename值 
         * @desc :
         *  
         * @param response  响应
         * @return String
         */
        public static String getFileName(HttpResponse response) {  
            Header contentHeader = response.getFirstHeader("Content-Disposition");  
            String filename = null;  
            if (contentHeader != null) {  
                HeaderElement[] values = contentHeader.getElements();  
                if (values.length == 1) {  
                    NameValuePair param = values[0].getParameterByName("filename");  
                    if (param != null) {  
                        try {  
                            //filename = new String(param.getValue().toString().getBytes(), "utf-8");  
                            //filename=URLDecoder.decode(param.getValue(),"utf-8");  
                            filename = param.getValue();  
                        } catch (Exception e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }  
            }  
            return filename;  
        }  
    
    
    
    }
    View Code

    3、接收消息的封装

    3.1 消息基类—BaseMessage

    package com.ray.weixin.gz.model.message.request;  
      
    /**
     *   @desc  : 消息基类(普通用户 -> 公众帐号) 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午10:58:08
     */
    public class BaseMessage {  
        // 开发者微信号  
        private String ToUserName;  
        // 发送方帐号(一个OpenID)  
        private String FromUserName;  
        // 消息创建时间 (整型)  
        private long CreateTime;  
        // 消息类型(text/image/location/link)  
        private String MsgType;  
        // 消息id,64位整型  
        private long MsgId;  
      
        public String getToUserName() {  
            return ToUserName;  
        }  
      
        public void setToUserName(String toUserName) {  
            ToUserName = toUserName;  
        }  
      
        public String getFromUserName() {  
            return FromUserName;  
        }  
      
        public void setFromUserName(String fromUserName) {  
            FromUserName = fromUserName;  
        }  
      
        public long getCreateTime() {  
            return CreateTime;  
        }  
      
        public void setCreateTime(long createTime) {  
            CreateTime = createTime;  
        }  
      
        public String getMsgType() {  
            return MsgType;  
        }  
      
        public void setMsgType(String msgType) {  
            MsgType = msgType;  
        }  
      
        public long getMsgId() {  
            return MsgId;  
        }  
      
        public void setMsgId(long msgId) {  
            MsgId = msgId;  
        }  
    }  
    View Code

    3.2 文本消息—TextMessage

    package com.ray.weixin.gz.model.message.request;  
      
    /**
     * @desc  : 文本消息 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:04:09
     */
    public class TextMessage extends BaseMessage {  
        // 消息内容  
        private String Content;  
      
        public String getContent() {  
            return Content;  
        }  
      
        public void setContent(String content) {  
            Content = content;  
        }  
    }  
    View Code

    3.3 图片消息—ImageMessage

    package com.ray.weixin.gz.model.message.request;  
       
    /**
     * @desc  : 图片消息 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:04:33
     */
    public class ImageMessage extends BaseMessage {  
        // 图片链接  
        private String PicUrl;  
      
        public String getPicUrl() {  
            return PicUrl;  
        }  
      
        public void setPicUrl(String picUrl) {  
            PicUrl = picUrl;  
        }  
    }  
    View Code

    3.4 链接消息—LinkMessage

    package com.ray.weixin.gz.model.message.request;  
      
    /**
     * @desc  :链接消息  
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:05:46
     */
    public class LinkMessage extends BaseMessage {  
        // 消息标题  
        private String Title;  
        // 消息描述  
        private String Description;  
        // 消息链接  
        private String Url;  
      
        public String getTitle() {  
            return Title;  
        }  
      
        public void setTitle(String title) {  
            Title = title;  
        }  
      
        public String getDescription() {  
            return Description;  
        }  
      
        public void setDescription(String description) {  
            Description = description;  
        }  
      
        public String getUrl() {  
            return Url;  
        }  
      
        public void setUrl(String url) {  
            Url = url;  
        }  
    }  
    View Code

    3.5 地理位置消息—LocationMessage

    package com.ray.weixin.gz.model.message.request;  
      
    /**
     * @desc  : 地理位置消息 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:07:39
     */
    public class LocationMessage extends BaseMessage {  
        // 地理位置维度  
        private String Location_X;  
        // 地理位置经度  
        private String Location_Y;  
        // 地图缩放大小  
        private String Scale;  
        // 地理位置信息  
        private String Label;  
      
        public String getLocation_X() {  
            return Location_X;  
        }  
      
        public void setLocation_X(String location_X) {  
            Location_X = location_X;  
        }  
      
        public String getLocation_Y() {  
            return Location_Y;  
        }  
      
        public void setLocation_Y(String location_Y) {  
            Location_Y = location_Y;  
        }  
      
        public String getScale() {  
            return Scale;  
        }  
      
        public void setScale(String scale) {  
            Scale = scale;  
        }  
      
        public String getLabel() {  
            return Label;  
        }  
      
        public void setLabel(String label) {  
            Label = label;  
        }  
    }  
    View Code

    3.6 音频消息—VoiceMessage

    package com.ray.weixin.gz.model.message.request;  
        
    /**
     * @desc  :音频消息  
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:08:25
     */
    public class VoiceMessage extends BaseMessage {  
        // 媒体ID  
        private String MediaId;  
        // 语音格式  
        private String Format;  
      
        public String getMediaId() {  
            return MediaId;  
        }  
      
        public void setMediaId(String mediaId) {  
            MediaId = mediaId;  
        }  
      
        public String getFormat() {  
            return Format;  
        }  
      
        public void setFormat(String format) {  
            Format = format;  
        }  
    }  
    View Code

    4. 回复消息的封装

    4.1 消息基类—BaseMessage

    package com.ray.weixin.gz.model.message.response;  
    
    /**
     * @desc  : 消息基类(公众帐号 -> 普通用户) 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:10:32
     */
    public class BaseMessage {  
        // 接收方帐号(收到的OpenID)  
        private String ToUserName;  
        // 开发者微信号  
        private String FromUserName;  
        // 消息创建时间 (整型)  
        private long CreateTime;  
        // 消息类型(text/music/news)  
        private String MsgType;  
        // 位0x0001被标志时,星标刚收到的消息  
        private int FuncFlag;  
      
        public String getToUserName() {  
            return ToUserName;  
        }  
      
        public void setToUserName(String toUserName) {  
            ToUserName = toUserName;  
        }  
      
        public String getFromUserName() {  
            return FromUserName;  
        }  
      
        public void setFromUserName(String fromUserName) {  
            FromUserName = fromUserName;  
        }  
      
        public long getCreateTime() {  
            return CreateTime;  
        }  
      
        public void setCreateTime(long createTime) {  
            CreateTime = createTime;  
        }  
      
        public String getMsgType() {  
            return MsgType;  
        }  
      
        public void setMsgType(String msgType) {  
            MsgType = msgType;  
        }  
      
        public int getFuncFlag() {  
            return FuncFlag;  
        }  
      
        public void setFuncFlag(int funcFlag) {  
            FuncFlag = funcFlag;  
        }  
    }  
    View Code

    4.2 文本消息—TextMessage

    package com.ray.weixin.gz.model.message.response;  
       
    /**
     * @desc  : 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:10:58
     */
    public class TextMessage extends BaseMessage {  
        // 回复的消息内容  
        private String Content;  
      
        public String getContent() {  
            return Content;  
        }  
      
        public void setContent(String content) {  
            Content = content;  
        }  
    }  
    View Code

    4.3 音乐消息—MusicMessage

    Music

    package com.ray.weixin.gz.model.message.response;  
       
    /**
     * @desc  : 音乐model 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:12:47
     */
    public class Music {  
        // 音乐名称  
        private String Title;  
        // 音乐描述  
        private String Description;  
        // 音乐链接  
        private String MusicUrl;  
        // 高质量音乐链接,WIFI环境优先使用该链接播放音乐  
        private String HQMusicUrl;  
      
        public String getTitle() {  
            return Title;  
        }  
      
        public void setTitle(String title) {  
            Title = title;  
        }  
      
        public String getDescription() {  
            return Description;  
        }  
      
        public void setDescription(String description) {  
            Description = description;  
        }  
      
        public String getMusicUrl() {  
            return MusicUrl;  
        }  
      
        public void setMusicUrl(String musicUrl) {  
            MusicUrl = musicUrl;  
        }  
      
        public String getHQMusicUrl() {  
            return HQMusicUrl;  
        }  
      
        public void setHQMusicUrl(String musicUrl) {  
            HQMusicUrl = musicUrl;  
        }  
      
    }  
    View Code

    MusicMessage

    package com.ray.weixin.gz.model.message.response;  
       
    /**
     * @desc  : 音乐消息 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:12:06
     */
    public class MusicMessage extends BaseMessage {  
        // 音乐  
        private Music Music;  
      
        public Music getMusic() {  
            return Music;  
        }  
      
        public void setMusic(Music music) {  
            Music = music;  
        }  
    }  
    View Code

    4.4 图文消息—NewsMessage

    Article

    package com.ray.weixin.gz.model.message.response;  
      
    /**
     * @desc  :  图文model 
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:15:30
     */
    public class Article {  
        // 图文消息名称  
        private String Title;  
        // 图文消息描述  
        private String Description;  
        // 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80,限制图片链接的域名需要与开发者填写的基本资料中的Url一致  
        private String PicUrl;  
        // 点击图文消息跳转链接  
        private String Url;  
      
        public String getTitle() {  
            return Title;  
        }  
      
        public void setTitle(String title) {  
            Title = title;  
        }  
      
        public String getDescription() {  
            return null == Description ? "" : Description;  
        }  
      
        public void setDescription(String description) {  
            Description = description;  
        }  
      
        public String getPicUrl() {  
            return null == PicUrl ? "" : PicUrl;  
        }  
      
        public void setPicUrl(String picUrl) {  
            PicUrl = picUrl;  
        }  
      
        public String getUrl() {  
            return null == Url ? "" : Url;  
        }  
      
        public void setUrl(String url) {  
            Url = url;  
        }  
      
    }  
    View Code

    NewsMessage

    package com.ray.weixin.gz.model.message.response;  
      
    import java.util.List;  
       
    /**
     * @desc  : 图文消息
     * 
     * @author: shirayner
     * @date  : 2017年11月13日 上午11:13:36
     */
    public class NewsMessage extends BaseMessage {  
        // 图文消息个数,限制为10条以内  
        private int ArticleCount;  
        // 多条图文消息信息,默认第一个item为大图  
        private List<Article> Articles;  
      
        public int getArticleCount() {  
            return ArticleCount;  
        }  
      
        public void setArticleCount(int articleCount) {  
            ArticleCount = articleCount;  
        }  
      
        public List<Article> getArticles() {  
            return Articles;  
        }  
      
        public void setArticles(List<Article> articles) {  
            Articles = articles;  
        }  
    }  
    View Code

    5.接收微信消息和事件—WeiXinServlet.java

    package com.ray.weixin.gz.controller;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import com.qq.weixin.mp.aes.AesException;
    import com.qq.weixin.mp.aes.WXBizMsgCrypt;
    import com.ray.weixin.gz.config.Env;
    import com.ray.weixin.gz.service.message.ReplyMessageService;
    
    
    
    /**
     * Servlet implementation class WeiXinServlet
     */         
    public class WeiXinServlet extends HttpServlet {
        private static final Logger logger = LogManager.getLogger(WeiXinServlet.class);
    
    
    
        private static final long serialVersionUID = 1L;
    
        /**
         * Default constructor. 
         */
        public WeiXinServlet() {
            // TODO Auto-generated constructor stub
        }
    
        //1.接收  回调模式  的请求
        protected void doGet(HttpServletRequest request, HttpServletResponse response)  {
            logger.info("get--------------");
            //一、校验URL
            //1.准备校验参数
            // 微信加密签名  
            String msgSignature = request.getParameter("signature");  
            // 时间戳  
            String timeStamp = request.getParameter("timestamp");  
            // 随机数  
            String nonce = request.getParameter("nonce");  
            // 随机字符串  
            String echoStr = request.getParameter("echostr");  
    
            PrintWriter out=null;
            try {
                //2.校验url
                //2.1 创建加解密类
                WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);
    
                //2.2进行url校验
                //不抛异常就说明校验成功
                String sEchoStr= wxcpt.verifyUrl_WXGZ(msgSignature, Env.TOKEN, timeStamp, nonce,echoStr);
    
                //2.3若校验成功,则原样返回 echoStr
    
                out = response.getWriter(); 
                out.print(sEchoStr);  
    
    
    
    
            } catch (AesException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (out != null) {
                    out.close();  
                    out = null;                       //释放资源
                }
            }
        }
    
        //2.接收  微信消息和事件  的请求
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            logger.info("post--------------");
            //1.将请求、响应的编码均设置为UTF-8(防止中文乱码)  
            request.setCharacterEncoding("UTF-8");  
            response.setCharacterEncoding("UTF-8");  
    
            //2.调用消息业务类接收消息、处理消息  
            String respMessage = ReplyMessageService.reply(request);  
    
            //3.响应消息  
            PrintWriter out = response.getWriter();  
            out.print(respMessage);  
            out.close(); 
    
    
    
        }
    
    }
    View Code

    6.被动回复消息业务类—ReplyMessageService.java

    package com.ray.weixin.gz.service.message;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.Date;
    import java.util.Map;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    import com.alibaba.fastjson.JSON;
    import com.qq.weixin.mp.aes.AesException;
    import com.qq.weixin.mp.aes.WXBizMsgCrypt;
    import com.ray.weixin.gz.config.Env;
    import com.ray.weixin.gz.model.message.response.TextMessage;
    import com.ray.weixin.gz.util.MessageUtil;
    
    
    /**@desc  : 发送消息-被动回复消息业务类
     * Passive reply message
     * @author: shirayner
     * @date  : 2017年10月31日 下午12:24:41
     */
    public class ReplyMessageService {
        private static final Logger logger = LogManager.getLogger(ReplyMessageService.class);
    
        /**
         * @desc :1.回复消息
         *  
         * @param request
         * @return 
         *   String 回复消息的加密xml字符串
         */
        public static String reply( HttpServletRequest request ) {
            String parms=JSON.toJSONString(request.getParameterMap()); 
            logger.info("parms:"+parms);
            //1.解密:从request中获取消息明文
            String xmlMsg=decryptMsg(request);
            logger.info(xmlMsg);
    
            //2.获取回复消息(明文)
            String replyMsg = getReplyMsg( xmlMsg);
    
            //3.根据消息加密方式判断是否加密
            String timeStamp = request.getParameter("timestamp");   // 时间戳    
            String nonce = request.getParameter("nonce");          // 随机数  
            String encryptType=request.getParameter("encrypt_type");
    
            //3.1 安全模式-加密:将回复消息加密
            if(null!=encryptType) {
                WXBizMsgCrypt wxcpt=null;
                try {
                    wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);
                    replyMsg=wxcpt.EncryptMsg(replyMsg, timeStamp, nonce);
    
                } catch (AesException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    
    
    
            return replyMsg;
        }
    
        /**
         * @desc :2.从request中获取消息明文
         *  从request中获取加密消息,将其解密并返回
         * @param request
         * @return String   消息明文
         */
        public static String decryptMsg(HttpServletRequest request) {
    
            String postData="";   // 密文,对应POST请求的数据
            String result="";     // 明文,解密之后的结果
    
            String msgSignature = request.getParameter("msg_signature"); // 微信加密签名  
            String timeStamp = request.getParameter("timestamp");   // 时间戳    
            String nonce = request.getParameter("nonce");          // 随机数  
            String encryptType=request.getParameter("encrypt_type");
    
            try {
                //1.获取加密的请求消息:使用输入流获得加密请求消息postData
                ServletInputStream in = request.getInputStream();
                BufferedReader reader =new BufferedReader(new InputStreamReader(in));  
    
                String tempStr="";   //作为输出字符串的临时串,用于判断是否读取完毕  
                while(null!=(tempStr=reader.readLine())){  
                    postData+=tempStr;  
                }  
    
                logger.info("postData:"+postData);
    
                //2.获取消息明文:对加密的请求消息进行解密获得明文 
                if(null!=encryptType) {
                    logger.info("安全模式:消息被加密");
                    WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(Env.TOKEN,Env.ENCODING_AES_KEY,Env.APP_ID);
                    result=wxcpt.DecryptMsg(msgSignature, timeStamp, nonce, postData);
                }else {
                    logger.info("明文模式");
                    result=postData;
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } catch (AesException e) {
                e.printStackTrace();
            }  
    
            return result;
        }
    
        /**
         * @desc :获取回复消息
         *  
         * @param request
         * @return String  返回加密后的回复消息
         */
        public static String getReplyMsg(String xmlMsg){
            String replyMsg = null; 
    
            try {
                //2.解析微信发来的请求,解析xml字符串
                Map<String, String> requestMap= MessageUtil.parseXml(xmlMsg);    
    
                //3.获取请求参数
                //3.1 企业微信CorpID  
                String fromUserName = requestMap.get("FromUserName");  
                //3.2 成员UserID
                String toUserName = requestMap.get("ToUserName");  
                //3.3 消息类型与事件 
                String msgType = requestMap.get("MsgType"); 
                String eventType = requestMap.get("Event");  
                String eventKey = requestMap.get("EventKey"); 
                logger.info("fromUserName:"+fromUserName);
                logger.info("toUserName:"+toUserName);
                logger.info("msgType:"+msgType);
                logger.info("Event:"+eventType+"  eventKey:"+eventKey);
    
                //4.组装 回复文本消息  
                TextMessage textMessage = new TextMessage();  
                textMessage.setToUserName(fromUserName);  
                textMessage.setFromUserName(toUserName);  
                textMessage.setCreateTime(new Date().getTime());  
                textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);  
                //4.1.获取回复消息的内容 :消息的分类处理
                String replyContent=getReplyContentByMsgType(msgType, eventType, eventKey);
                textMessage.setContent(replyContent);  
                System.out.println("replyContent:"+replyContent);
    
                //5.获取xml字符串: 将(被动回复消息型的)文本消息对象 转成  xml字符串
                replyMsg = MessageUtil.textMessageToXml(textMessage); 
    
    
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }  
    
            return replyMsg;
        }
    
    
        /**
         * @desc :3.处理消息:根据消息类型获取回复内容
         *  
         * @param msgType 消息类型
         * @return String 回复内容
         */
        public static  String getReplyContentByMsgType(String msgType,String eventType,String eventKey){
            String replyContent="";
            //1.文本消息  
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {  
                replyContent = "您发送的是文本消息!";  
    
            }  
            //2.图片消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {  
                replyContent = "您发送的是图片消息!";  
            }  
            //3.地理位置消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) { 
    
                replyContent = "您发送的是地理位置消息 !";  
            }  
            //4.链接消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {  
                replyContent = "您发送的是链接消息!";  
            }  
            //5.音频消息  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {  
                replyContent = "您发送的是音频消息!";  
            }
            //6.事件推送  
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) { 
                replyContent=getReplyContentByEventType(eventType, eventKey);
            }
            //7.请求异常
            else {
                replyContent="请求处理异常,请稍候尝试!";
            }  
    
            return replyContent;
        }
    
        /**
         * @desc :5.处理消息:根据事件类型获取回复内容
         *  
         * @param eventType  事件类型
         * @param eventKey  事件key值
         * @return 
         *   String
         */
        public static String getReplyContentByEventType(String eventType,String eventKey){
    
            String respContent="";
            // 订阅  
            if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {  
                respContent = "欢迎关注!";  
            }  
            // 取消订阅  
            else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {  
                // TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息  
            } 
            //上报地理位置事件
            else if(eventType.equals("LOCATION")){
    
            }
            // 自定义菜单点击事件  
            else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {  
    
                if (eventKey.equals("12")) {  
    
                } else if (eventKey.equals("13")) {  
                    respContent = "周边搜索菜单项被点击!";  
                } else if (eventKey.equals("14")) {  
                    respContent = "历史上的今天菜单项被点击!";  
                } else if (eventKey.equals("21")) {  
                    respContent = "歌曲点播菜单项被点击!";  
                } else if (eventKey.equals("22")) {  
    
                    respContent = "经典游戏菜单项被点击!";  
                } else if (eventKey.equals("23")) {  
                    respContent = "美女电台菜单项被点击!";  
                } else if (eventKey.equals("24")) {  
                    respContent = "人脸识别菜单项被点击!";  
                } else if (eventKey.equals("25")) {  
                    respContent = "聊天唠嗑菜单项被点击!";  
                } else if (eventKey.equals("31")) {  
                    respContent = "Q友圈菜单项被点击!";  
                } else if (eventKey.equals("32")) {  
                    respContent = "电影排行榜菜单项被点击!";  
                } else if (eventKey.equals("33")) {  
                    respContent = "幽默笑话菜单项被点击!";  
                }  
            } 
            return respContent;
        }  
    
    }
    View Code

    三、参考资料

    1.微信公众平台技术文档

    2.柳峰—微信公众帐号开发教程第5篇-各种消息的接收与响应

  • 相关阅读:
    stm32f103 SPI单线TX发数据来驱动LCD
    【转】常见排序算法
    [转]命令行 Subversion 入门
    JLINK V8 Keil MDK4.10 STM32
    字符串表示的大整数相加
    字符串反转
    字符串表示的大整数相乘
    猴子选大王
    [转]Posix-- 互斥锁 条件变量 信号量
    [转]Openwrt的Inittab
  • 原文地址:https://www.cnblogs.com/shirui/p/7825481.html
Copyright © 2011-2022 走看看