zoukankan      html  css  js  c++  java
  • 微信个人公众号开发-java

    一:申请公众号

       无聊的去申请一个个人公众号,试了下微信实现自定义菜单与自定义对话的一些基本功能,如果多花点时间,金钱(= =!)还是有不少功能可以使用,还是蛮有意思的。

    废话不多说,先看一下申请的流程。首先进入微信公众平台申请一个个人的订阅号,其实它自带的功能里就有自动回复的设置,不过还是试试开发接口去实现,更有意思。

     二:接口实现

    在开发-基本配置里,服务器配置就是当前公众号和我们自己服务器建立连接的配置。

    controller:该方法的路径就是基本配置里的服务器地址(URL)

    /**
         * 微信接入
         * @param
         * @return
         * @throws IOException
         */
        @RequestMapping(value="/wechat/connect",method = {RequestMethod.GET, RequestMethod.POST})
        @ResponseBody
        public void connectWeixin(HttpServletRequest request, HttpServletResponse response) throws IOException {
            // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
            request.setCharacterEncoding("UTF-8");  //微信服务器POST消息时用的是UTF-8编码,在接收时也要用同样的编码,否则中文会乱码;
            response.setCharacterEncoding("UTF-8"); //在响应消息(回复消息给用户)时,也将编码方式设置为UTF-8,原理同上;boolean isGet = request.getMethod().toLowerCase().equals("get");
    
            PrintWriter out = response.getWriter();
    
            try {
                if (RequestMethod.GET.name().equals(request.getMethod())) {
                    String signature = request.getParameter("signature");// 微信加密签名
                    String timestamp = request.getParameter("timestamp");// 时间戳
                    String nonce = request.getParameter("nonce");// 随机数
                    String echostr = request.getParameter("echostr");//随机字符串
    
                    // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
                    if (SignUtil.checkSignature(DNBX_TOKEN, signature, timestamp, nonce)) {
                        LOGGER.info("Connect the weixin server is successful.");
                        response.getWriter().write(echostr);
                    } else {
                        LOGGER.error("Failed to verify the signature!");
                    }
                }else{
                    String respMessage = "";
                    try {
                        respMessage = wechatService.weixinPost(request);
                        out.write(respMessage);
                        LOGGER.info("The request completed successfully");
                        LOGGER.info("to weixin server "+respMessage);
                    } catch (Exception e) {
                        LOGGER.error("Failed to convert the message from weixin!", e);
                    }
                }
            } catch (Exception e) {
                LOGGER.error("Connect the weixin server is error.", e);
            }finally{
                out.close();
            }
        }
    

      校验的代码如下:

    public class SignUtil {
    
        /**
         * 验证签名
         *
         * @param token 微信服务器token,在env.properties文件中配置的和在开发者中心配置的必须一致
         * @param signature 微信服务器传过来sha1加密的证书签名
         * @param timestamp 时间戳
         * @param nonce 随机数
         * @return
         */
        public static boolean checkSignature(String token,String signature, String timestamp, String nonce) {
            String[] arr = new String[] { token, timestamp, nonce };
            // 将token、timestamp、nonce三个参数进行字典序排序
            Arrays.sort(arr);
    
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            String tmpStr = SHA1.encode(arr[0] + arr[1] + arr[2]);
    
            // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
            return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
        }
    
    }
    

      

    import java.security.MessageDigest;
    
    /**
     * 微信公众平台(JAVA) SDK
     *
     * SHA1算法
     *
     * @author helijun 2016/06/15 19:49
     */
    public final class SHA1 {
    
        private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
                '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    
        /**
         * Takes the raw bytes from the digest and formats them correct.
         *
         * @param bytes the raw bytes from the digest.
         * @return the formatted bytes.
         */
        private static String getFormattedText(byte[] bytes) {
            int len = bytes.length;
            StringBuilder buf = new StringBuilder(len * 2);
            // 把密文转换成十六进制的字符串形式
            for (int j = 0; j < len; j++) {
                buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
                buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
            }
            return buf.toString();
        }
    
        public static String encode(String str) {
            if (str == null) {
                return null;
            }
            try {
                MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
                messageDigest.update(str.getBytes());
                return getFormattedText(messageDigest.digest());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    

      其中加密字段分别为:signature:微信加密签名   timestamp:时间戳  nonce:随机数  echostr:随机字符串,

    而代码中的DNBX_TOKEN 就是公众号里配置的 

    再来看看如何处理具体的post请求:

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.yxy.nova.bean.wechat.TextMessage;
    import com.yxy.nova.util.MessageUtil;
    import com.yxy.nova.util.SimpleHttpClient;
    import com.yxy.nova.util.wechat.weather.TianQiCityID;
    import com.yxy.nova.util.wechat.weather.TianQiWeatherHelper;
    import com.yxy.nova.web.util.WebUtil;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.http.NameValuePair;
    import org.apache.http.message.BasicNameValuePair;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    import java.util.Map;
    
    /**
     * @date 2021/1/22 上午11:38
     * @Description
     */
    @Service
    public class WechatServiceImpl implements WechatService {
    
        private final Logger LOGGER = LoggerFactory.getLogger(getClass());
    
        private static JSONObject accessToken = new JSONObject();
    
        @Value("${wechat-AppId}")
        private String appleId;
        @Value("${wechat-AppSecret}")
        private String appSecret;
        @Autowired
        private SimpleHttpClient simpleHttpClient;
    
    
    
        /**
         * 处理微信发来的请求
         *
         * @param request
         * @return
         */
        @Override
        public String weixinPost(HttpServletRequest request) {
            String respMessage = null;
            try {
    
                // xml请求解析
                Map<String, String> requestMap = MessageUtil.xmlToMap(request);
    
                // 发送方帐号(open_id)
                String fromUserName = requestMap.get("FromUserName");
                // 公众帐号
                String toUserName = requestMap.get("ToUserName");
                // 消息类型
                String msgType = requestMap.get("MsgType");
                // 消息内容
                String content = requestMap.get("Content");
    
                LOGGER.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType);
    
                // 文本消息
                if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
                    //这里根据关键字执行相应的逻辑,只有你想不到的,没有做不到的
    
                    TianQiCityID ci = new TianQiCityID();
                    if(ci.getCityIDMap().get(content) !=null){
                        TextMessage text = new TextMessage();
                        TianQiWeatherHelper tianQiWeatherHelper = new TianQiWeatherHelper();
                        tianQiWeatherHelper.setSimpleHttpClient(simpleHttpClient);
                        text.setContent(tianQiWeatherHelper.getWeatherReportByCityName(content));
                        text.setToUserName(fromUserName);
                        text.setFromUserName(toUserName);
                        text.setCreateTime(System.currentTimeMillis() + "");
                        text.setMsgType(msgType);
                        respMessage = MessageUtil.textMessageToXml(text);
                    }
                    if ("天气".equals(content)) {
                        TextMessage text = new TextMessage();
                        TianQiWeatherHelper tianQiWeatherHelper = new TianQiWeatherHelper();
                        tianQiWeatherHelper.setSimpleHttpClient(simpleHttpClient);
                        text.setContent(tianQiWeatherHelper.getWeatherReportByIP(WebUtil.getRemoteAddr(request)));
                        text.setToUserName(fromUserName);
                        text.setFromUserName(toUserName);
                        text.setCreateTime(System.currentTimeMillis() + "");
                        text.setMsgType(msgType);
                        respMessage = MessageUtil.textMessageToXml(text);
                    }
    
                    //自动回复
    //                TextMessage text = new TextMessage();
    //                text.setContent("the text is" + content);
    //                text.setToUserName(fromUserName);
    //                text.setFromUserName(toUserName);
    //                text.setCreateTime(System.currentTimeMillis() + "");
    //                text.setMsgType(msgType);
    //                respMessage = MessageUtil.textMessageToXml(text);
    
                }
    //            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {// 事件推送
    //                String eventType = requestMap.get("Event");// 事件类型
    //
    //                if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {// 订阅
    //                    respContent = "欢迎关注xxx公众号!";
    //                    return MessageResponse.getTextMessage(fromUserName , toUserName , respContent);
    //                } else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {// 自定义菜单点击事件
    //                    String eventKey = requestMap.get("EventKey");// 事件KEY值,与创建自定义菜单时指定的KEY值对应
    //                    logger.info("eventKey is:" +eventKey);
    //                    return xxx;
    //                }
    //            }
                //开启微信声音识别测试 2015-3-30
    //            else if(msgType.equals("voice"))
    //            {
    //                String recvMessage = requestMap.get("Recognition");
    //                //respContent = "收到的语音解析结果:"+recvMessage;
    //                if(recvMessage!=null){
    //                    respContent = TulingApiProcess.getTulingResult(recvMessage);
    //                }else{
    //                    respContent = "您说的太模糊了,能不能重新说下呢?";
    //                }
    //                return MessageResponse.getTextMessage(fromUserName , toUserName , respContent);
    //            }
                //拍照功能
    //            else if(msgType.equals("pic_sysphoto"))
    //            {
    //
    //            }
    //            else
    //            {
    //                return MessageResponse.getTextMessage(fromUserName , toUserName , "返回为空");
    //            }
                // 事件推送
                else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
                    String eventType = requestMap.get("Event");// 事件类型
                    // 订阅
                    if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
    
                        TextMessage text = new TextMessage();
                        text.setContent("欢迎关注,xxx");
                        text.setToUserName(fromUserName);
                        text.setFromUserName(toUserName);
                        text.setCreateTime(System.currentTimeMillis() + "");
                        text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
    
                        respMessage = MessageUtil.textMessageToXml(text);
                    }
                    // TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
                    else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {// 取消订阅
    
    
                    }
                    // 自定义菜单点击事件
                    else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
                        String eventKey = requestMap.get("EventKey");// 事件KEY值,与创建自定义菜单时指定的KEY值对应
                        if (eventKey.equals("weather")) {
                            TextMessage text = new TextMessage();
                            text.setContent("请输入城市名称");
                            text.setToUserName(fromUserName);
                            text.setFromUserName(toUserName);
                            text.setCreateTime(System.currentTimeMillis() + "");
                            text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
    
                            respMessage = MessageUtil.textMessageToXml(text);
                        }
                    }
                }
            }
            catch (Exception e) {
                LOGGER.error("error......");
            }
            return respMessage;
        }
    }

    如上面的代码,我在文本消息中,简单的实现了根据城市名称查询天气情况。接口的实现就不贴出来了,就是一个简单的接口对接。

    三:第三方接口

    网上有关于 www.weather.com.cn网站获取天气情况的接口,但是本人测试使用发现返回字段比较少,而且刷新的不是很及时,

    现在推荐一个免费的第三方接口:https://www.tianqiapi.com/,在api接口中有三个免费的接口,可以实现一些简单的功能了。

    我在demo中使用的就是:免费实况天气接口:https://www.tianqiapi.com/index/doc?version=v6,用完感受还是不错的。

    至此,已经实现了公众号的部分功能,其实微信的接口文档还有很多,还有很多更好玩的功能,比如微信对话开放平台,有兴趣的可以申请开发权限,去探索一下吧。

     
  • 相关阅读:
    查看数据库错误日志的位置
    快速过滤出完整的SQL语句
    使用apidocJs快速生成在线文档
    vs开发nodejs系列之 修改新建js文件的模板
    Node.js 入门资料
    ECMAScript 6 学习资料
    NodeJS学习目录
    MongoDB学习笔记系列~目录
    sails ORM组件 Waterline v0.10 英文文档
    node.js---sails项目开发
  • 原文地址:https://www.cnblogs.com/yxy-ngu/p/14316814.html
Copyright © 2011-2022 走看看