设计初稿.jpg

Vcenter的微信后台架构

先在文章头部放上一张架构图/设计图,我也不知道怎么称呼这个东西,将就着看吧。

在这个图上,我将网站分为了两个部分,一个是常规的API后台,也就是网站中间件
另一部分,通过路由,分出一个微信中间件,在路由中,使用 api.MyDomain.com/wechat/{wechatName}的方式来取得预先绑定的微信号,当然,如果我们要先写微信部分的话,这部分的信息和数据结构就只能先提前写到数据库里了,毕竟正式的网站后台还没有开写。

这其实只是一张草图,没有详细介绍微信目标Controller怎么来处理微信的逻辑,这就是这篇开发手册的目的所在。

在下面的描述中,所有的controller仅指代微信目标controller,意为 微信部分的主控制逻辑

实现方案选择

有了前面Database的教训,我们知道在Nodejs这样的web后台当中,一个Controller可以有两种实现方式

  • 单例模式,全局共享一个controller
    其缺点是配置无法实时调整,通过网站后台进行管理的时候需要添加许多细节代码
  • 对于每个ctx(context——KOA为每个请求创建的上下文)new一个新实例
    这样做会有一些好处,有利于实时更新配置

考虑到我们的controller不像数据库连接一样,其内在实现不会依赖第三方,因此我选择的是对每个ctx创建一个新的controller。

消息处理

微信后台主要做的事情其实就是提供一个消息接口,该接口接收微信传递的消息,并作出响应的应答。
要实现这个需求,我们需要提供若干个小的消息处理单元,称为unit,这些小单元通过组合,形成一个公众号的服务总和。

比如有实现关注后回复消息的unit,有实现关键词自动回复的unit,有实现特定关键词触发执行命令的unit,这些功能对应的消息类型和消息内容各不相同,有的属于各个公众号共有的功能,比如关键词自动回复,有的属于不同公众号各不相同的,比如关键词触发执行命令,这些都是需要考虑到的,这也是将不同功能划分为不同unit的原因,毕竟一个js文件里写几百个if也不好看。

好了,决定好消息处理的方式是将消息转换成对应的unit来处理,那么接下来的事情就很简单了,就是如何来进行消息的分发。

controller实现

首先我们要对微信的请求做出划分,微信会对这个链接发出两种请求,一种是校验绑定的,一种是正常消息。
两种请求都会在url后面带query参数,需要使用Token进行校验,因此会有这种代码:

if(this.Verify(ctx)){
    if(ctx.query.echostr){
         // 该请求为Token验证
        ctx.body = ctx.query.echostr;
    }else{
        // 该请求为消息请求
    }
}        

代码大概长这样,至于Verify怎么实现,就八仙过海各显神通了,腾讯的开发者文档也有详细的描述,自己看着实现即可,有朝一日Vcenter开源以后就比较方便了。

然后需要实现的就是消息请求的逻辑。
首先需要将xml转化成json格式,腾讯发给消息接口的消息类型均是post的xml格式
需要先通过xml2json转化成json格式,挂载在ctx.req.body这个对象上,这样就可以通过js代码正常的访问到。

然后需要通过消息的FromUserName字段,根据 wechat_app_name从数据库中取出这个用户的数据。

Vcenter当然是要支持多微信后台的啦,所以wechat_app_name是一个区分不同微信公众号的索引。

这一步其实与session的实现大同小异,只不过session是为了临时存储一个值

然后需要为该ctx初始化一个session,别忘了开发手册1里面提到的,我的session模块支持被动模式,直接通过FromUserName去初始化session即可,这样,我们的微信后台就可以维持一个有状态的消息处理了。

unit和controller对接

以上我们已经完成了所有的初始化工作,再接下来就是处理一个将消息转交给不同的unit来处理的问题。

在我的设计中,其应该是一种与数据库结合的方式,unit在本身声明的时候就分为两类,一类是公共的,一类是可选的,在中间件取得controller的消息的时候,需要从数据库中查询到这个wechat_app_name对应的可选的unit,将这些unit也导入。

而在消息处理时,需要进行这种操作,unit本身需要声明一个排序等级,当然,由于代码都是你自己写的,所以一般来说是不要出现重复的,一个消息需要按顺序遍历所有的unit,在将消息传递给unit的时候也会同时将中间件的回复方法作为callback写进去,如果unit调用了callback,则认为该unit处理了该消息,不再遍历剩下的unit,如果没有调用,则继续遍历,如果所有的中间件都没有回复消息,则自动回复success,防止腾讯弹出报错。

这种遍历的消息处理方式是我经过考虑的。

比如我们要实现一个语音控制的指令的时候,(微信公众号可以设置成语音消息自动识别为文字),那么我们就可以将这个语音转文字的unit的排序等级设的较低,使其先被运行,但是其他的unit依然可以顺次对消息进行处理。

这种队列式的调用对于模糊命令也有不错的效果,如果有多个unit需要监听模糊的指令,那么只有目标的unit会做出回应。

当然,这在后期也是可以作出优化的,比如可以通过在中间件中添加额外设置的方法,在session中指定优先的中间件,以免被其他的unit捕获并作出应答。

当然啦,看到这个unit队列的机制,我们就要首先为一些unit声明一个正确的排序的等级,比如关键字模糊匹配显然是优先级比较差的,应该在所有的都没有匹配的时候才进行消息处理。

结语

以上就是关于Vcenter微信Controller部分的相关构想,首先写这篇文档,也是为了帮助自己理清思路,如有词不达意或者表述不清的地方万请见谅,同时我也期待Vcenter能够早日面世