版权声明:
本文为博主原创文章,未经博主允许不得转载。关注公众号 技术汇(ID: jishuhui_2015) 可联系到作者。
在上两篇文章中,介绍了BPMN2.0和工作流定义语言(以下简称WDL),以及工单系统的业务流程设计。本文是工单系统系列的最后一篇,着重讲解工单系统的程序设计。
因为所有流程的配置和定义都在WDL文件中,所以必然会引入关于WDL的解析层,这是整个工单系统的基础。
除了解析层,还少不了执行层的存在,整个工单流程就是靠执行层去推动的。
如果对Activiti的源码有所钻研的读者,看完此篇文章后,会觉得非常类似,这是不可否认的,站在巨人的肩膀上才能看得更远更全。
Activiti:一直被模仿,从未被超越。
一、BpmnModel
这部分内容主要介绍WDL中各元素的设计,它们有个统一的名字:BpmnModel。这个名字来源于Activiti的源码,在此沿用之。
根据基于BPMN2.0的工单系统架构设计(上)文章中的介绍,WDL文件中可以提取五个关键的基本要素:
1、Definitions,这个是根元素,无可厚非;
2、Message,关于消息的定义,是Definitions的子元素,可以有多个消息的定义;
3、Process,整个流程的定义,也是Definitions的子元素,暂不支持SubProcess;
4、EventDefinition,事件定义,与其他元素区别较大,所以单独提取出来,包括了消息事件,定时事件;
6、FlowNode,可以理解为是Process的各个子元素,比如SequenceFlow,Task,Gateway等等。
BaseElement是最顶层的抽象,id和elementName作为共有属性,同时有一个validate校验方法,对元素的合法性进行校验。
因为是需要将一整个WDL文件解析出来,这其中就会包含很多元素对象,最后还需要一个真正意义上的BpmnModel进行装载,如下:
在上图的BpmnModel中,Process非常关键,因为其下有众多子元素(FlowNode),找到了Process就能找到其他的子元素。所以,Process的另外一个作用就是元素容器(Container),容器里面可以维护自己的子元素,如下是接口定义:
二、WDL解析器
Activiti有一个强大之处在于能进行可视化流程配置,只要了解BPMN2.0规范,就能很轻松的配置出想要的流程。
当然,本工单系统是没有必要去实现这个功能的,所以这一部分侧重讲解从WDL文件到BpmnModel的过程。
笔者从Activiti源码中看到了有一个interface,当中定义了所有Activiti支持的元素,当然也涵盖了上述提到的那些基本元素。该类名是BpmnXMLConstants,其内容较大,故不在此展示了。
笔者沿用了这个类,但是做了一点小改进:将元素标签和元素属性分开,做成了两个interface,如下所示:
Activiti中使用的解析工具是JDK自带的XMLStream,大概是不想引入新的依赖,增加开发者的负担吧!笔者使用的是Dom4j,而不是XMLStream。基于这一点考虑,笔者在设计的时候抽象出了顶层接口,如下所示:
BaseBpmnXMLParser接口中的泛型即为元素解析对象,比如XMLStreamReader,Dom4j中的Element对象等等。doParse方法即为解析的入口,WDL中所支持的各类元素标签都将有一个Parser实现。
三、WDL转换器
这一个部分的主要功能是WDL与BpmnModel的互相转换。
与解析器的设计类似,也是要根据XML的解析工具进行实现。
在Converter接口中,定义了三个方法,其中两个是WDL和BpmnModel的互转,目前仅实现了convertToBpmnModel。
还有一个是对BpmnModel的校验方法,校验不通过直接抛出Exception。
对于转换成功的BpmnModel,会存放在一个容器内,称之为BpmnModelContainer。
这个Container接口定义了对BpmnModel基本的添加,移除和查找方法。另外还有三个方法需要解释一下:
1、next。指示工单流程的下一个步骤,返回下一个步骤的元素。因为可能存在ParallelGateway,这就意味着下一个步骤将会有多个节点同步执行,所以返回结果是一个Queue对象,存放着下一个步骤要执行的节点元素。
2、conditionParse。在基于BPMN2.0的工单系统架构设计(上)文章中曾提及到了WDL中表达式的解析。在container中,此方法主要解析的是网关的执行条件,配合next方法使用。
3、fill。这个方法是用于填充Container,该方法的调用时机是构造BpmnModelContainer的时候,当然是要借助WDL转换器实现。如下所示:
@Configuration
@EnableConfigurationProperties(WorkflowProperties.class)
public class BpmnXmlManager {
/**
* 构造BpmnModelContainer,解析WDL文件,填充BpmnModel
*/
@Bean
public BpmnModelContainer bpmnContainer(WorkflowProperties workflowProperties, BaseBpmnXMLConverter converter) throws Exception {
BpmnModelContainer container = new BpmnModelContainer(workflowProperties, converter);
container.fill();
return container;
}
/**
* 构造XML解析工厂,默认是Dom4j的解析方式
*/
@Bean
@ConditionalOnMissingBean(BpmnXMLParserFactory.class)
public Dom4jParserFactory xmlParserFactory() {
return new Dom4jParserFactory();
}
/**
* 构造XML转换工厂,默认是Dom4j的转换方式
*/
@Bean
@ConditionalOnMissingBean(BaseBpmnXMLConverter.class)
public Dom4jConverter xmlConverter() {
return new Dom4jConverter();
}
}
四、工单执行
前三部分工作完成之后,工单就可以正常流转了。
1、start,工单的发起;
2、accept,后台操作人员接单;
3、doTask,执行工单节点(用户任务);
4、getWorkOrderDetail,获取工单详情,包含工单基本信息,工单动态,工单客户信息,工单任务列表等;
5、addWorkOrderProgress,添加一条工单进度信息,方便客户端查看工单进度;
6、nextElement,查找下一个工单节点,查找范围涵盖了Process的所有子元素,是推动工单流程的重要方法。
对于WDL中的Task和Event都有设计其对应的Runner,用于执行其内在的业务逻辑。
在Runner执行期间,会遇到一些定时任务,比如定时边界捕获事件,笔者使用了Quartz开发框架,整合了SpringBoot进行定时任务的管理。
五、总结
本篇文章对工单系统的程序设计做了较为详细的讲解,对于整个工单系统的设计也就到此为止了。如果笔者能通读工单系统设计系列的三篇文章,对于今后的工单系统设计也将会有所启发。