本系列所有文章请访问:概述
jxTMS所实现的微信机器人其工作的逻辑闭环是:
1、微信机器人向用户显示菜单
2、用户输入菜单选项
3、jxTMS提示用户输入
4、用户输入
5、所有等待中的变量全部完成输入后,jxTMS显示所有的用户输入【包括默认值】,并提示用户确认
6、用户确认后,将所有用户输入【包括默认值】合并到事件响应函数的输入流中
7、jxTMS将事件响应函数投入运行
8、jxTMS将程序执行结果输出显示给用户
9、向用户显示菜单,然后等待用户输入菜单选项
所以用户输入时有两个状态:
菜单选择状态
变量输入状态
jxTMS需要跟踪并适时切换用户的当前状态,以准确的从用户输入流中分离出菜单选择操作和变量值输入。其中,变量的输入是交互式的,即微信机器人先提示用户现在需要输入什么内容,然后等待用户输入,用户输入后再提示输入下一个变量,等所有变量输入完毕,然后再向用户显示其所有的输入值【包括未输入变量的默认值】,然后等待用户确认。
用户需要在ui的引导下,充分理解业务办理的目的、要求其输入的是什么、有什么要求,准确表达、准确操作。而交互式对话的好处就在于信息比较集中、精准,用户的注意力会比较集中,不太容易被多余的信息干扰而理解错误,但需要在每一步都有足够的说明与引导。
web界面由于信息丰富而且是整体呈现给用户,所以其数据控件前的提示词就不需要很长很多,这在web界面下是不会影响用户对业务的理解。但提取出来用到微信机器人中显示给用户看,就会因为信息量太小而让用户困惑,不知道要干什么、该如何做。
所以,jxTMS的微信机器人设计成了输入输出相分离,不会将从web界面的定义中提取到的提示词用于交互输入。原因就在于web界面中对单个数据的提示词过于简短,用在交互式输入时会由于信息不足而影响用户的理解。因此微信机器人专门设计了交互式对话的定义语句,以提供足够的说明信息来帮助用户完整、准确的理解当前场景下输入的目的、意图与要求。
输入需要校验。我们都知道业务管理系统有句名言:垃圾进、垃圾出。而校验就是用来检验输入的数据是否满足某种要求的、确保数据的源头质量,在jxTMS中叫做:数据约束。由于jxTMS已经具备了数据约束的能力,所以直接引用就好了。
默认值在web界面中用处不大,因为web界面有丰富的控件,可以帮助用户快捷输入,使用默认值所提高的效率意义不大,反而需要通过让用户自己选择来进一步明确其意图同时还可以避免用户由于信息量过大,在还没完全理解的情况下就直接操作带来意外。即,web界面中由于信息丰富、操作效率高,反而要限制默认值的使用,以避免用户误操作【即实际意图未必是默认值所表达的,但由于信息量太大,用户分心了,没注意看也没仔细去理解】。
但在交互式输入中,默认值在效率提升方面就价值巨大了,同时由于是用户一个字一个字输入的,其肯定是足够冷静、足够想明白到底要干什么的了,所以在交互式对话中,能用默认值就一定要用,除非默认值会导致重大的表达错误。
通过上面的讨论,我们已经知道了,交互式输入的一个输入单元,就是一个五元组:
变量名,可以让事件响应代码用self.getInput函数读取到用户的输入值
提示说明,在用户输入前推送给用户,以帮助用户理解要输入的信息是用来干什么的、有什么样的要求
校验表达式,在用户输入后,用来验证用户输入是否符合要求
校验规则说明,校验失败后会向用户推送的输入要求说明
默认值,用多数人的同样输入来减少用户输入、提高效率
注:实际中还有一个区分是否需要base64编码的指示,因为如果需要输入的数据同时可以显示在微信机器人与web中,对于web界面中的某些取值是base64编码的数据控件,如textarea、htmlContainer等,就也必须进行base64编码。因为这些控件在web端时都可能输入特殊字符,不用base64编码就无法正确的传递
所以其定义就是用这五个元素对事件响应函数进行修饰就可以了:
PyObject wxPrompt(String bind,String prompt,Object defaultValue,String verifyStr,String verifyRuleDescr,Boolean base64)
各变量的意义非常直白,就不复赘述了。wxPrompt必须跟在wx修饰或其衍生出的wxOP、wxDisp、wxDataTable之后【python修饰符是最开始的修饰符最后使用】,示例如下:
@myModule.wxPrompt('var2','变量2'.decode('utf-8'),'tv2','len>2 and len<5','长度应大于2且小于5'.decode('utf-8'),False)
@myModule.wxPrompt('var1','变量1'.decode('utf-8'),'tv1')
@myModule.wx('tms','第一个测试'.decode('utf-8'),'测试'.decode('utf-8'),None,False)
@myModule.event('cmd', 'wxTest')
def wxTest(self, db, ctx):
......
上述示例给【测试->第一个测试】这个命令型微信功能添加了两个交互式输入变量:var1和var2。其中var1由于不需要检验,所以采用了简写,var2则限制其长度只能是3或者4个字符。两变量都还提供了默认值。大家可以看一下这个定义的实际交互过程:
在父菜单中输入3选择【第一个测试】执行时,由于两变量都提供了默认值,所以jxTMS直接显示用户输入确认表,请用户确认其输入。用户此时对var1的值不同意,输入1想修改var1的值,这时jxTMS发送输入提示,用户再次输入后,jxTMS再次显示用户输入确认表。此时,如果用户想修改var2的值,但输入不符合要求时:
如果最后用户确认其输入,输入y即可。
注:目前尚不支持【取消所有输入,终止执行】。这是由于微信机器人的菜单选择和输入是混流的,需要依靠严密的状态跟踪来确定用户当前的输入是分流给菜单选择还是变量输入,打断闭环的【选中菜单项->用户输入->执行->输出结果->显示菜单】处理逻辑再行恢复出原本的流会导致处理逻辑异常复杂,很可能导致状态恢复错误。而jxTMS对微信机器人的支持是全新的功能,整个工作逻辑还未经大量应用考验,所以笔者权衡再三,暂时取消了该功能
用户输入完毕,jxTMS会将用户输入重定向到事件响应函数,用self.getInput函数即可读取到相应的用户输入了。
在业务办理时,有时会出现突发性的输入请求。比如,在新成员注册时,有可能出现重名。jxTMS解决重名的办法是不直接使用名字,而是使用简称:如果没有重名就直接用名字做简称,如果有重名则需要起一个区别性的简称。那么在新成员注册时,简称就不是必须要求用户输入的【而且不重名时,各人简称的默认值还不一样】,但当用户提交注册时,发现有重名则需要提示用户输入一个简称,而当输入的简称依然有重名时,还需要反复做出提示直到没有重名。
注:哈,笔者曾就职过的一家企业,三十多人有三个人重名,最后:大xx、小xx、女xx,还得写到通讯录中:) 这个印象深刻到在jxTMS中,一个组织有三个名字:给人用的简称、给人用的唯一性全名【一般是工商注册名】、给机器用的英文唯一名【数据库、消息接收都以此名为准】
针对这样的需求jxTMS增加了一个临时性输入的功能:
#设置临时性输入提示,需要临时增加几个就调用几次
setTmpExpect(String bind,String prompt,Object defaultValue,String verifyStr,String verifyRuleDescr)
#提交临时性输入提示
commitTmpExpect(IDBop db,context ctx)
#结束临时性输入提示
clearTmpExpect()
各函数以及参数的意义一目了然,我们举个例子就好:
@myModule.wx('tms','临时提示测试'.decode('utf-8'),'测试'.decode('utf-8'),None,False)
@myModule.event('cmd', 'wxTest2')
def wxTest2(self, db, ctx):
v2 = self.getInputString('v2')
if v2 is None:
#没有v2变量的输入值,临时设置一个v2变量的输入
self.setTmpExpect('v2','临时需要输入一个字符串'.decode('utf-8'),None,'len>2','长度大于2'.decode('utf-8'))
self.commitTmpExpect(db,ctx)
return False
jx.log('v2:{}',v2)
#由于设置了临时提示,必须加以清除,以避免对其后的临时性提示造成干扰
self.clearTmpExpect()
return False
则【临时提示测试】在执行时,发现缺少v2变量,就临时为其创建一个临时性的输入提示,然后请求输入v2变量,在得到v2变量后,必须及时调用clearTmpExpect函数来取消临时性输入的状态。
jxTMS目前已打包为docker容器,可以下拉jxTMS的docker镜像并按jxTMS使用示例尝试使用。