springboot开发微信公众号(二)接收并使用机器人回复公众号消息(附源码)

1.接收公众号消息需要在公网,所以开发测试时需要准备内网转公网域名的插件natapp 
2.下载地址https://natapp.cn/,natapp官网教程https://natapp.cn/article/natapp_newbie
3.接收公众号消息之前需要验证我们在公众号平台上填写的token, http://3axbzd.natappfree.cc是natapp公网映射的本地8080端口域名,wx/check为本地接口URL,token值随便填写,但是验证时需要与本地相同

4.查看消息接口使用指南,其中token是在页面填写的token

springboot开发微信公众号(二)接收并使用机器人回复公众号消息(附源码)_第1张图片

5.对token、timestamp、nonce三个字段进行字典排序和加密算法然后进行验证

/*
     * @Author 
     * @Description //1)将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
     * @Date 13:34 2020/7/23
     * @Param [signature, timestamp, nonce, token]
     * @return {@link boolean}
     **/
    public boolean checkSignature(String signature, String timestamp, String nonce,String token){
        try {
            String shaToken=dictionarySorting(timestamp,nonce,token);
            if(signature.equals(shaEncode(shaToken))){
                return true;
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        return false;
    }
    /*
     * @Author 
     * @Description //字典排序
     * @Date 13:32 2020/7/23
     * @Param [token, timestamp, nonce]
     * @return {@link java.lang.String}
     **/
    public String dictionarySorting(String token,String timestamp,String nonce){
        String[] shaToken = new String[] { token, timestamp, nonce };
        Arrays.sort(shaToken);
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < shaToken.length; i++) {
            stringBuilder.append(shaToken[i]);
        }
        return stringBuilder.toString();
    }
    /*
     * @Author 
     * @Description //sha1加密算法
     * @Date 13:25 2020/7/23
     * @Param [inStr]
     * @return {@link java.lang.String}
     **/
    public static String shaEncode(String inStr) throws Exception {
        MessageDigest sha = null;
        try {
            sha = MessageDigest.getInstance("SHA-1");
        } catch (Exception e) {
            log.info(e.getMessage(),e);
            return "";
        }
        byte[] byteArray = inStr.getBytes("UTF-8");
        byte[] md5Bytes = sha.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
6.编写验证token的接口
    @RequestMapping(value="/check")
    public String wxChartInterface(HttpServletRequest request, HttpServletResponse response){
        log.info("验证微信消息开始。。。");
        try {
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            //微信加密签名
            String signature=request.getParameter("signature");
            String timestamp=request.getParameter("timestamp");
            String echostr = request.getParameter("echostr");
            String nonce = request.getParameter("nonce");
            log.info("signature={},timestamp={},echostr={},nonce={}",signature,timestamp,echostr,nonce);
            if (CheckInterfaceUtil.me().checkSignature(signature, timestamp, nonce,token)){
                log.info("验证微信消息结束。。。");
                return echostr;
            }
        } catch (UnsupportedEncodingException e) {
            log.error("验证微信消息异常。。。",e);
        }
        log.info("验证微信消息失败。。。");
        return null;
    }
7.点击提交,验证token

springboot开发微信公众号(二)接收并使用机器人回复公众号消息(附源码)_第2张图片

8.查看接收消息的消息文档,编写实体类,由于公众号消息全部是xml格式,所以编写实体时加入xml注解,接收的url地址为验证token消息时的url地址,但是请求方式为POST

springboot开发微信公众号(二)接收并使用机器人回复公众号消息(附源码)_第3张图片

@Data
@XStreamAlias("xml")
public class ReqMessage {
    @XStreamAlias("ToUserName")
    protected String ToUserName;
    @XStreamAlias("FromUserName")
    protected String fromUserName;
    @XStreamAlias("CreateTime")
    protected long createTime;
    @XStreamAlias("MsgType")
    protected String msgType;
    @XStreamAlias("Content")
    private String content;//文本消息内容
    @XStreamAlias("ediaId")
    private String mediaId;//语音消息媒体id,可以调用获取临时素材接口拉取数据。
    @XStreamAlias("Format")
    private String format;//语音格式,如amr,speex等
    @XStreamAlias("MsgId")
    private String msgId;//消息id,64位整型
    @XStreamAlias("MediaID")
    private String mediaID;//语音消息媒体id,可以调用获取临时素材接口拉取该媒体
    @XStreamAlias("Recognition")
    private String recognition;//语音识别结果,UTF8编码
    @XStreamAlias("ThumbMediaId")
    private String thumbMediaId;//视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
    @XStreamAlias("Location_X")
    private String location_X;//地理位置维度
    @XStreamAlias("Location_Y")
    private String location_Y;//Location_Y
    @XStreamAlias("Scale")
    private String scale;//地图缩放大小
    @XStreamAlias("Title")
    private String title;//消息标题
    @XStreamAlias("Description")
    private String description;//消息描述
    @XStreamAlias("Url")
    private String url;//消息链接
}
 @Value("${token}")
    private String token;
    @RequestMapping(value="/check")
    public String wxChartInterface(HttpServletRequest request, HttpServletResponse response){
        log.info("验证微信消息开始。。。");
        if(request.getMethod().equals("POST")){//POST请求为请求消息
            weChartInterface2( request,  response);
            return null;
        }
        try {
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            //微信加密签名
            String signature=request.getParameter("signature");
            String timestamp=request.getParameter("timestamp");
            String echostr = request.getParameter("echostr");
            String nonce = request.getParameter("nonce");
            log.info("signature={},timestamp={},echostr={},nonce={}",signature,timestamp,echostr,nonce);
            if (CheckInterfaceUtil.me().checkSignature(signature, timestamp, nonce,token)){
                log.info("验证微信消息结束。。。");
                return echostr;
            }
        } catch (UnsupportedEncodingException e) {
            log.error("验证微信消息异常。。。",e);
        }
        log.info("验证微信消息失败。。。");
        return null;
    }
    //@RequestMapping(value="/check",method = RequestMethod.POST)
    public void weChartInterface2(HttpServletRequest request, HttpServletResponse response){
        log.info("接收到公众号消息");
        response.setCharacterEncoding("utf-8");
        //将微信请求xml转为实体对象
        ReqMessage reqMessage = MessageUtils.me().xmlToMessage(request);
        log.info("公众号消息体为={}", JSON.toJSONString(reqMessage));
        String message = MessageSendUtils.me().sendMessage(reqMessage);
        try(PrintWriter out=response.getWriter()) {
            out.write(message);
            log.error("公众号消息回复成功,message={}",message);
        } catch (IOException e) {
            log.error("公众号消息回复失败");
        }
    }
9.编写方法接收并回复公众号消息
    /*
     * @Author
     * @Description //拼写回复消息体
     * @Date 15:54 2020/7/23
     * @Param [reqMessage]
     * @return {@link java.lang.String}
     **/
    public  String sendMessage(ReqMessage reqMessage) {
        ReqMessage resMessage = new ReqMessage();
        resMessage.setToUserName(reqMessage.getFromUserName());//回复消息时交换发送人和收件人
        resMessage.setFromUserName(reqMessage.getToUserName());
        resMessage.setMsgType("text");//回复文本格式
        resMessage.setContent("欢迎关注");
        resMessage.setCreateTime(System.currentTimeMillis());
        return messageToXml(resMessage);
    }
    public String messageToXml(ReqMessage resMessage){
        XStream xstream  = new XStream();
        xstream.processAnnotations(resMessage.getClass());
        return xstream.toXML(resMessage);
    }
10.发送消息获取回复

springboot开发微信公众号(二)接收并使用机器人回复公众号消息(附源码)_第4张图片

11.使用图灵机器人接口智能回复
 public  String sendMessage(ReqMessage reqMessage) {
        ReqMessage resMessage = new ReqMessage();
        resMessage.setToUserName(reqMessage.getFromUserName());//回复消息时交换发送人和收件人
        resMessage.setFromUserName(reqMessage.getToUserName());
        resMessage.setMsgType("text");//回复文本格式
        resMessage.setContent(getRespMessage(reqMessage.getContent()));
        resMessage.setCreateTime(System.currentTimeMillis());
        return messageToXml(resMessage);
    }
    public String messageToXml(ReqMessage resMessage){
        XStream xstream  = new XStream();
        xstream.processAnnotations(resMessage.getClass());
        return xstream.toXML(resMessage);
    }
    public String getRespMessage(String message){
        Map map=new HashMap<>();
        map.put("key","0b96821f992fade2720ef2a922ca7f6e");
        map.put("question",message);
        String url=OKHttp3Utils3.getUrl(map,ApiEum.ROBBOT_URL.getInfo(),"UTF-8");
        CtBossSender ctBossSender=new CtBossSender();
        JSONObject nutMap=ctBossSender.sendHttpGetRobbot(url);
        return nutMap.getJSONArray("newslist").getJSONObject(0).getString("reply");
    }
public enum ApiEum {
    ACCESS_TOKEN_URL("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"),
    //ROBBOT_URL("http://api.tianapi.com/txapi/robot/index"),
    ROBBOT_URL("http://api.tianapi.com/txapi/tuling/index"),
    BASE_URL(" https://api.weixin.qq.com/cgi-bin/"),
    DELETE_PERSONAL_MENU_URL("menu/delconditional"),//删除个性化菜单
    CREATE_PERSONALIZED_MENU_URL("menu/addconditional"),//创建个性化菜单
    MENU_GET_URL("menu/get"),//自定义菜单的查询接口
    MENU_DELETE_URL("menu/delete"),//自定义菜单删除
    MENU_CREATE_URL("menu/create");//自定义菜单创建
    private String info;
    ApiEum(String info) {
        this.info = info;
    }
    public String getInfo() {
        return info;
    }
}
12.发送消息,获取机器人回复

springboot开发微信公众号(二)接收并使用机器人回复公众号消息(附源码)_第5张图片

ps:源码地址https://gitee.com/bjiangAnhui/yichengyoushaonian/tree/master/boot-wechart

你可能感兴趣的:(接收公众号消息并回复,图灵机器人,spring,boot)