• 微信公众号02 接收文本消息、回复文本消息、接入百度翻译功能


    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);
    
        }
    View Code

     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;
        }
    View Code

    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);
    
        }
    
    }
    Md5Utils.java

    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;
        }
    }
    HttpMethodUtils.java

    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 = '	';
                        else if (aChar == 'r')
                            aChar = '
    ';
                        else if (aChar == 'n')
                            aChar = '
    ';
                        else if (aChar == 'f')
                            aChar = 'f';
                        outBuffer.append(aChar);
                    }
                } else
                    outBuffer.append(aChar);
            }
            return outBuffer.toString();
        }
    
    
    }
    TransformUtils.java

    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;
    }
    TranslationResponse.java
    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;
    }
    TransResult.java

    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;
        }
    
    
    
    }
    BaiduTransApi.java

    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 启动应用,测试效果如下

    扫码获取源代码

     

     

  • 相关阅读:
    Virtuabox 虚拟机克隆方法
    CentOS 7 防火墙 出现Failed to start iptables.service: Unit iptables.service failed to load
    Linux系统下安装rz/sz命令及使用说明
    os、sys模块
    collections、random、hashlib、configparser、logging模块
    time、datatime模块
    正则表达式、re模块
    递归、二分查找法
    内置函数、匿名函数
    生成器进阶、生成器表达式
  • 原文地址:https://www.cnblogs.com/NeverCtrl-C/p/10247498.html
Copyright © 2020-2023  润新知