欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Activiti 工作流入门

程序员文章站 2024-03-04 15:14:35
...

基础知识

工作流:
可以认为是一系列的规范流程,让业务按照拟定的规则处理和运转,本质上是一系列逻辑相关的活动的集合。例如某校学生申请奖学金这一业务,必须按照“学生申请→辅导员审批→学生处审批→教务处审批”这一流程运转,也就是“学生申请”、“辅导员审批”、“学生处审批”以及“教务处审批”这些活动的集合。最原始的时候,在系统中完成这样的功能,需要通过一系列的独立活动页面以及修改数据库中的一些标记状态来实现任务的流转的,这样的做法在开发和维护上都是十分困难的,新增任务结点,网关判断,添加处理人员都十分繁琐,之后就有了工作流引擎。
工作流引擎:
实现流程,让任务能够按拟定的流程自动的驱动下去,不再需要我们实现流程的驱动,记录流程的流转情况等等,提供流程生命周期的管理功能,Activiti就是一种工作流引擎。
工作流平台:
如果每一个系统(项目)都有工作流,就会产生很多相同的代码,为了更好的管理工作流,为了让工作流与项目解耦,有的时候把工作流剥离出来做成微服务的形式,搭建一套完整的工作流平台,对外提供接口,让各个业务系统更专注于自己的核心业务。

Activiti 6.0 架包

查看Activiti引擎的架包如图所示,可以发现其主要包含两个部分:db以及engine。
Activiti 工作流入门
Activiti对流程的定义,创建,维护以及查询的实现都是是通过对表的操作完成的,Activiti有一套自己的表,都是以“ACT”打头命名的,记录了流程定义等相关信息(”ACT_RE*”)、流程驱动过程中的信息(“ACT_RU*”)、流程历史相关信息(“ACT_HI*”)以及一些通用数据(“ACT_GE*”)等。如下所示:
Activiti 工作流入门
Activiti的ORM是通过Mybatis实现的,所以回到之前提到的db部分,这部分的内容主要包含对以上提到的28张表的创建,及删除语句。表不需要我们自己创建,初始化的时候可以配置自动创建,支持db2,h2,hsql,mysql,oracle,postgres等数据库。举个Spring boot中配置的例子:

@Configuration
@EnableConfigurationProperties(ActivitiProperties.class)
public class ProcessEngineAutoConfiguration extends DataSourceProcessEngineAutoConfiguration {
    @Configuration
    @EnableConfigurationProperties(ActivitiProperties.class)
    public static class ProcessEngineConfiguration extends DataSourceProcessEngineConfiguration {

    }
}

其中ActivitiProperties.class中默认的策略如下:

//表不存在就自动创建表
private String databaseSchemaUpdate = "true";

还可以配置为“false”(不自动创建表),“drop-create”(先删除再创建表)等,此处不详细介绍Spring boot项目中的配置与整合,主要是介绍Activiti自身的入门内容。
db中的mapping路径下是表操作相关的sql语句xml文件(用于接口映射,实现对表的操作,不理解的话需要去看看ORM以及Mybatis相关的内容),剩下的就是相关配置文件以及升级等内容。如下图所示:
Activiti 工作流入门
相对的,engine中主要是相关的接口及其实现。Activiti引擎有七大Service接口:

RepositoryService:流程仓库Service,用于管理流程仓库,例如部署,删除,读取流程资源;
IdentifyService:身份Service,可以管理和查询用户、组之间的关系;
RuntimeService:运行时Service,可以处理所有正在运行状态的流程实例、任务等;
TaskService:任务Service,用于管理、查询任务,例如签收,办理,指派等;
FormService:表单Service,用于读取和流程、任务相关的数据表单;
HistoryService:历史Service,可以查询所有历史数据,例如流程实例、任务、活动、变量、附件等;
ManagementService:引擎管理Service,和具体业务无关,主要是可以查询引擎配置、数据、作业等。
以上的Service接口都是Activiti封装好的,底层实现都是通过Mybatis转化为对28张表的操作,应用程序中经常会用到以上的Service,功能十分强大。

Activiti 工作流生命周期

在Activiti中,工作流的生命周期主要包含以下几个方面:

  1. 设计流程模型,生成流程资源
  2. 根据流程模型生成流程定义
  3. 配置工作流人员
  4. 根据流程定义启动流程生成流程实例
  5. 流程实例由一系列任务实例构成,任务自动流转
  6. 修改流程模型,发布新的流程版本优化流程

流程模型

Activiti为我们提供了设计流程的图形用户界面,该界面的渲染内容是根据stencilset.json文件生成的,该文件的资源网上很容易获取到,拓展也很方便,开发人员可以根据自己的需要定制化的修改其中的内容。下面举个简单的流程模型创建的例子:
Activiti 工作流入门
上图就是一个根据stencilset.json文件渲染的流程设计界面,可以在系统需要的地方接入这个页面,可以看到左侧是元素菜单,功能是非常丰富的,可以设计出非常复杂的流程,通过拖拽等操作可以在右侧的画板中设计我们的业务流程。
当我们设计完流程之后,生成的主要内容包括模型ID,模型名称,模型的key,以及我们设计的流程内容的json数据,上图中的json数据部分如下图所示,此处不过多叙述,都是activiti自动生成的,了解便可。

Activiti 工作流入门
其主要内容包括resourceId以及其中的箭头,任务结点等信息。
保存设计好的流程模型,核心实现主要通过调用之前提到RepositoryService来实现,如下所示:

//引入架包后Service可直接自动注入
@Autowired
private RepositoryService repositoryService;
//未编辑模型,只保存模型名字和key
Model newModel = repositoryService.newModel();  
newModel.setMetaInfo(modelObjectNode.toString());  
newModel.setName(name);  
newModel.setKey(key);  
repositoryService.saveModel(newModel);
//也可以通过ProcessEngine获取Service
@Autowired
private ProcessEngine processEngine;
//获取之前保存的模型
Model model = processEngine.getRepositoryService().getModel(modelId);
//添加模型与之前提到的json的关联关系
processEngine.getRepositoryService().addModelEditorSource(model.getId(), values.getFirst("json_xml").getBytes("utf-8"));
//保存一张根据json数据生成的流程图片
processEngine.getRepositoryService().addModelEditorSourceExtra(model.getId(), result);

提及的只是部分核心实现,并非完整代码,主要体现如何使用Activiti实现工作流的操作,以上方法涉及的主要是ACT_RE_MODEL表以及ACT_GE_BYTEARRAY表,封装的Service方法底层也是对这两张表进行操作。查询当前所有的模型也是通过RepositoryService实现,举个例子:

ModelQuery modelQuery = repositoryService.createModelQuery();
if(StringUtil.isNotEmpty(modelKey))
{
    modelQuery.modelKey(modelKey);
}
if(StringUtil.isNotEmpty(modelName))
{
    modelQuery.modelNameLike(modelName);
}
if(StringUtil.isNotEmpty(modelId))
{
    modelQuery.modelId(modelId);
}
Page<Model> p = new Page<Model>(page, rows);
int[] pageParams = p.getPageParams(modelQuery.list().size());
List<Model> list = modelQuery.listPage(pageParams[0], pageParams[1]);
return new Datagrid<Model>(p.getTotal(), list);

在Activiti中,查询可以使用标准查询和Native查询,标准查询即我们上面代码中的形式:需要创建一个指定类型的Query对象,然后使用链式编程的方式设置查询参数即可,就是一个条件查询。这样的查询不能支持复杂的多表连接查询,所以有时候就NativeQuery接口。这两个接口的api就不详细介绍,命名都很清晰,看看就明白。
这样,我们设计并创建了流程模型,信息存储在ACT_RE_MODEL、ACT_GE_BYTEARRAY两张表中,主要包括流程的基础信息,以及设计页面的json数据和一张用于展示模型的png图片。

流程定义

有了流程模型,就可以通过部署生成流程定义,还是通过RepositoryService实现:

Model modelData = repositoryService.getModel(modelId);
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
byte[] bpmnBytes = null;

BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
bpmnBytes = new BpmnXMLConverter().convertToXML(model);

String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes)).deploy();

这一个过程主要就是生成流程的XML文件,创建流程模型的时候我们有了json数据,部署就可以将json数据转换成工作流中认识的xml数据。上文中提到的流程模型部署生成流程定义的xml数据如下所示:
Activiti 工作流入门
部署的流程定义的数据存储在ACT_RE_PROCDEF、ACT_RE_DEPLOYMENT以及ACT_GE_BYTEARRAY表中,同理的,查询流程定义也很简单:

ProcessDefinition pd =repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();

查询流程定义的XML:

InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),resourceName);

实际执行的其实是以下sql:
select * from ACT_GE_BYTEARRAY where DEPLOYMENT_ID_ = ? AND NAME_ = ?
可以看到,底层还是对一些表的操作。
流程定义是有**和挂起状态的,只有**状态的流程定义才能用来启动流程实例。**和挂起状态的转换还是通过调用API实现。

流程实例

有了流程定义,就可以根据流程定义的key启动流程,每一次启动流程就会生成一个流程实例,比如说设计好了请假的流程模型,通过流程模型生成了流程定义,并**该定义之后,每一次学生提交请假申请就生成一个请假的流程实例,流程实例由一个个任务实例构成,如审批任务。
启动流程使用的是RuntimeService:

ProcessInstance pi = processEngine.getRuntimeService.startProcessInstanceByKey(procDefKey);

可以在启动流程的时候传入一些业务参数:

ProcessInstance pi = processEngine.getRuntimeService.startProcessInstanceByKey(procDefKey, variables);

下图是启动流程过程中的项目日志,通过日志我们不难发现,启动流程同理也是对Activiti的相关表进行操作,包括历史表,任务实例表,用户表,运行实例表等等:
Activiti 工作流入门

任务实例

启动流程实例的时候,会自动驱动到第一个任务结点,也就是说会生成任务实例。查看当前的活动任务(即流程实例运转到的流程结点):

TaskQuery taskQuery = this.taskService.createTaskQuery();

if (!isEmpty(procDefName)) {
    taskQuery = taskQuery.processDefinitionName(procDefName);
}
if (!isEmpty(procDefKey)) {
    taskQuery = taskQuery.processDefinitionKey(procDefKey);
}
if (!isEmpty(taskName)) {
    taskQuery = taskQuery.taskName(taskName);
}
if (!isEmpty(owner)) {
    taskQuery = taskQuery.taskAssignee(owner);
}
totalSum = taskQuery.list().size();

以上便是接口提供的带条件的查询。当需要推动此流程时,只需要调用以下方法完成该任务,activiti就会自动的驱动流程的运行,到达下一结点,直至结束。

taskService.complete(taskId, variables)

指派人员

流程的设计,包括人员的指派,也就是说当流程流转到某一任务结点时,哪些人可以看到该任务,拾取该任务,或者推动该任务的流转。
在Activiti中,如果给任务指派了特定的人员,那么只有特定的人员能看到该任务并推动该任务流转;如果给任务指派了候选人,那么所有候选人都能看到该任务,当其中某一个候选人拾取了该任务,其余候选人无法看到并拾取该任务,只有拾取了任务的候选人能推动该任务;
Activiti实现这一功能实际上就是在ACT_RU_TASK表的assignee字段中存储了一个字符串,该字符串标识唯一的处理人员。所以必须要有一套与业务相关的用户表,解决方案有两种:

  1. Activiti有一套自己的简单的用户和组的表,我们可以把需要对接工作流的业务系统的用户数据导入Activiti自己的表中,自己维护一套。这种方案比较麻烦。
  2. 可以让对接的业务系统提供用户相关的接口,工作流平台的用户信息通过调对接的业务系统的用户接口获取和设置。

总结

Activiti工作流引擎能够很好的管理工作流,实现流程的自动驱动。它提供了7大Service接口已满足开发需求,底层是通过Mybatis对28张表操作而实现。
Activiti还有很多高级特性,还有很多细节的东西在本文没有涉及。有机会的话在之后补充博文记录学习。