微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync)

时间:2022-08-31 22:45:22
这篇讲解对微信公众号开发DEMO分析,主要的目的是了解它的原理和微信API的使用。
然后进行修改,实现我们想要的功能,如:菜单的创建、信息交互、跳转到自定义HTML5页面等等。



一、目录结构讲解。
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
展开目录后,按照1、2、3、4、5的顺序讲解:
1、web.xml网站配置(上面图片标错了,是在WEB-INF未展来的目录里的web.xml)
2、公众账号信息配置。
3、微信推送消息的入口。
4、处理微信消息的业务类。
5、相关微信API的使用方法,创建菜单,进行设备授权,生成二维码等。



二、web.xml(程序引导配置)

微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
双击web.xml,就会看到网站配置的内容
1、声明地址名称,绑定相关的Servlet类,以后访问就用这个地址。
2、将名称映射到地址。
这样就能成功访问:http://你的域名/callback来访问com.bluelight.demo.web.CallbackServlet类了。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
我们运行,右击bluelight->Run As->Run Server,然后Finish。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
1、在后面添加callback,回车。
2、出现Null异常,说明已经成功调用了类,只是没有传参数。





那我们如何添加一个访问类地址呢?
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
我们复制,访着实现一个能使用calltest访问的地址。





微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
重新运行,然后访问http://localhost:8080/demo/calltest,成功调用了类文件。
这里还有一种是jsp,可以直接放到WebContent目录里,直接访问文件名就可以,不需要做映射。




三、config.properties(微信公众号appID、appsecret、taken设置)
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
双击config.properties,打开后appID、appsecret、taken都是空的,我们要复制进去。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
登陆公众号开发测试网址:
http://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
直接复制appID、appsecret填写,URL是我们之后要教大家申请的服务器地址,taken自定义一个就可以了。



四、CallbackServlet.java(入口类讲解)
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
打开类,主要看到三个类:doGet、doPost、out函数。


微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
在这里的doGet函数,基本不做事,只接收到传来的值,然后直接就返回空的值回去了,如果验证不通过就返回错误而以。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
doPost涵数,多了解决xml,然后使用CallbackService业务类进行处理(这才是关键的地方),然后调用out函数才返回给微信。




微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
out函数,使用了Writer类,然后输出返回,这样微信就能收到啦。




五、CallbackService.java(微信消息业务类讲解)
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
在入口类的doPost涵数里,调用了这个业务类,那我们来一一分析它。
这个类就只有一个事件处理函数handle


[Java] 纯文本查看 复制代码?
001002003004005006007008009010011012013014015016017018019020021022023024025026027028029030031032033034035036037038039040041042043044045046047048049050051052053054055056057058059060061062063064065066067068069070071072073074075076077078079080081082083084085086087088089090091092093094095096097098099100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 packagecom.bluelight.demo.service; importjava.util.Map; importorg.apache.commons.codec.binary.Base64; importcom.bluelight.demo.api.DeviceApi;importcom.bluelight.demo.api.MpApi;importcom.bluelight.demo.consts.MsgType;importcom.bluelight.demo.consts.XmlResp;importcom.bluelight.demo.mock.DBMock;importcom.bluelight.demo.protocol.BlueLight;importcom.bluelight.demo.protocol.BlueLight.CmdId; /** * 回调业务处理 */publicclass
CallbackService {
         // 自定义菜单中的key值        publicstatic
final
String V1001_LIGHT_ON = "V1001_LIGHT_ON";//点灯
        publicstatic
final
String V1002_LIGHT_OFF = "V1002_LIGHT_OFF";//灭灯
                 publicString handle(Map<String, String> reqMap) throwsException {                String msgType = reqMap.get("MsgType");                String fromUser = reqMap.get("FromUserName");                String toUser = reqMap.get("ToUserName");                 // 针对不同类型的消息和事件进行处理                 // 文本消息                if(MsgType.TEXT.equals(msgType)) {                        // 可以在此处进行关键字自动回复                        String content = "收到文本消息:"+ reqMap.get("Content");                        returnXmlResp.buildText(fromUser, toUser, content);                }                                 // 基础事件推送                if(MsgType.EVENT.equals(msgType)) {                        String event = reqMap.get("Event");                        // 关注公众号                        if(MsgType.Event.SUBSCRIBE.equals(event)) {                                // 回复欢迎语                                returnXmlResp.buildText(fromUser, toUser, "欢迎关注蓝牙灯泡demo测试公众号!");                        }                        // 菜单点击事件                        if(MsgType.Event.CLICK.equals(event)) {                                // 根据key值判断点击的哪个菜单                                String eventKey = reqMap.get("EventKey");                                // 点灯/灭灯                                if(V1001_LIGHT_ON.equals(eventKey)                                                || V1002_LIGHT_OFF.equals(eventKey)) {                                        //是否点灯操作                                        booleanopen = V1001_LIGHT_ON.equals(eventKey);                                         // 根据 fromUserName 获取绑定的信息                                        Map<String, String> boundInfo = DBMock.queryBoundInfo(fromUser);                                         // 未绑定                                        if(boundInfo == null) {                                                returnXmlResp.buildText(fromUser, toUser, "未绑定");                                        }                                         String deviceType = boundInfo.get("deviceType");                                        String deviceID = boundInfo.get("deviceID");                                        String openID = boundInfo.get("openID");                                                                                 // 构造设备消息                                        CmdId cmdId = open ? BlueLight.CmdId.OPEN_LIGHT_PUSH : BlueLight.CmdId.CLOSE_LIGHT_PUSH;                                        byte[] respRaw = BlueLight.build(cmdId, null, (short)0).toBytes();                                        // Base64编码                                        finalString content = Base64.encodeBase64String(respRaw);                                                                                 // 推送消息给设备                                        DeviceApi.transMsg(deviceType, deviceID, openID, content);                                                                                 // 回复                                        booleandebug = true;                                        if(debug){                                                // 返回调试信息                                                String debugText = "已发送"+ (open ? "点灯":"灭灯") + "消息:"+"deviceID为"+ deviceID + ",设备消息为"+ content;                                                returnXmlResp.buildText(fromUser, toUser, debugText);                                        }else{                                                return"";                                        }                                }                        }                }                 // 设备消息或事件                if(MsgType.DEVICE_EVENT.equals(msgType)                                || MsgType.DEVICE_TEXT.equals(msgType)) {                        String reqContent = reqMap.get("Content");                        String deviceType = reqMap.get("DeviceType");                        String deviceID = reqMap.get("DeviceID");                        String sessionID = reqMap.get("SessionID");                        finalString openID = reqMap.get("OpenID");                        // 设备事件推送                        if(MsgType.DEVICE_EVENT.equals(msgType)) {                                String event = reqMap.get("Event");                                // 绑定/解绑事件                                if(MsgType.DeviceEvent.BIND.equals(event)                                                || MsgType.DeviceEvent.UNBIND.equals(event)) {                                        // 存储用户和设备的绑定关系                                        if(MsgType.DeviceEvent.BIND.equals(event)){                                                DBMock.saveBoundInfo(reqMap);                                        }else{                                                DBMock.removeBoundInfo(reqMap.get("FromUserName"));                                        }                                        // 设备绑定/解绑事件可以回复空包体                                        return"";                                }                        }                        // 收到设备消息                        if(MsgType.DEVICE_TEXT.equals(msgType)) {                                // Base64解码                                byte[] reqRaw = Base64.decodeBase64(reqContent);                                // 反序列化                                BlueLight lightReq = BlueLight.parse(reqRaw);                                                                 // 逻辑处理                                // demo中 推送消息给用户微信                                String reqText = lightReq.body;                                System.out.println("recv text:" + reqText);                                String transText = "收到设备发送的数据:";                                                                 byte[] reqTextRaw = reqText.getBytes("UTF-8");                                if(reqTextRaw.length > 0&& reqTextRaw[reqTextRaw.length - 1] == 0) {                                        // 推送给微信用户的内容去掉末尾的反斜杠零'\0'                                        transText = transText + newString(reqTextRaw,0, reqTextRaw.length - 1,"UTF-8");                                }else{                                        transText = transText + reqText;                                }                                                                 // 推送文本消息给微信                                MpApi.customSendText(openID, transText);                                 // demo中 回复 收到的内容给设备                                BlueLight lightResp = BlueLight.build(BlueLight.CmdId.SEND_TEXT_RESP, reqText, lightReq.head.seq);                                // 序列化                                byte[] respRaw = lightResp.toBytes();                                // Base64编码                                String respCon = Base64.encodeBase64String(respRaw);                                                                 // 设备消息接口必须回复符合协议的xml                                returnXmlResp.buildDeviceText(toUser, fromUser, deviceType, deviceID, respCon, sessionID);                        }                }                 // 未处理的情况返回空字符串                return"";        }}


代码主要针对不同类型的消息,进行处理,如TEXT,EVENT,DEVICE_EVENT,DEVICE_TEXT。
TEXT是纯文本发过来,含表情。
EVENT是关注公众号,取消公众号,菜单点击等等,
DEVICE_EVENT是硬件的消息,DEVICE_TEXT就是硬件发送过来的文本(一般用文本交互就可以了)



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
在菜单点击事件里,实现了开和关,这里获取硬件的信息,前提是先扫二维码绑定,这个之后讲硬件时会讲到的。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
在设备事件里,如果是绑定事件和解绑事件,就进行临时的保存和删除。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
这里处理收到设备的信息处理。




[Java] 纯文本查看 复制代码?
123456789 //
文本消息
                if(MsgType.TEXT.equals(msgType)) {                        // 可以在此处进行关键字自动回复                        String content = "收到文本消息:"+ reqMap.get("Content");if(reqMap.get("Content").equals("你好")){        content = "你也好,你全家都好。";}                        returnXmlResp.buildText(fromUser, toUser, content);                }

这里我们做一下修改,添加一段代码,如果用户发送“你好”两字,那么我直接回复他:你也好,你全家都好。
if(content.equals("你好")){
content = "你也好,你全家都好。";
}





六、Tools.java(操作微信API类讲解)
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
Tools.java共有main、createQrByDeviceId、createQrImage、device_Auth、createMenu等5个函数。
分别为主函数、建立二维码、生成图片、授权设备、建立菜单。
这里我们只跳用建立菜单,二维码和授权,之后在说硬件的时候再教大家。


[Java] 纯文本查看 复制代码?
010203040506070809101112 //
文本消息
                if(MsgType.TEXT.equals(msgType)) {                        // 可以在此处进行关键字自动回复                        String content = "收到文本消息:"+ reqMap.get("Content");                        if(reqMap.get("Content").equals("你好")){                                content = "你也好,你全家都好。";                        }                        if(reqMap.get("Content").equals("菜单") ){                                Tools.createMenu();//建立菜单                        }                        returnXmlResp.buildText(fromUser, toUser, content);                }

跟上篇一样,用户发送菜单二字,我们就调用并生成菜单
提醒:(菜单生成微信要返回主界面,再进一次微信,同时设置第二次有时要24小时内才能生效)
但这里完成,经过小编测试,也还是不成功,原因是因为当前微信的demo不支持https请求(也不知道为什么微信demo自己不调试好),那我们再改改按下一个步聚来做就可以了。




七、支持Https的POST(默认DEMO不支持,修改代码)
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
我们要在这里添加二个文件,原本是没有的。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
添加进去,SSLClient.java、MyX509TrustManager.java这两个文件就是实现https的请求的(修改后的DEMO在最后面提供给大家下载的)




微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
我们还需要在http请求的地方调用SSLClient类,如上图,我们在executeGet、executePost添加了一段代码。
if( url.indexOf("https")>=0 ){
        httpclient =new SSLClient();
}else{
        httpclient = new DefaultHttpClient();
}
如果是https那么就自动请求ssl,实现可以请求https了。




八、JSP文件调用类文件。
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
右击WebContent文件-》New->JSP File,新建一个jsp文件,取名为test.jsp,然后点击finish。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 

自动生成jsp文件,然后运行。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
网址后面加test.jsp,然后回车,就能看到白白的页面,访问成功。



微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
[Java] 纯文本查看 复制代码?
01020304050607080910111213141516 <%@
page language=
"java"contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"import="com.bluelight.tools.Tools"%><!DOCTYPE
html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta
http-equiv=
"Content-Type"content="text/html; charset=UTF-8">
<title>Insert
title here</title>
</head><body>调用类函数的方法:<%            Tools ts= newTools();            ts.createMenu();    %></body></html>

这时再修改一个这个文件的代码如上图,这样就可以成功调用类文件了,但不建议这么调用不正规,只是方便而以。

微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
再运行,输出如下信息,说明成功!



九、打包成War文件。
微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync)  
开始导出war文件。


微信硬件开发系列教程03-微信公众号开发DEMO(airkiss/airsync) 
导出一个文件名,保存就能成功导出war文件,之后就可以上传到服务器了。



十、总结。
原来的DEMO不支持https请求,也没有jsp文件,这些都是要自己建立的。
经过对微信Demo的源代码分析、修改、调式,那么我们这一课节就完成了。
附源代码下载: http://download.csdn.net/detail/hunhun1122/9807706