微信公众号开发-业务开发03


1. 前言
  接下来是对业务进行简单的说明。其实这是一个很简单的项目。跟市面上的WiFi故事机是类似的,包含设备局域网发现、Airkiss配网、设备绑定、微信互聊、公众号点播。


2.局域网发现

  标准的Airkiss协议里面有自带局域网发现协议,只要在微信公众号后台增加设备功能插件,然后利用Airkiss第三版完整协议,那么可以完成设备的,局域网发现、Airkiss配网、设备绑定。这三个功能。不过我目前做的这个项目,只用到Airkiss的配网,没有用到其他的功能。
  局域网发现,我这里的实现方式是,微信公众号网页发送一个HTTP请求到服务器,服务器记录IP地址,设备联网后,同样请求服务器,记录IP地址。如果两者的IP地址是一样的,那么表示两个设备处于同一个局域网。这种方式,在绝大部分情况下,是没有问题的。只是对那些大局域网会有些影响,比如同个小区,只有一个外网地址出口,或者类似于企业或学校,只有少数的外网地址,就会出现,设备被其他人误扫描,绑定。

3.Airkiss配网、超声波配网

  这个在公众号页面上用JSSDK调用WiFi配置即可。

1 wx.invoke('configWXDeviceWiFi', { }, function(res) {
2     if(res.err_msg == "configWXDeviceWiFi:ok"){
3         window.location.href = "/wx/wechat/scan_bind";
4     }else if(res.err_msg == "configWXDeviceWiFi:fail"){
5         window.Vue.$toast("配置不成功,可以尝试使用声波配网");
6     }else if(res.err_msg == "configWXDeviceWiFi:cancel"){
7     }
8 });

  由于网络的复杂性,有时候通过Airkiss配网会不成功,因此需要有其他的辅助性配网方式,本项目使用声波配网。流程是,在界面上输入WiFi的SSID和密码,然后传到服务器,服务器根据自定义算法生成一段WAV的音频文件,然后把这个音频文件放到

4.设备绑定
  通过上面的,设备局域网发现及配网网络后。就进入设备绑定功能。设备绑定,就是在数据库中记录当前设备的设备ID和当前登录的微信用户。这里公众号唯一确定一个用户的是工作的OpenID。这个OpenID是公众号内用户的唯一凭证。


5.微信互聊
  在公众号的聊天窗口中,每次用户向公众号发送信息或者一些事件时,都是会携带OpenID。然后根据OpenID对应绑定的设备ID,向该设备发送语音信息。
  这里分为两种情况,一种是用户发送到设备,一种是设备发送到用户。
  用户发送到设备的,用户在公众号发送语音信息,会触发微信的一个语音信息事件,我们只需要在语音信息注册一个Handler即可。这里的Handler只会有一个MediaID,服务器还需要通过这个MediaID获取语音二进制AMR音频文件。这个AMR文件可以直接发到设备或存放到服务器。

newRouter.rule().async(false).msgType(XmlMsgType.VOICE).handler(msgvoiceHandler).end();
 1 @Component
 2 public class WechatMpMsgVoiceHandler extends AbstractHandler {
 3 
 4     @Value("${project.resource.url}")
 5     private String voice_prefix;
 6     @Value("${project.voice.dir}")
 7     private String voiceDir;
 8     @Autowired
 9     private WechatVoiceCtrlService wechatvoicectrlService;
10     @Autowired
11     private WechatBindService wechatbindService;
12     @Autowired
13     private MQTTPushMessageService mqttpushmessageService;
14     
15     @Override
16     public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map context, WxMpService wxmpService,
17             WxSessionManager sessionManager) throws WxErrorException {
18         try {
19             File file = wxmpService.getMaterialService().mediaDownload(wxMessage.getMediaId());
20             String mediaId = wxMessage.getMediaId();
21             String filename = TimestampUtils.getCurrentDateDirFormat() + "/" + mediaId;
22             File moveit = new File(voiceDir + "/" + filename);
23             if(!moveit.getParentFile().exists()) {
24                 moveit.getParentFile().mkdirs();
25             }
26             FileUtils.moveFile(file, moveit);
27             String _id = "";
28             //写入数据库
29             WechatDevicePoModel device = 
30                     wechatbindService.selectOneWechatDeviceByOpenID(wxMessage.getFromUser());
31             //System.out.println(device);
32             if(device == null) {
33                 return new WechatMpTextBuilder().build("您还未绑定设备哦", wxMessage, wxmpService);
34             }
35             MongoVoiceVoModel voice = new MongoVoiceVoModel();
36             voice.setData(filename);
37             voice.setDeviceid(device.getDevice_id());
38             voice.setRand(RandomUtils.getRandomStr());
39             voice.setRead(false);
40             voice.setTs(TimestampUtils.getCurrentTimestamp());
41             voice.setType("path");
42             voice = wechatvoicectrlService.save(voice);
43             if(voice != null) {
44                 _id = voice.get_id();
45                 MqttPayloadVoModel payload = new MqttPayloadVoModel<>();
46                 payload.setId(RandomUtils.getRandomStr());
47                 payload.setCmd("message_new");
48                 payload.setTs(TimestampUtils.getCurrentTimestamp());
49                 MqttVoiceMessagePayloadVoModel parm = new MqttVoiceMessagePayloadVoModel();
50                 parm.set_id(_id);
51                 parm.setTxt(wxMessage.getRecognition());
52                 parm.setUrl(voice_prefix + "?filename=" + voice.getData() + "&access_token=");
53                 payload.setParm(parm);
54                 System.out.println(payload);
55                 mqttpushmessageService.sendWechatNewMessage(device.getDevice_id(), payload);
56                 //增加对该设备下的所有用户进行广播
57                 List users = wechatbindService.selectListWechatUserByDeviceID(device.getDevice_id());
58                 for(int i=0; i) {
59                     WechatUserPoModel user = users.get(i);
60                     if(user.getOpenid().equals(wxMessage.getFromUser())) {
61                         continue;
62                     }
63                     try {
64                         wxmpService.getKefuService().sendKefuMessage(
65                                 WxMpKefuMessage.VOICE().toUser(user.getOpenid()).mediaId(mediaId).build());
66                     }catch (WxErrorException e) {
67                         //一些长期没有跟公众号互动的用户会发送失败
68                     }
69                 }
70                 
71                 return new WechatMpTextBuilder().build("已发送[Yeah!]\n" + wxMessage.getRecognition(), 
72                         wxMessage, wxmpService);
73             }else {
74                 return new WechatMpTextBuilder().build("发送失败", wxMessage, wxmpService);
75             }
76         }catch (Exception e) {
77             e.printStackTrace();
78         }
79         return new WechatMpTextBuilder().build("发送失败2", wxMessage, wxmpService);
80     }
81 }

  另一种情况是,设备发送语音到用户。由于设备是无法直接对接微信服务器,或者说,无法直接上传音频AMR文件到微信。因此,需要把音频文件上传到服务器,服务器再上传到微信服务器,然后获取到临时资源文件的MediaID。再通过设备ID对应绑定的用户的OpenID,发送一个客服信息。

  设备上传音频到服务器,服务器上传音频到微信,再通过微信客服接口发送给绑定的用户。

 1     /**
 2      * 设备->微信用户
 3      * @param device_id
 4      * @param amr
 5      * @return
 6      */
 7     public boolean send(String device_id, InputStream input) {
 8         try {
 9             List users = wechatbindService.selectListWechatUserByDeviceID(device_id);
10             if(CheckUtils.isEmpty(users)) {
11                 return false;
12             }
13             WxMediaUploadResult result = 
14                     wxmpService.getMaterialService().mediaUpload(
15                             WxConsts.MediaFileType.VOICE, "amr", input);
16             String mediaid = result.getMediaId();
17             for(int i=0; i) {
18                 WechatUserPoModel user = users.get(i);
19                 try {
20                     wxmpService.getKefuService().sendKefuMessage(
21                             WxMpKefuMessage.VOICE().toUser(user.getOpenid()).mediaId(mediaid).build());    
22                 }catch (WxErrorException e) {
23                     //一些长期没有跟公众号互动的用户会发送失败
24                 }
25             }
26             return true;
27         } catch (WxErrorException e) {
28             if(e.getError().getErrorCode() == 45047) {
29                 //保留语音信息
30             }
31             System.out.println(e.getError().getErrorCode());
32             e.printStackTrace();
33         }
34         return false;
35     }

6.公众号点播及展示
  公众号H5界面与设备互动,设备与用户两者间需要通过服务器转发信息或命令到对方。这里我使用MQTT作为信息转发中间件。
  设备与用户间订阅同个Topic,然后互相发送信息即可。对于设备信息的展示,用户端的命令控制,都是通过MQTT,这里都是一些自定义协议,就不在过多说明了。


7. 前端界面


8.数据库表
  用户表

 1 create table wechat_user(
 2     userid serial primary key,  --主键
 3     openid text,                --公众号下用户OpenID
 4     nickname text,              --昵称
 5     sex_desc text,              --性别描述
 6     sex int,                    --性别
 7     city text,                  --城市
 8     province text,              --省份
 9     country text,               --国家
10     head_img_url text,          --头像
11     subscribe_time bigint,      --订阅时间
12     union_id text,              --
13     remark text,                --
14     subscribe_scene text,       --订阅渠道
15     status boolean              --用户状态
16 );

  设备表

1 create table wechat_device(
2     devid serial primary key,   --主键
3     device_id text,             --设备ID,必须保证全局唯一
4     device_mac text,            --设备MAC地址
5     auth_key text,              --通讯加密key 32字节16进制串
6     op_type int,                --绑定类型
7     op_status int               --设备状态 -1禁用  0新建 1生产 2微信验证
8 );

  用户-设备绑定表

1 create table wechat_bind(
2     bindid serial primary key,  --主键
3     openid text,                --用户OpenID
4     device_id text              --设备ID
5 );

9.总结
  总的来说,这个项目还是没有多少技术含量的,很普通的一个项目,一个多月就把前后端给做了,最后也是没有实际使用。又是一个报废项目。感觉心有点受伤呀!既然花时间做了,还是要简单记录一下。

本文地址: https://www.cnblogs.com/wunaozai/p/9913379.html