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篇-各種消息的接收與響應

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM