简介
Activiti是一个基于Apache v2开源协议的工作流引擎,工作流引擎全称是业务流程管理(Business Process Management),一种定义了数据流转行为的流程框架,业界有很多工作流引擎,均是BPM协议的实现,例如Activiti,Flowchat等,本文基于Spring Boot,来简单实现一个图书馆的业务管理。
环境介绍
- Spring Boot 2.1.6.RELEASE
- Activiti 6.0.0
- Mysql 5.7
Activiti 6.0.0官方文档:https://www.activiti.org/userguide/
代码实践
依赖
1 |
|
配置
对于Activiti,本质上是提供了一个通用框架来处理业务数据,即传统意义上的数据和业务分离,业务流框架自己提供了Repository来操作数据,所以这里需要自己配置单独的数据源,当然,也可以直接使用参数注入的方式将spring boot的数据源给注入进来,这里我采用单独数据源的方式。
1 | spring.activiti.datasource.driver-class-name=com.mysql.cj.jdbc.Driver |
使用注解方式创建配置类:
1 |
|
主要是第一个ProcessEngine,在配置流程引擎的时候,传入了两个参数,第一个参数是Spring Boot中的事务管理,第二个参数是一个全局事件监听,关于监听后面会说到。最为常用的是runtimeService
、taskService
、historyService
,其中第一个是流程运行过程中查询流程的业务类,第二个是流程运行过程中的任务类,第三个是一些历史业务类,这个历史业务,在配置为历史级别为Full的时候,会将流程过程中任务的本地变量、任务全局变量都记录下来。
基本配置就这些,配置完成以后,启动项目,框架会自动生成28个表。
- ACT_EVT_LOG:事件日志;
- ACT_GE_BYTEARRAY:流程定义和流程资源,比如bpm.xml规则文件和流程图等;
- ACT_GE_PROPERTY:系统相关属性;
- ACT_HI_ACTINST:历史流程实例,记载了流程中每个节点信息;
- ACT_HI_ATTACHMENT:历史附件;
- ACT_HI_COMMENT:历史的说明性信息;
- ACT_HI_DETAIL:历史的流程运行中的细节信息;
- ACT_HI_IDENTITYLINK:历史流程运行过程中用户关系;
- ACT_HI_PROCINST:历史流程实例,记载了流程的开始-结束等信息;
- ACT_HI_TASKINST:历史任务实例;
- ACT_HI_VARINST:历史变量实例;
- ACT_ID_GROUP:组信息;
- ACT_ID_INFO:身份信息;
- ACT_ID_MEMBERSHIP:身份信息-组信息中间表;
- ACT_ID_USER:身份信息-用户信息;
- ACT_PROCDEF_INFO:死信任务;
- ACT_RE_DEPLOYMENT:部署信息;
- ACT_RE_MODEL:模型信息;
- ACT_RE_PROCDEF:已部署的流程定义;
- ACT_RU_DEADLETTER_JOB:运行失败任务表;
- ACT_RU_EVENT_SUBSCR:运行时事件;
- ACT_RU_EXECUTION:运行时正在执行的实例;
- ACT_RU_IDENTITYLINK:运行时用户关系表;
- ACT_RU_JOB:运行时作业表;
- ACT_RU_SUSPENDED_JOB:运行时暂停任务;
- ACT_RU_TASK:运行时任务;
- ACT_RU_TIMER_JOB:运行时定时任务;
- ACT_RU_VARIABLE:运行时变量表;
这是几个表主要的描述,详细表字段的描述移步:https://blog.csdn.net/hj7jay/article/details/51302829
需求
在使用Activiti之前,通常会自己定义一个业务表,使用业务表的id作为工作流框架中的业务key,业务key和流程实例id一一对应。
1 | CREATE TABLE `user_business` ( |
前后端分离创建规则
如果不使用Activiti Designer来画图,也可以通过Spring MVC暴露Restful API,在Service层组装规则,例如传进来一个json格式的字符串:
1 | { |
node即为图中每一个节点,link是节点之间的联线,事先创建节点和连线的实体类,这部分不做详细介绍。然后反序列化,并创建流程图:
1 | List<ActivitiNodeElementDto> nodeElementDtoList = GSON.fromJson(jsonObject.get("nodeList"), new TypeToken<List<ActivitiNodeElementDto>>() { |
上面代码依赖的一些方法无非是创建各种节点:
1 | /** |
执行项目,访问接口,可以在process目录下看到新创建的流程图以及对应的规则xml:
发起某个流程
1 | //设置发起人 |
获取待处理任务列表
1 | public List<ActivitiTodoTaskDto> getTodoTaskList(Integer uid, Integer currentPage, Integer pageSize) { |
完成某个任务
1 | public Boolean completeTask(Boolean approval, Integer uid, String taskId, String approvalContent) { |
上述的mapper都是业务自己的mapper,这里的处理方式除了直接使用taskService.complete(taskId, variables);
这种方式直接完成任务以外,还可以使用taskService.claim();
来抢占任务。
获取某个流程实例的节点列表
1 | //获取历史任务实例 |
监听整个流程状态
在这个例子中,流程有以下几种类型:
1 | public enum UserBusinessStatus { |
在一开始基础配置的时候,曾经配置一个全局监听,这里就是发挥作用的时候了。
1 | 4j |
AbstractEventHandler
:
1 | public abstract class AbstractEventHandler { |
作为本项目的抽象事件监听,指导手册https://www.activiti.org/userguide/中3.17.6节中详细描述了所支持的事件类型,以及发生的时间。常用的事件有:任务审批拒绝/用户撤销、任务审批超时、流程审批通过。对于第一个事件,上面的操作是直接从运行中表删除(runtimeService.deleteProcessInstance
),文档中有该描述:
1 | PROCESS_CANCELLED:A process has been cancelled. Dispatched before the process instance is deleted from runtime. Process instance is cancelled by API call RuntimeService.deleteProcessInstance |
写一个监听继承抽象监听:
1 | 4j |
同理,超时对应ActivitiEventType.TIMER_FIRED;
事件,这个事件会由每个任务节点绑定的过期边界BoundaryEvent
触发,审批完成对应ActivitiEventType.PROCESS_COMPLETED
事件,三种分别调用下面三个方法:
1 |
|
对于是用户主动撤销还是审批不通过导致的终止,可以通过变量判断,即审批不通过设置了不通过标记,而主动撤销没有该变量。
流程实例节点图
获取流程实例图,将图片base64编码返回展示,需要注意处理不同系统下的展示:
1 | HistoricProcessInstance historicProcessInstance=historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); |
总结
这次的工作流整合中遇到不少的问题,但是要始终牢记工作流只是简化了对数据的操作而已,最终业务方依然是自己的代码,7.x系列文档还不齐全,感谢大神咖啡兔的博客:
http://www.kafeitu.me/activiti-in-action.html以及好友Hacken丶分享的部分公司内部处理方案。