1 說明
本篇博文承接上一篇博文:https://www.cnblogs.com/NeverCtrl-C/p/10241763.html
2 接收文本消息
微信公眾號官方文檔:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140453
說明:接收文本消息屬於接收普通消息的范圍,當公眾號粉絲向公眾號發送消息時,微信服務器會向開發者服務器發送一個POST請求,這個POST請求攜帶XML格式的數據包到開發者填寫的URL上
2.1 文本消息XML格式
<xml> <ToUserName>< ![CDATA[toUser] ]></ToUserName> <FromUserName>< ![CDATA[fromUser] ]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType>< ![CDATA[text] ]></MsgType> <Content>< ![CDATA[this is a test] ]></Content> <MsgId>1234567890123456</MsgId> </xml>
2.2 文本消息參數說明
參數 | 描述 |
---|---|
ToUserName | 開發者微信號 |
FromUserName | 發送方帳號(一個OpenID) |
CreateTime | 消息創建時間 (整型) |
MsgType | text |
Content | 文本消息內容 |
MsgId | 消息id,64位整型 |
step01 創建一個com.xunyji.xunyjitest.comm.TransformUtils類用來存放一下數據類型轉換相關的工具方法2.3 Java代碼實現
step02 引入XML和對象相互轉換相關的jar包
<!--xml2對象 start--> <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <!--xml2對象 end--> <!--對象2XML start--> <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream --> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.3.1</version> </dependency> <!--對象2XML end-->
step03 創建com.xunyji.xunyjitest.comm.TransformUtils#xml2Map方法用於將XML轉化成Map類型
/** * xml轉換成map【微信平台請求開發者平台時的數據格式都是XML格式的】 * @param request * @return * @throws IOException * @throws DocumentException */ public static Map<String, String> xml2Map(HttpServletRequest request) throws IOException, DocumentException { // 01 定義Map對象用來存放數據 Map<String, String> map = new HashMap<>(); // 02 創建SAXReader用於讀取xml文件 SAXReader reader = new SAXReader(); // 03 讀取Request中的信息 InputStream ins = request.getInputStream(); Document doc = reader.read(ins); // 04 獲取xml的根元素 Element root = doc.getRootElement(); // 05 獲取根元素中的所有子元素 List<Element> list = root.elements(); // 06 遍歷所有子元素並將數據存放到定義好的集合中 for (Element e : list) { map.put(e.getName(), e.getText()); } // 07 關閉輸入流 ins.close(); // 08 返回存放xml內容的Map對象 return map; }
step04 創建一個com.xunyji.xunyjitest.web.weixin.WeixinController#receiveMessage方法用於接收微信平台發送的POST請求
step0401 該方法接收POST請求
step0402 通過 HttpServletRequest 對象獲取微信平台傳過來的XML數據包
step0403 將XML數據轉化成Map數據並打印輸出
@PostMapping public void receiveMessage(HttpServletRequest request, HttpServletResponse response) throws IOException, DocumentException { // 01 請求、響應編碼格式設定 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); // 02 獲取輸出對象 PrintWriter out = response.getWriter(); // 03 獲取微信平台傳過來的請求參數【PS:默認是XML格式的,這里轉化成了Mapl類型】 Map<String, String> receiveMap = TransformUtils.xml2Map(request); log.info("接收微信消息時獲取到的信息為:" + receiveMap); // 04 從Map對象中獲取單個數據 String fromUserName = receiveMap.get("FromUserName"); String toUserName = receiveMap.get("ToUserName"); String msgType = receiveMap.get("MsgType"); String content = receiveMap.get("Content"); }
step0404 啟動項目並通過粉絲賬戶向公眾號發送文本消息,效果如圖所示
3 回復文本消息
回復文本消息屬於被動回復消息的范圍,微信官網提供的被動回復消息文檔:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543
技巧01:粉絲向公眾號發送消息時微信服務器會向開發者服務器發送一個POST請求,開發者可以從這個POST請求中獲取到一些信息,也可以像發送者返回一些信息
3.1 回復文本消息XML格式
<xml> <ToUserName>< ![CDATA[toUser] ]></ToUserName> <FromUserName>< ![CDATA[fromUser] ]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType>< ![CDATA[text] ]></MsgType> <Content>< ![CDATA[你好] ]></Content> </xml>
3.2 回復文本消息參數說明
參數 | 是否必須 | 說明 |
---|---|---|
ToUserName | 是 | 接收方帳號(收到的OpenID) |
FromUserName | 是 | 開發者微信號 |
CreateTime | 是 | 消息創建時間 (整型) |
MsgType | 是 | image |
MediaId | 是 | 通過素材管理中的接口上傳多媒體文件,得到的id。 |
3.3 Java代碼實現
step01 創建一個com.xunyji.xunyjitest.model.weixin.send.SendBaseMessage類作為所有被動回復消息的基類
step02 創建一個com.xunyji.xunyjitest.model.weixin.send.ReplyTextMessage類作為回復文本消息的實體類
@Data @NoArgsConstructor @AllArgsConstructor public class ReplyTextMessage extends ReplyBaseMessage { /** 回復的消息內容(換行:在content中能夠換行,微信客戶端就支持換行顯示) */ private String Content; }
step03 創建一個com.xunyji.xunyjitest.comm.enums.weixin.MessageTypeEnum枚舉類來存放各種消息類型
@Getter public enum MessageTypeEnum { // 接收普通消息類型【0開頭表示接收的消息類型】 start /** 被動回復文本消息 */ RECEIVE_MESSAGE_TEXT(001, "text"), RECEIVE_MESSAGE_IMAGE(002, "image"), RECEIVE_MESSAGE_VOICE(003, "voice"), RECEIVE_MESSAGE_LOCATION(004, "location"), RECEIVE_MESSAGE_LINK(005, "link"), RECEIVE_MESSAGE_SHORTVIDEO(06, "shortvideo"), RECEIVE_MESSAGE_VIDEO(007, "video"), // 接收普通消息類型【0開頭表示接收的消息類型】 end ; /** 消息類型編號 */ private Integer code; /** 消息類型(和微信文檔中保持一致) */ private String type; MessageTypeEnum(Integer code, String type) { this.code = code; this.type = type; } }
step04 創建一個com.xunyji.xunyjitest.comm.util.weixin.MessageUtils類作為各種消息封裝類
step0401 創建com.xunyji.xunyjitest.comm.util.weixin.MessageUtils#replyTextMessageToXml方法實現將ReplyTextMessage 轉化成 XML
/** * ReplyTextMessage 轉化成 XML * @param replyTextMessage * @return */ private String replyTextMessageToXml(ReplyTextMessage replyTextMessage) { XStream xStream = new XStream(); xStream.alias("xml", replyTextMessage.getClass()); return xStream.toXML(replyTextMessage); }
step0402 創建com.xunyji.xunyjitest.comm.util.weixin.MessageUtils#initReplyTextMessage方法實現封裝回復文本消息時所需的XML格式字符串
/** * 封裝XML格式的"發送文本消息" * @param fromUserName 粉絲appId * @param toUserName 公眾號appId * @param content XML格式的字符串 * @return */ public String initReplyTextMessage(String fromUserName, String toUserName, String content) { ReplyTextMessage text = new ReplyTextMessage(); text.setToUserName(fromUserName); text.setFromUserName(toUserName); text.setMsgType(MessageTypeEnum.RECEIVE_MESSAGE_TEXT.getType()); long time = System.currentTimeMillis(); text.setCreateTime(String.valueOf(time)); text.setContent("逆向公眾號發送了:" + content); return replyTextMessageToXml(text); }
step05 重構com.xunyji.xunyjitest.web.weixin.WeixinController#receiveMessage方法實現接收文本消息時回復文本消息,其他消息不做任何處理
step0501 從封裝了請求數據的map集合中獲取公眾號appid(toUserName)、粉絲appid(fromUserName)、接收消息類型(msgType)
step0502 判斷接收消息類型並做相應處理,此處以接收到的消息類型為文本消息為例
如果是文本消息就獲取文本內容,然后封裝響應數據即可
step0503 必須對響應數據進行非空處理,因為如果響應數據為null就會出現一個錯誤,很影響用戶體驗

@PostMapping public void receiveMessage(HttpServletRequest request, HttpServletResponse response) throws IOException, DocumentException { // 01 請求、響應編碼格式設定 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); // 02 獲取輸出對象 PrintWriter out = response.getWriter(); // 03 獲取微信平台傳過來的請求參數【PS:默認是XML格式的,這里轉化成了Mapl類型】 Map<String, String> receiveMap = TransformUtils.xml2Map(request); log.info("接收微信消息時獲取到的信息為:" + receiveMap); // 04 從Map對象中獲取單個數據 String fromUserName = receiveMap.get("FromUserName"); String toUserName = receiveMap.get("ToUserName"); String msgType = receiveMap.get("MsgType"); // 05 用於存放String類型的XML格式響應數據 String message = null; // 06 如果接收的消息類型是text類型的處理邏輯 if (MessageTypeEnum.RECEIVE_MESSAGE_TEXT.getType().equals(msgType)) { String content = receiveMap.get("Content"); message = new MessageUtils().initReplyTextMessage(fromUserName, toUserName, content); } // 07 響應對象非空處理,如果返回null會報異常(影響用戶體驗) if (message == null) { message = ""; } // 08 打印XML格式的響應消息 log.info("被動回復消息的XML格式為:" + message); // 09 響應XML格式數據給微信服務器 out.print(message); }
step06 啟動項目並向公眾號發送文本消息,效果如圖所示
3.4 代碼重構
應該根據不同的消息類型調用不同的服務層方法來實現業務邏輯,不應該將所有業務邏輯都放到controller層中
step01 引入fastjson依賴,因為需要將Map轉化成Bean
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.54</version> </dependency>
step02 創建com.xunyji.xunyjitest.service.ReceiveMessageService接口用於定義處理各種接收到的消息
step03 創建com.xunyji.xunyjitest.service.ReceiveMessageServiceImpl實現了實現處理各種接收到的消息
step04 創建com.xunyji.xunyjitest.comm.TransformUtils#parseMap2Object方法用於將Map轉化成Bean
/** * Map類型轉化成指定類型 * @param paramMap 待轉化的Map對象 * @param clazz 目標類型的類類型 * @param <T> 目標類型 * @return */ public static <T> T parseMap2Object(Map<String, String> paramMap, Class<T> clazz) { return JSONObject.parseObject(JSONObject.toJSONString(paramMap), clazz); }
step05 創建com.xunyji.xunyjitest.service.ReceiveMessageServiceImpl#textMessageHandler方法用於處理文本類型消息

@Override public String textMessageHandler(Map<String, String> receiveParam) { // 01 用於存放封裝好的回復文本消息的XML數據 String message = null; // 02 將Map類型參數轉化成ReceiveTextMessage類型 ReceiveTextMessage receiveTextMessage = TransformUtils.parseMap2Object(receiveParam, ReceiveTextMessage.class); log.info("接收到的text消息請求參數為:" + receiveTextMessage); // 03 獲取文本內容和雙方信息 // 0301 粉絲appId String fromUserName = receiveTextMessage.getFromUserName(); // 0302 公眾號appId String toUserName = receiveTextMessage.getToUserName(); // 0303 接收到的文本內容 String content = receiveTextMessage.getContent(); content = content.trim(); // 04 根據文本內容響應不同的數據 TODO: 應該將接收到的消息信息存儲到數據庫或者緩存中 if ("1".equals(content)) { message = messageUtils.initReplyTextMessage(fromUserName, toUserName, messageUtils.firstMenu()); } else if ("2".equals(content)) { message = messageUtils.initReplyTextMessage(fromUserName, toUserName, messageUtils.secondMenu()); } else if ("?".equals(content) || "?".equals(content)) { message = messageUtils.initReplyTextMessage(fromUserName, toUserName, messageUtils.menuText()); } else { message = messageUtils.initReplyTextMessage(fromUserName, toUserName, content); } return message; }
step06 在com.xunyji.xunyjitest.web.weixin.WeixinController#receiveMessage方法中,如果是文本消息類型就調用com.xunyji.xunyjitest.service.ReceiveMessageService#textMessageHandler實現業務處理
step07 啟動應用並以此發送 1、2、?、?,效果圖如下:
4 接入百度翻譯功能
4.1 百度翻譯開放平台
官網地址:http://api.fanyi.baidu.com/api/trans/product/index
說明:進入到百度翻譯平台官網過后需要注冊一個開發者賬號,注冊的時候填入相關信息即可
4.2 通用翻譯API技術文檔
官網文檔地址:http://api.fanyi.baidu.com/api/trans/product/apidoc
技巧01:百度通用翻譯API提供了URL,開發者只需要根據文檔說明訪問這個URL即可
step01 通用翻譯API的地址:http://api.fanyi.baidu.com/api/trans/vip/translate 或者 https://fanyi-api.baidu.com/api/trans/vip/translate
step02 可以向兩個url地址發送GET請求或者POST請求,但是必須攜帶如下參數(PS:是指請求參數,即url后面跟的那種參數)
技巧01:為保證翻譯質量,請將單次請求長度控制在 6000 bytes以內。(漢字約為2000個)
字段名 | 類型 | 必填參數 | 描述 | 備注 |
---|---|---|---|---|
q | TEXT | Y | 請求翻譯query | UTF-8編碼 |
from | TEXT | Y | 翻譯源語言 | 語言列表(可設置為auto) |
to | TEXT | Y | 譯文語言 | 語言列表(不可設置為auto) |
appid | INT | Y | APP ID | 可在管理控制台查看 |
salt | INT | Y | 隨機數 | |
sign | TEXT | Y | 簽名 | appid+q+salt+密鑰 的MD5值 |
step03 簽名生成規則
step0301 將請求參數中的 APPID(appid), 翻譯query(q, 注意為UTF-8編碼), 隨機數(salt), 以及平台分配的密鑰(可在管理控制台查看)
按照 appid+q+salt+密鑰 的順序拼接得到字符串1。
step0302 對字符串1做md5,得到32位小寫的sign(PS:簽名是為了保證調用安全,使用MD5算法生成的一段字符串,生成的簽名長度為 32位,簽名中的英文字符均為小寫格式)
step04 注意事項
step0401 請先將需要翻譯的文本轉換為UTF-8編碼
step0402 在發送HTTP請求之前需要對各字段做URL encode
step0403 在生成簽名拼接 appid+q+salt+密鑰 字符串時,q不需要做URL encode,在生成簽名之后,發送HTTP請求之前才需要對要發送的待翻譯文本字段q做URL encode。
step05 響應的JSON格式說明
字段名 | 類型 | 描述 |
---|---|---|
from | TEXT | 翻譯源語言 |
to | TEXT | 譯文語言 |
trans_result | MIXED LIST | 翻譯結果 |
src | TEXT | 原文 |
dst | TEXT | 譯文 |
4.3 官方Demo實現
step01 官方demo下載地址:https://fanyiapp.cdn.bcebos.com/api/demo/java.zip ,官方demo文件說明如下圖所示
step02 將這個三個文件拷貝到項目中
step03 測試官方提供的demo
4.4 自定義翻譯demo
說明:百度翻譯官方提供的demo中使用的是java提供的HttpURLConnection進行遠程調用的,而我比較喜歡用RestTemplate實現第三方的服務調用;該系列文章之后的文章中調用你微信公眾號平台的相關url時也是使用RestTemplate進行調用,所以這里創建一個由RestTemplate實現的HTTP工具類
step01 創建一個com.xunyji.xunyjitest.config.CreatBeanConfig類來創建項目所需的Bean
技巧01:需要在類級別添加 @Configuration 來指明該類是一個配置類,@Configuration 標注的類相當於一個XML配置類
step02 創建com.xunyji.testdemo.comm.config.CreatBeanConfig#restTemplate方法來創建RestTemplate對應的Bean
技巧01:注意亂碼問題,參考博文 -> https://blog.csdn.net/papamilk/article/details/80000683
坑01:解決亂碼問題時需要使用到HttpClient相關的jar包
<!--httpclient start--> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.6</version> </dependency> <!--httpclient end-->
step03 在application-dev.yml文件中配置百度翻譯所需的賬號和url信息
step04 創建相關實體類來讀取step03里面配置的信息,這里的配置有三層,所以需要兩個百度翻譯相關的配置實體類
step05 創建MD5加密工具類com.xunyji.xunyjitest.comm.util.Md5Utils

package com.xunyji.testdemo.util; import java.io.*; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * @author AltEnter * @create 2019-01-09 10:13 * @desc MD5編碼工具類 **/ public class Md5Utils { // 首先初始化一個字符數組,用來存放每個16進制字符 private static final char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * 獲得一個字符串的MD5值 * @param input 輸入的字符串 * @return 輸入字符串的MD5值 * */ public static String md5(String input) { if (input == null) { return null; } try { // 拿到一個MD5轉換器(如果想要SHA1參數換成”SHA1”) MessageDigest messageDigest = MessageDigest.getInstance("MD5"); // 輸入的字符串轉換成字節數組 byte[] inputByteArray = input.getBytes("utf-8"); // inputByteArray是輸入字符串轉換得到的字節數組 messageDigest.update(inputByteArray); // 轉換並返回結果,也是字節數組,包含16個元素 byte[] resultByteArray = messageDigest.digest(); // 字符數組轉換成字符串返回 return byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) { return null; } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } /** * 獲取文件的MD5值 * * @param file * @return */ public static String md5(File file) { try { if (!file.isFile()) { System.err.println("文件" + file.getAbsolutePath() + "不存在或者不是文件"); return null; } FileInputStream in = new FileInputStream(file); String result = md5(in); in.close(); return result; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 獲取一個輸入流的MD5值 * @param in * @return */ public static String md5(InputStream in) { try { MessageDigest messagedigest = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[1024]; int read = 0; while ((read = in.read(buffer)) != -1) { messagedigest.update(buffer, 0, read); } in.close(); String result = byteArrayToHex(messagedigest.digest()); return result; } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 獲取一個字節數組的MD5值 * @param byteArray * @return */ private static String byteArrayToHex(byte[] byteArray) { // new一個字符數組,這個就是用來組成結果字符串的(解釋一下:一個byte是八位二進制,也就是2位十六進制字符(2的8次方等於16的2次方)) char[] resultCharArray = new char[byteArray.length * 2]; // 遍歷字節數組,通過位運算(位運算效率高),轉換成字符放到字符數組中去 int index = 0; for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b >>> 4 & 0xf]; resultCharArray[index++] = hexDigits[b & 0xf]; } // 字符數組組合成字符串返回 return new String(resultCharArray); } }
step06 創建HTTP請求方法工具類com.xunyji.xunyjitest.comm.util.HttpMethodUtils

package com.xunyji.xunyjitest.comm.util; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map; /** * @author AltEnter * @create 2019-01-10 21:20 * @desc HTTP請求方法工具類 **/ @Component @Slf4j public class HttpMethodUtils { @Autowired private RestTemplate restTemplate; /** * get請求方法封裝【利用RestTemplate實現】 * @param url * @return */ public String doGetStrByRestTemplate(String url) { String result = null; result = restTemplate.getForObject(url, String.class); return result; } /** * get請求方法封裝【利用RestTemplate實現】 * @param url * @return */ public Map doGetMapByRestTemplate(String url) { Map result = null; // RestTemplate restTemplate = new RestTemplate(); result = restTemplate.getForObject(url, Map.class); return result; } /** * get請求方法封裝【利用RestTemplate實現】 * @param url 請求路徑【不帶參數的和帶參數的url都可以】 * @param params 請求路徑參數 * @return */ public String doGetStrWithMapParamByRestTemplate(String url, Map<String, String> params) throws UnsupportedEncodingException { String result = null; String query = URLEncoder.encode(params.get("q"), "UTF-8"); log.info("加密前請求參數為:" + params); // 封裝get請求的完整url String sendUrl = getUrlWithQueryString(url, params); log.info("參數加密后的請求URL為:" + sendUrl); result = restTemplate.getForObject(sendUrl, String.class); return result; } /** * get請求方法封裝【利用RestTemplate實現】 * @param url * @return */ public Map doGetMapWithMapByRestTemplate(String url, Map<String, String> params) throws UnsupportedEncodingException { Map result = null; String query = URLEncoder.encode(params.get("q"), "UTF-8"); log.info("加密前請求參數為:" + params); // 封裝get請求的完整url String sendUrl = getUrlWithQueryString(url, params); log.info("加密后請求參數為:" + sendUrl); result = restTemplate.getForObject(sendUrl, Map.class); return result; } /** * 將不帶參數的url和Map類型的參數封裝成一個帶參數的url * @param url * @param params * @return */ private static String getUrlWithQueryString(String url, Map<String, String> params) { if (params == null) { return url; } StringBuilder builder = new StringBuilder(url); if (url.contains("?")) { builder.append("&"); } else { builder.append("?"); } int i = 0; for (String key : params.keySet()) { String value = params.get(key); // 過濾空的key if (value == null) { continue; } if (i != 0) { builder.append('&'); } builder.append(key); builder.append('='); // builder.append(encode(value)); // 如果使用RestTemplate請求時就不需要進行UrlEncode進行加密了,加密了反而會報錯 builder.append(value); i++; } return builder.toString(); } /** * 對輸入的字符串進行URL編碼, 即轉換為%20這種形式 * * @param input 原文 * @return URL編碼. 如果編碼失敗, 則返回原文 */ public static String encode(String input) { if (input == null) { return ""; } try { return URLEncoder.encode(input, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return input; } }
step07 創建對象互轉工具類 com.xunyji.xunyjitest.comm.util.TransformUtils

package com.xunyji.xunyjitest.comm.util; import com.alibaba.fastjson.JSONObject; import java.util.Map; /** * @author AltEnter * @create 2019-01-07 8:37 * @desc 各種類型轉化工具類 **/ public class TransformUtils { /** * Map類型轉化成指定類型 * @param paramMap 待轉化的Map對象 * @param clazz 目標類型的類類型 * @param <T> 目標類型 * @return */ public static <T> T parseMap2Object(Map<String, String> paramMap, Class<T> clazz) { return JSONObject.parseObject(JSONObject.toJSONString(paramMap), clazz); } /** * String類型轉化成指定類型 * @param str 帶轉化的String對象 * @param clazz 目標類型的類類型 * @param <T> 目標類型 * @return */ public static <T> T parseStr2Object(String str, Class<T> clazz) { return JSONObject.parseObject(str, clazz); } /** * unicode 2 utf * @param theString * @return String */ public static String unicodeToUtf8(String theString) { char aChar; int len = theString.length(); StringBuffer outBuffer = new StringBuffer(len); for (int x = 0; x < len;) { aChar = theString.charAt(x++); if (aChar == '\\') { aChar = theString.charAt(x++); if (aChar == 'u') { // Read the xxxx int value = 0; for (int i = 0; i < 4; i++) { aChar = theString.charAt(x++); switch (aChar) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = (value << 4) + aChar - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': value = (value << 4) + 10 + aChar - 'a'; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': value = (value << 4) + 10 + aChar - 'A'; break; default: throw new IllegalArgumentException( "Malformed \\uxxxx encoding."); } } outBuffer.append((char) value); } else { if (aChar == 't') aChar = '\t'; else if (aChar == 'r') aChar = '\r'; else if (aChar == 'n') aChar = '\n'; else if (aChar == 'f') aChar = '\f'; outBuffer.append(aChar); } } else outBuffer.append(aChar); } return outBuffer.toString(); } }
step08 創建百度翻譯響應實體類com.xunyji.xunyjitest.model.baidu.TranslationResponse com.xunyji.xunyjitest.model.baidu.TransResult

package com.xunyji.xunyjitest.model.baidu; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * @author AltEnter * @create 2019-01-09 16:07 * @desc 翻譯請求響應數據封裝實體類 **/ @Data @NoArgsConstructor @AllArgsConstructor @Builder public class TranslationResponse { /** 翻譯源語言 */ private String from; /** 譯文語言 */ private String to; /** 翻譯結果 */ private TransResult[] trans_result; }

package com.xunyji.xunyjitest.model.baidu; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * @author AltEnter * @create 2019-01-09 16:09 * @desc 翻譯結果 **/ @Data @NoArgsConstructor @AllArgsConstructor @Builder public class TransResult { /** 翻譯源文本 */ private String src; /** 譯文文本 */ private String dst; }
step09 創建百度翻譯API封裝類com.xunyji.xunyjitest.comm.util.BaiduTransApi

package com.xunyji.xunyjitest.comm.util; import com.xunyji.xunyjitest.config.baidu.BaiduBaseConfig; import com.xunyji.xunyjitest.model.baidu.TransResult; import com.xunyji.xunyjitest.model.baidu.TranslationResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; /** * @author AltEnter * @create 2019-01-09 10:28 * @desc 百度翻譯API封裝 **/ @Component @Slf4j public class BaiduTransApi { @Autowired private HttpMethodUtils httpMethodUtils; @Autowired private BaiduBaseConfig baiduBaseConfig; /** * 發送get請求進行翻譯 * @param query * @param from * @param to * @return */ public String getTransStrResult(String query, String from, String to) throws UnsupportedEncodingException { Map<String, String> params = buildParams(query, from, to); // 調用自定義的get方法 String restStr = httpMethodUtils.doGetStrWithMapParamByRestTemplate(baiduBaseConfig.getTranslation().getTransApiHost(), params); String result = TransformUtils.unicodeToUtf8(restStr); TranslationResponse translationResponse = TransformUtils.parseStr2Object(result, TranslationResponse.class); log.info("翻譯請求結果為:" + translationResponse.toString()); TransResult transResult = translationResponse.getTrans_result()[0]; return String.format("%s -> %s", transResult.getSrc(), transResult.getDst()); // 調用官方提供的get方法 // return HttpGet.get(baiduBaseConfig.getTranslation().getTransApiHost(), params); } public Map<String, String> getTransMapResult(String query, String from, String to) throws UnsupportedEncodingException { Map<String, String> params = buildParams(query, from, to); // 調用自定義的get方法 Map<String, String> restMap = httpMethodUtils.doGetMapWithMapByRestTemplate(baiduBaseConfig.getTranslation().getTransApiHost(), params); String trans_result = restMap.get("trans_result"); return restMap; // 調用官方提供的get方法 // return HttpGet.get(baiduBaseConfig.getTranslation().getTransApiHost(), params); } /** * 構建請求參數 * @param query * @param from * @param to * @return */ private Map<String, String> buildParams(String query, String from, String to) { String appid = baiduBaseConfig.getTranslation().getAppId(); String securityKey = baiduBaseConfig.getTranslation().getSecurityKey(); // String appid = "20190109000255665"; // String securityKey = "iNPVhM9qtM3Kb0ZTesI9"; Map<String, String> params = new HashMap<String, String>(); params.put("q", query); params.put("from", from); params.put("to", to); params.put("appid", appid); // 隨機數 String salt = String.valueOf(System.currentTimeMillis()); params.put("salt", salt); // 簽名 String src = appid + query + salt + securityKey; // 加密前的原文 log.info("加密前的簽名:" + src); String sign = Md5Utils.md5(src); params.put("sign", sign); log.info("加密后的簽名:" + sign); return params; } }
step10 調用com.xunyji.xunyjitest.comm.util.BaiduTransApi#getTransStrResult進行測試即可
坑01:利用restTemplate調用百度翻譯平台的url是不需要對參數進行urlEncoder加密,否則會報錯:sign錯誤
4.5 接入百度翻譯
step01 翻譯文本格式說明 ->需要翻譯的內容也是text類型額數據,翻譯文本內容的格式如下
en翻譯足球 -> 表示將'足球'翻譯成英文
zh翻譯football -> 表示將'football'翻譯成中文
zh翻譯 -> 調出此菜單
en翻譯 -> 調出此菜單
step02 創建com.xunyji.testdemo.util.weixin.MessageUtil#threeMenu方法作為翻譯格式的提示信息封裝方法
step03 創建com.xunyji.testdemo.util.weixin.BaiduTransApi#getTransStrResult方法來對百度翻譯api進行再一次封裝
step04 重構com.xunyji.xunyjitest.service.ReceiveMessageServiceImpl#textMessageHandler中的邏輯來實現翻譯功能
step05 啟動應用,測試效果如下
掃碼獲取源代碼