工作流设计简介
适读人群:参与过工作流功能开发的程序员。(有可能你并不知道,你现在做的功能就是一个工作流功能)
一、 什么是工作流
举个很土的例子。程序员出差,回来后要报销票据。公司规定票据需要由员工所属经理审批,然后交由财务审批,财务审批通过程序员才可以拿到钱。在这个过程中,经理可以驳回申请,财务也可以驳回申请,驳回后单据回到程序员编辑草稿的状态。见下图。
这就是一个工作流,一个单据由多个角色的用户审批,不同角色看到的界面是不同的,每一个状态都对应一个界面,例如程序员能够看到“开始”和“草稿”的页面,能够编辑报销单据的名称和金额等信息,可以点击“保存草稿”和“提交经理审批”按钮;经理能够编辑“经理审批理由”,可以点击“同意”和“驳回”按钮;财务能够编辑“财务审批理由”,可以点击“同意”和“驳回”按钮等。
二、 为什么要使用工作流
因为客户的需求总是在变更,我们开发程序编码到一半的时候需求就会有颠覆性的修改。好的设计师不是设计出多么牛X的构架,而是设计出的程序易于快速适应需求的变更。下面来看看客户是如何变更需求的。
以上一节中的报销审批为例。客户提出修改要求,所有程序员的报销都提交给经理审批,那经理每天不用干正事了,时间都花在审批报销上了,以后报销这种小事,大于1000元的再来找经理审批,小于等于1000元的直接找财务就行了。流程见下图。
过两天可能客户又说,还是要经理审批500元以上的审批吧,毕竟有些时候那些500-1000元的报销单据财务不能直接做主。这样的修改倒还好说,比较只要修改一个系统常量就搞定,如果客户再说,我们不需要经理审批了,整个流程只要财务审批即可,那程序的修改可就大了。流程见下图。
我相信各位肯定见过比这更狗血的需求变更。牢骚抱怨不能解决问题,更合理的设计才是王道。所以如果你的需求需要这种不同人员对一个单据进行的审批,那么请使用工作流。
三、 工作流的组成
本文不具体介绍工作流的编码,如果各位想落地了解工作流的具体编码,可以查阅微软的WF工作流引擎或者JBPM开源工作流引擎。
工作流只要有节点状态和流转事件两部分组成。
以上面报销流程为例,包括“开始”在内的所有矩形都是节点状态(“草稿”、“经理待审批”、“财务待审批”、“财务审批通过”)。所有的节点状态都对应一个界面。相应的人员在对应的节点状态打开报销单据的时候才能进行自己的操作,不然只能进行只读查看。例如当前单据处于“经理待审批”状态,那么如果经理打开这个单据能够编辑自己的审批意见,并且可以选择点击“通过”或者“驳回”按钮。如果程序员进入这个单据,只能看到自己以前编辑过的内容,不能再次编辑,也不能进行经理和财务的操作。
到这里就不难理解流转事件了,流转事件就是一个状态到下一状态的触发事件。还以报销流程为例,图中由一个节点状态指向另一个节点状态的线既为流转事件。程序员点击“提交”按钮,就是将当前为“草稿”节点状态的单据流转到了“经理待审批”状态的事件。
节点状态与流转事件的关系一般保存在配置文件中,例如JBPM就保存在XML文件中,WF更直观,它附带一个工作流的图形编辑器,编辑好图形后自动保存到对应的XAML文件中。以上面用户的需求修改为例,如果客户要求整个流程中不需要经理审批环节,那么只需要在配置文件中做修改,讲“草稿”状态下的“提交审批”流转事件直接指向“财务待审批”即可,只是这样一个简单的修改即可完成客户那复杂的修改需求,所以这里就是工作流的精华所在了。
除此之外我们还可以改造工作流引擎,在流转事件中加入一些通用功能,例如邮件提醒、短信提醒等功能。例如增加如下需求,所有报销审批操作执行成功后都要邮件提醒下一个状态环节的负责人,程序员提交“草稿”后单据进入“经理待审批”状态,那么就需要邮件通知经理,催促他尽快处理这位经理名下的报销单据。
四、 需要注意的一些事情
我在这里给出一些工作流节点命名的一些建议。尽量以“待”字开头,例如“经理待审批”、“财务待审批”,这样命名可以体现出当前状态需要由何种角色的人员来处理,而且也便于需求的变更。这里强烈不推荐使用“已”字命名,例如“程序员已提交”、“经理已审批”等。那么如果这样设计,程序员在草稿状态提交后就进入“程序员已提交”状态,经理审批通过后进入“经理已提交”状态。
这时客户变更需求,要吧“程序员已提交”状态删除,那么这时候程序员在草稿状态提交后就流转到了“经理已审批”状态。这样理解起来就有些混乱了。
这里需要声明一点,图中的“结束”状态在设计程序的时候不对应具体界面,指向“结束”的流转事件也就没有具体的按钮触发。也就是说“结束”状态只是一个虚的状态,指向“结束”状态的节点状态我们都认为它是一种终结的状态。例如“财务审批通过”状态就不可能再向其他状态流转了,那么它就是一种结束的状态。上面的例子中只有一个结束状态,我们这里再做一次需求的变更,所有的驳回操作不回到“草稿”状态,都流转到对应的结束状态。如图所示。
其实所有的人都希望将工作聚焦在当前需要完成的工作,其次是将要进行的工作。而那些已经完成的工作只有年终总结的时候需要统计一下。例如经理最希望看到当前待审批的报销单,如果经理忙完了手头的工作不免好奇心起会去关心一下还处于草稿状态的报销单,至于“经理驳回”状态下的报销单,可能只是年终总结的时候大BOSS需要看一下数量而已。所以在设计经理的报销单查询页面时,应该默认查询出所有“经理待审批”状态的报销单。所有的角色都希望默认查询出自己对应状态的报销单据。财务的操作也应该是如此这里不再冗述。再举一个例子,有时候大BOSS希望知道某一时间段内有多少报销单已经完成了(这是所说的已完成就是已经结束的报销单,包括“财务审批通过”、“经理驳回”和“财务驳回”),那么只需要从工作流中查询出指向结束的节点状态报销单即可。
我再声明一点,虽然“经理驳回”与“财务驳回”在某些业务上是相同的,但是请不要因为节省编码而将这两种状态设计成一种状态,比较人家客户的业务人员在提出需求的时候是分开命名的,这也就意味着这是两种不同的业务,谁知道哪天客户心血来潮需要在“经理驳回”状态下添加一个流转事件允许返回到“草稿”状态。到那时候可能我们可怜的程序员就要在某些代码上疯狂的加标志位了。
五、 写在最后
细心的人可能会有这样的想法,程序员在“草稿”状态下可以无限制的保存当前编辑的报销单,那么这个保存事件是不是应该也是一种流程,是不是应该从“草稿”到“草稿”的流转呢?
本文只是简介了一下简单工作流的设计,在实际的设计中还有很多复杂的流程,例如某些业务需要在某个状态节点由一群人来审批,这群人审批都通过了才可以流转到下一个节点;某些业务是由多个状态起始,最后结束的时候汇聚到一个状态,有点形似Y型流程设计。