使用socket.io开发微信点餐并在后台蓝牙打印小票功能(二)

接上篇
使用socket.io开发微信点餐并在后台蓝牙打印小票功能(一)/)
我们现在已经能在app里打印了,但是我们想要的功能是在用户端点击下单,然后商家后台的手机要能根据用户下的订单打印出对应的票据出来,普通的ajax请求已经不能满足我们的需求了,用轮询的话性能太差,这时候就该轮到websocket协议出场了. websocket是长连接的,完全能满足我们的需求
所以第一步就是搭建websocket服务器,我是用nodejs开发,然后选择了express和socket.io这两个库.

使用socket.io这个库的原因是因为兼容性强,Socket.io将Websocket和轮询 (Polling)机制以及其它的实时通信方式封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码,用的人也多
由于手机和打印设备众多,如何让用户下单后就能让对应商家手机通知打印机打印呢?

我想到的是商家手机上安装的app存有唯一的id,每个用户通过扫描携带商家id的二维码,然后下单后再emit一个事件,携带id和要打印的数据到服务器,服务器再广播一个事件,把这个id广播出去,商家端app接收后再对比这个id和本地id是否相同,相同则打印服务端携带的数据
整体思路就是这样

说干就干,服务端代码简单


核心功能就是监听sendData事件,接收携带id和打印内容的数据,然后接收完毕就广播print事件,发送id和打印内容
商家客户端方面也不难,监听print事件,接受id和打印内容,如果接收到的id和本地设备id相同,则控制打印机进行打印
ps:要注意的是之前点击这个按钮是直接打印的,现在变成监听print事件,接收完数据再打印接收到的内容

用户网页端方面,我简单做了个demo,只有简单的html


如上图所示每次点击发送按钮的时候,都会把商家id和打印内容发送到服务器,然后服务器把id和打印内容广播到每台商家端app上,app接收到信息检测接收到的id是否和本地设备id相等,相等则打印接收到的内容
可以通过这个视频感受一下

ps:需要注意的是cordova打包后会出现无法连接socket,但是网页正常的现象,这是因为cordova默认只允许”file”协议,这时候应该装个cordova-plugin-whitelist,让cordova允许其他协议
即:

1
cordova plugin add cordova-plugin-whitelist

官网文档提到
By default, navigations only to file:// URLs, are allowed. To allow others URLs, you must add tags to your config.xml:

cordova默认只允许file协议,所以下面需要修改config.xml,让cordova允许http协议,添加下面内容

1
2
3
4
5
6
7
8
9
<!-- A wildcard can be used to whitelist the entire network,
over HTTP and HTTPS.
*NOT RECOMMENDED* -->
<allow-navigation href="*" />
<!-- The above is equivalent to these three declarations -->
<allow-navigation href="http://*/*" />
<allow-navigation href="https://*/*" />
<allow-navigation href="data:*" />

这样就没问题了

消息队列

我们现在的是实时打印的,万一商家端手机网络中断,蓝牙关闭,不小心把app关了,这样就会错过要打印的内容,这可不能容忍,所以我们还得给加上消息队列的功能
思路很简单:
在服务器端创建一个空数组,然后监听打印成功事件,如果没数据传回来就把sendData事件传过来的id和打印内容的 json 字符串 push 到这个数组里,遍历这个数组,如果成功了,就从这个数组剔除打印成功的元素
然后给客户端的打印成功的回调那里加一个emit事件,emit打印成功这个事件
这样一个消息队列就搞定了,原理简单,但是还有很多要注意的,比如隔多长时间遍历一次数组,多长时间取消打印回收内存等等

2016/08/05 更新

最后发现用数组很难维护,最后我使用mongoDB解决的,我的做法如下:
设置一个保存数据到数据库的函数,status(是否已经打印)都为false

每次接收到sendData事件就把数据存到数据库,同时监听hadPrint事件,商家app每次完成打印都会触发hadPrint事件



hadPrint被触发时,会根据orderId来查找对应订单,同时设置该订单的status(是否已经打印)为true

最后设置一个定时函数,定时检测数据库里status为false的订单,找到了就把它打印,这样我们的项目基本雏形就完成了,由于商业项目,不能具体描述代码详情,只能简单介绍下思路,抛砖引玉,希望对读者有所帮助

最后附上成果:




还有又被我浪费的一卷打印纸

(完)