一 介绍
1.1 Activiti 介绍
Activiti 是由 jBPM (BPM,Business Process Management 即业务流程管理) 的创建者 Tom Baeyens 离开 JBoss 之后建立的项目,构建在开发 jBPM 版本 1 到 4 时积累的多年经验的基础之上,旨在创建下一代的 BPM 解 决方案。
Activiti 作为一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调 度。 Activiti 作为一个遵从 Apache 许可的工作流和业务流程管理开源平台,其核心是基于Java的超快速、超稳定的 BPMN2.0 流程引擎,强调流程服务的可嵌入性和可扩展性,同时更加强调面向业务人员。
Activiti 流程引擎重点关注在系统开发的易用性和轻量性上。每一项 BPM 业务功能 Activiti 流程引擎都以服务的形 式提供给开发人员。通过使用这些服务,开发人员能够构建出功能丰富、轻便且高效的 BPM 应用程序。 Activiti是一个针对企业用户、开发人员、系统管理员的轻量级工作流业务管理平台,其核心是使用Java开发的快 速、稳定的BPMN 2.0流程引擎。Activiti是在ApacheV2许可下发布的,可以运行在任何类型的Java程序中,例如服 务器、集群、云服务等。
Activiti可以完美地与Spring集成。同时,基于简约思想的设计使Activiti非常轻量级。 官网:https://www.activiti.org/
1.2 Activiti 开发流程
- 画流程定义模型: 遵守BPMN的流程规范,使用BPMN的流程定义工具,通过 流程符号 把整个业务流程定义出来,可以将流程 定义文件字节流保存到模型数据表中(Model)。
- 部署流程定义: 加载画好的流程定义文件,将它转换成流程定义数据(ProcessDefinition),保存到流程定义数据表中。
- 启动流程(提交流程申请): 生成流程实例数据(ProcessInstance),生成第1节点任务数据(Task)。
- 处理人审批流程节点任务: 完成任务审批,生成审批结果,生成下一节点任务数据。
1.3 BPMN 2.0 规范是什么
业务流程模型注解(Business Process Modeling Notation - BPMN)是业务流程模型的一种标准图形注解。这个 标准是由对象管理组(Object Management Group - OMG)维护的。
标准的早期版本(1.2版以及之前)仅仅限制在模型上, 目标是在所有的利益相关者之间形成通用的理解, 在文 档,讨论和实现业务流程之上。 BPMN标准证明了它自己,现在市场上许多建模工具都使用了BPMN标准中的元素 和结构。
BPMN规范的2.0版本,当前已经处于最终阶段了, 允许添加精确的技术细节在BPMN的图形和元素中, 同时制定 BPMN元素的执行语法。 通过使用XML语言来指定业务流程的可执行语法, BPMN规范已经演变为业务流程的语 言, 可以执行在任何兼容BPMN2的流程引擎中, 同时依然可以使用强大的图形注解。
目前BPMN2.0是最新的版本,它用于在BPM上下文中进行布局和可视化的沟通。BPMN 2.0是使用一些符号来明确 业务流程设计流程图的一整套符号规范,它能增进业务建模时的沟通效率。
1.4 BPMN 2.0 基本流程符号
1.4.1 事件 Event
开始 中间事件 结束
1.4.2 活动
活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程;
1.4.3 网关 Gateway
网关用来处理决策:
- 排他网关 (x)
只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为true时,继 续执行当前网关的输出流; 如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。如果所有网关计算结果没有true,则引 擎会抛出异常。 排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流。
- 并行网关 (+)
所有路径会被同时选择 分支: 并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路。 汇聚 :所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
- 包含网关 (o)
可以同时执行多条线路,也可以在网关上设置条件分支:计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行 汇聚:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行。
- 事件网关 (o+)
专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关 后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态。
1.5 Activiti API 服务接口
Activiti 流程引擎包含了25张表,而且表之间的关系也比较复杂,比如包含各种外键约束。
按照传统的方式,有了数据库表后,就应该为每张表创建 Entity 实体类,然后为其创建对应的 DAO 接口,然 后再创建对应的 Service来实现对表数据的增删改查;
但是按照传统方式的,就会有一个很严峻的问题,表的数量太多,并且关系复杂,还要兼顾流程引擎的处理方 式,自己去搞一套,几乎不可能;
其实不需要我们去创建Entity 、 DAO、Service、Controller,因为Activiti已经把这些东西给搞好了,只需要 调用即可。
Process Engine API 和服务
引擎 API 是与 Activiti 交互的最常见方式。
可以从ProcessEngine中获取包含工作流/ BPM方法的各种服务。
ProcessEngine和服务对象是线程安全的。因此,可以为整个服务器保留对其中之一的引用。
Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用对应Service接口可以操作对应 的数据表。
1.5.1 核心Service接口及其获取
// 会在首次调用时初始化并构建一个流程引擎,此后始终返回相同的流程引擎。
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 引擎管理类
ManagementService managementService = processEngine.getManagementService();
// 动态修改流程管理类
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
// 流程运行管理类
RuntimeService runtimeService = processEngine.getRuntimeService();
// 流程仓库管理类
RepositoryService repositoryService = processEngine.getRepositoryService();
// 任务管理类
TaskService taskService = processEngine.getTaskService();
// 历史管理类
HistoryService historyService = processEngine.getHistoryService();
二 表
2.1 建表
前提:在数据库中建库 并和配置文件中的连接信息保持一致。
在test中执行如下语句,即可建表成功:
@Autowired
ProcessEngine processEngine;
@Test
public void getProcessEngine() {
System.out.println("processEngine: " + processEngine);
}
建好表后,执行两条语句,使其完整:
ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN VERSION_ VARCHAR(255);
ALTER TABLE ACT_RE_DEPLOYMENT ADD COLUMN PROJECT_RELEASE_VERSION_ VARCHAR(255);
2.2 表介绍
Acitiviti数据库中表的命名都是以 ACT_ 开头的。第二部分是一个两个字符用例表的标识。此用例大体与服务API是 匹配的。
ACT_GE_* : GE 表示 general 。通用数据,各种情况都使用的数据 ,如存放资源文件(图片,规则等)。
ACT_HI_xxx : HI 表示history。就是这些表包含着历史的相关数据,如结束的流程实例(变量,任务等)。
ACT_RE_xxx : RE 表示repository。带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片,规则 等)。 Activiti只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直 很小速度很快。
ACT_RU_xxx : RU 表示 runtime。这是运行时的表存储着流程变量,用户任务,变量,职责(job)等运行时的 数据。Activiti只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的 表小且快。
ACT_EVT_* :EVT表示EVENT,流程引擎的通用事件日志记录表,方便管理员跟踪处理。
表分类 | 表名 | 说明 |
---|---|---|
通用数据 | ||
act_ge_bytearray | 二进制数据表(流程图) | |
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_re_deployment | 部署信息表 | |
act_re_model | 流程设计模型表 | |
act_re_procdef | 流程设计模型表 | |
流程运行表 | ||
act_ru_deadletter_job | 作业死亡信息表,如果作业失败超过重试次数,则写入到此表 | |
act_ru_event_subscr | throwEvent、catchEvent时间监听信息表 | |
act_ru_execution | 运行时流程执行实例表 | |
aact_ru_identitylink | 运行时流程人员表,主要存储任务节点与参与者的相关信息 | |
act_ru_integration | 运行时积分表 | |
act_ru_job | 定时异步任务数据表 | |
act_ru_suspended_job | 运行时作业暂停表, 比如流程中有一个定时任务,如果把这个任 务停止工作了,这个任务写入到此表中 | |
act_ru_task | 运行时任务节点表 | |
act_ru_timer_job | 运行时定时器作业表 | |
act_ru_variable | 运行时流程变量数据表 | |
其他表 | ||
act_procdef_info | 流程定义的动态变更信息 | |
act_evt_log | 流程引擎的通用事件日志记录表 |
三 关于model
3.1 model画图
画图前,先指定如下三处
必须有个开始 和 结束 :
加入任务:
注意:双击节点可以指定节点名称
指定任务办理人:
然后到下一步(此时是以写死的方式来指定办理人的)
完成整个流程绘图后,点击左上角保存
点击保存后出现如下框,填写完 点保存就好
动态分配办理人
方式1:指定变量
此时是指定了一个表达式的样子来指定的动态办理人,为该表达式赋值的时机是流程到达该节点之前,如果该节点之前是 开始 ,那么就在流程启动的时候指定,如果该节点之前是 任务 ,那么就在完成上一步任务的同时指定下一步办理人。
如何加流程参数在本文档的第九节。
方式2:指定对象属性名
在启动流程或者完成上一步任务办理的同时,指定一个user对象进去就好了。
如何加流程参数在本文档的第九节。
方式3:方法表达式
注意:userService是Spring容器中的一个bean实例,对应调用getUsername()方法。
如何加流程参数在本文档的第九节。
3.2 操作model
3.2.1 模型查询对象
@Autowired
RepositoryService repositoryService;
/**
* 查询所有流程定义模型
*/
@Test
public void modelList() {
ModelQuery query = repositoryService.createModelQuery();
List<Model> list = query.orderByCreateTime()
.desc()
.list();
for (Model model : list) {
System.out.print("模型id:" + model.getId());
System.out.print(", 模型名称:" + model.getName());
System.out.print(",模型描述: " + model.getMetaInfo());
System.out.print(",模型标识key:" + model.getKey());
System.out.println(",模型版本号:" + model.getVersion());
}
}
3.2.2 删除模型对象
@Autowired
RepositoryService repositoryService;
/**
* 删除流程定义模型
* 会影响到的表:ACT_RE_MODE 和 ACT_GE_BYTEARRAY
*/
@Test
public void deleteModel() {
// modelId 为表ACT_RE_MODE 中的ID字段
String modelId = "0a499790-7c1d-11f6-9001-2c337a6d7e1d";
repositoryService.deleteModel(modelId);
System.out.println("删除成功");
}
3.2.3 导出流程定义模型资源的zip压缩包
@Autowired
RepositoryService repositoryService;
@Test
public void exportZip() throws IOException {
// 1. 查询模型基本信息 (modelId 为表ACT_RE_MODE 中的ID字段)
String modelId = "da0fbf5c-7c1d-11f6-949f-2c337a6d7e1d";
Model model = repositoryService.getModel(modelId);
if(model != null) {
// 2. 查询流程定义模型的json字节码
byte[] bpmnJsonBytes = repositoryService.getModelEditorSource(modelId);
// 2.1 将json字节码转换为xml字节码
byte[] xmlBytes = bpmnJsonXmlBytes(bpmnJsonBytes);
if(xmlBytes == null) {
System.out.println("模型数据为空-请先设计流程定义模型,再导出");
}else {
// 压缩包文件名
String zipName = model.getName() + "." + model.getKey() + ".zip";
File file = new File("D:/" + zipName);
FileOutputStream outputStream = new FileOutputStream(file);
// 实例化zip输出流
ZipOutputStream zipos = new ZipOutputStream(outputStream);
// 将xml添加到压缩包中(指定xml文件名:请假流程.bpmn20.xml )
zipos.putNextEntry(new ZipEntry(model.getName() + ".bpmn20.xml"));
zipos.write(xmlBytes);
// 3. 查询流程定义模型的图片字节码
byte[] pngBytes = repositoryService.getModelEditorSourceExtra(modelId);
if(pngBytes != null) {
// 图片文件名(请假流程.leaveProcess.png)
zipos.putNextEntry(new ZipEntry(model.getName() + "." + model.getKey() + ".png"));
zipos.write(pngBytes);
}
// 4. 以压缩包的方式导出流程定义模型文件
zipos.closeEntry();
zipos.close();
System.out.println("导出流程定义模型zip成功");
}
}else {
System.out.println("模型不存在");
}
}
private byte[] bpmnJsonXmlBytes(byte[] jsonBytes) throws IOException {
if(jsonBytes == null) {
return null;
}
// 1. json字节码转成 BpmnModel 对象
JsonNode jsonNode = objectMapper.readTree(jsonBytes);
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
if(bpmnModel.getProcesses().size() == 0) {
return null;
}
// 2. BpmnModel 对象转为xml字节码
byte[] xmlBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
return xmlBytes;
}
3.2.4 导出流程定义模型资源的.xml文件
@Test
public void exportXml() throws IOException {
// 1. 查询模型基本信息
String modelId = "da0fbf5c-7c1d-11f6-949f-2c337a6d7e1d";
// 查询json字节码
byte[] bytes = repositoryService.getModelEditorSource(modelId);
String filename = null;
ByteArrayInputStream in = null;
if(bytes != null) {
// 1. json字节码转成 BpmnModel 对象
JsonNode jsonNode = objectMapper.readTree(bytes);
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
if(bpmnModel.getProcesses().size() > 0) {
// 2. BpmnModel 对象转为xml字节码
byte[] xmlBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
in = new ByteArrayInputStream(xmlBytes);
filename = StringUtils.isBlank(bpmnModel.getMainProcess().getName())
? bpmnModel.getMainProcess().getId() : bpmnModel.getMainProcess().getName();
}
}
if(filename == null) {
filename = "模型数据为空,请先设计流程,再导出";
in = new ByteArrayInputStream(filename.getBytes("utf-8"));
}
// 文件输出流
FileOutputStream out = new FileOutputStream(new File("D:/" + filename + ".bpmn20.xml"));
IOUtils.copy(in, out);
out.close();
in.close();
System.out.println("导出xml成功");
}
3.2.5 通过流程定义模型数据部署流程定义
/**
* 会影响到的表:
* ACT_RE_PROCDEF
* ACT_RE_DEPLOYMENT
* ACT_GE_BYTEARRAY
* ACT_RE_MODEL 更新流程部署id,将模型与部署的流程定义绑定
* @throws Exception
*/
@Test
public void deploy() throws Exception {
// 1. 查询流程定义模型json字节码
String modelId = "b8efa921-da43-11eb-8aa7-2c337a6d7e1d";
byte[] jsonBytes = repositoryService.getModelEditorSource(modelId);
if(jsonBytes == null) {
System.out.println("模型数据为空,请先设计流程定义模型,再进行部署");
return;
}
// 将json字节码转为 xml 字节码,因为bpmn2.0规范中关于流程模型的描述是xml格式的,而activiti遵守了这个规范
byte[] xmlBytes = bpmnJsonXmlBytes(jsonBytes);
if(xmlBytes == null) {
System.out.println("数据模型不符合要求,请至少设计一条主线流程");
return;
}
// 2. 查询流程定义模型的图片
byte[] pngBytes = repositoryService.getModelEditorSourceExtra(modelId);
// 查询模型的基本信息
Model model = repositoryService.getModel(modelId);
// xml资源的名称 ,对应act_ge_bytearray表中的name_字段
String processName = model.getName() + ".bpmn20.xml";
// 图片资源名称,对应act_ge_bytearray表中的name_字段
String pngName = model.getName() + "." + model.getKey() + ".png";
// 3. 调用部署相关的api方法进行部署流程定义
Deployment deployment = repositoryService.createDeployment()
.name(model.getName()) // 部署名称
.addString(processName, new String(xmlBytes, "UTF-8")) // bpmn20.xml资源
.addBytes(pngName, pngBytes) // png资源
.deploy();
// 更新 部署id 到流程定义模型数据表中
model.setDeploymentId(deployment.getId());
repositoryService.saveModel(model);
System.out.println("部署成功");
}
四 操作部署信息
4.1 部署
4.1.1通过zip压缩包进行部署流程定义
/**
* 通过zip压缩包进行部署流程定义
*/
@Test
public void deployByZip() throws Exception {
File file = new File("D:/请假流程模型xx.leaveProcess.zip");
// 创建输入流
FileInputStream inputStream = new FileInputStream(file);
// 读取zip资源压缩包,转成输入流
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("请假申请流程222-压缩包")
.deploy();
// 4. 输出部署结果
System.out.println("部署ID: " + deployment.getId());
System.out.println("部署名称:" + deployment.getName());
}
4.1.2 通过.xml文件部署
@Test
public void deployByFile() throws FileNotFoundException {
File file = new File("D:/请假申请流程.bpmn20.xml");
// 文件输入流
FileInputStream inputStream = new FileInputStream(file);
// 资源名称
String filename = file.getName();
// 调用相关api方法进行部署
Deployment deployment = repositoryService.createDeployment()
.name("请假申请流程")
.addInputStream(filename, inputStream)
.deploy();
// 输出部署结果
System.out.println("部署ID: " + deployment.getId());
System.out.println("部署名称:" + deployment.getName());
}
4.2 删除部署流程定义
/**
* 根据部署ID来删除部署的流程定义数据
* 影响到的表:
* ACT_GE_BYTEARRAY
* ACT_RE_DEPLOYMENT
* ACT_RE_PROCDEF
* ACT_RU_EVENT_SUBSCR
* ACT_RU_IDENTITYLINK
*/
@Test
public void delete() {
try {
String deploymentId = "f88ccd79-db17-11eb-a1e0-2c337a6d7e1d";
// 默认不是级联删除操作,如果有正在执行的流程实例,则删除时会抛出异常,并且不会删除历史表数据
//repositoryService.deleteDeployment(deploymentId);
// 如果为true则是级联删除操作(小心使用~)
// 如果流程定义启动了对应的流程实例,也可以进行删除,并且会删除历史数据
//repositoryService.deleteDeployment(deploymentId, true);
System.out.println("删除流程定义数据成功");
}catch (Exception e) {
e.printStackTrace();
if(e.getMessage().indexOf("a foreign key constraint fails") > 0) {
System.out.println("有正在执行的流程实例,不允许删除");
}else {
System.out.println("删除失败,原因: " + e.getMessage());
}
}
}
4.3 启动流程
@Autowired
RuntimeService runtimeService;
@Test
public void startProcessInstance() {
// 启动流程实例(流程定义key processDefinitionKey)
// 通过流程定义key启动的流程实例 ,找的是最新版本的流程定义数据
// 根据key部署 这个案例里"leaveProcess" 为部署的key
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leaveProcess");
System.out.println("流程定义id: " + processInstance.getProcessDefinitionId());
System.out.println("流程实例id: " + processInstance.getId());
}
五 操作流程定义
5.1 分页查询
@Autowired
RepositoryService repositoryService;
/**
* 分页条件查询流程定义列表数据
*/
@Test
public void getProcDefList() {
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
String name = "请假";
if(StringUtils.isNotBlank(name)) {
// 条件查询
query.processDefinitionNameLike("%"+name+"%");
}
// 如果多个相同key, 只查询最新版本的流程定义,
query.latestVersion();
// 按key降序
query.orderByProcessDefinitionKey().desc();
// 当前页码
int current = 1;
// 每页显示多少条
int size = 5;
// 当前页第1条数据的下标
int firstResult = (current-1) * size;
// 分页查询
List<ProcessDefinition> processDefinitionList = query.listPage(firstResult, size);
for (ProcessDefinition pd : processDefinitionList) {
System.out.print("流程部署id:" + pd.getDeploymentId());
System.out.print(",流程定义id:" + pd.getId());
System.out.print(",流程定义key: " + pd.getKey());
System.out.print(",流程定义名称:" + pd.getName());
System.out.print(",版本号:" + pd.getVersion());
System.out.println(",状态:" + ( pd.isSuspended() ? "挂起(暂停)" : "激活(开启)") );
}
// 总记录数
long total = query.count();
System.out.println("满足条件的流程定义总记录:" + total);
}
5.2 修改流程定义状态
/**
* 挂起或激活流程定义 :对应 act_re_procdef 表中的 SUSPENSION_STATE_ 字段,1是激活,2是挂起
* - 流程定义被挂起:此流程定义下的所有流程实例不允许继续往后流转了,就被停止了。
* - 流程定义被激活:此流程定义下的所有流程实例允许继续往后流转。
* - 为什么会被挂起?
* - 可能当前公司的请假流程发现了一些不合理的地方,然后就把此流程定义挂起。
* - 流程不合理解决办法:
* - 方式一:可以先挂起流程定义,然后更新流程定义,然后激活流程定义。
* - 方式二:挂起了就不激活了,重新创建一个新的请假流程定义。
*/
@Test
public void updateProcDefState() {
// processDefinitionId 为 act_re_procdef 表id
String processDefinitionId = "leaveProcess:2:f6a379a1-7cdb-11f6-8052-2c337a6d7e1d";
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId(processDefinitionId)
.singleResult();
// 判断是否挂起,true则挂起,false则激活
if(processDefinition.isSuspended()) {
// 将当前为挂起状态更新为激活状态
// 参数说明:参数1:流程定义id,参数2:是否激活(true是否级联对应流程实例,激活了则对应流程实例都可以审批),参数3:什么时候激活,如果为null则立即激活,如果为具体时间则到达此时间后激活
repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
}else {
// 将当前为激活状态更新为挂起状态
// 参数说明:参数1:流程定义id,参数2:是否挂起(true是否级联对应流程实例,挂起了则对应流程实例都不可以审批),参数3:什么时候挂起,如果为null则立即挂起,如果为具体时间则到达此时间后挂起
repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
}
}
5.3 导出流程定义文件
/**
* 导出流程定义文件(xml或png)
*/
@Test
public void exportProcDefFile() throws Exception{
String processDefinitionId = "leaveProcess:2:f6a379a1-7cdb-11f6-8052-2c337a6d7e1d";
ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
// 获取的是 xml 资源名
String resourceName = processDefinition.getResourceName();
// 获取 png 图片资源名
resourceName = processDefinition.getDiagramResourceName();
// 查询到相关的资源输入流 (deploymentId, resourceName)
InputStream input =
repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
// 创建输出流
File file = new File("D:/" + resourceName);
FileOutputStream output = new FileOutputStream(file);
IOUtils.copy(input, output);
input.close();
output.close();
System.out.println("流程定义资源文件导出成功: " + resourceName);
}
六 操作流程实例
6.1 启动流程实例
@Autowired
RuntimeService runtimeService;
/**
* 涉及到的数据表:
* ACT_HI_TASKINST
* ACT_HI_PROCINST 流程实例
* ACT_HI_ACTINST 节点实例表
* ACT_HI_IDENTITYLINK 流程实例相关办理人
* ACT_RU_EXECUTION
* ACT_RU_TASK
* ACT_RU_IDENTITYLINK
*/
@Test
public void startProcessInstance() {
// 流程定义唯一标识key
String processKey = "leaveProcess";
// 业务id
String businessKey = "10000";
// 启动当前流程实例的用户(会保存到 act_hi_procinst 表中 start_user_id 字段中)
Authentication.setAuthenticatedUserId("catch");
// 启动流程实例(流程定义唯一标识key, 业务id),采用流程key对应的最新版本的流程定义数据
ProcessInstance pi = runtimeService.startProcessInstanceByKey(processKey, businessKey);
// 将流程定义名称 作为 流程实例名称
runtimeService.setProcessInstanceName(pi.getProcessInstanceId(), pi.getProcessDefinitionName());
System.out.println("启动流程实例成功:" + pi.getProcessInstanceId());
}
6.2 查询流程实例
6.2.1 查询正在运行的流程实例
/**
* 查询正在运行中的流程实例
* 核心表:act_ru_execution ,其中 parent_id 为空,就是正在运行的流程实例
*/
@Test
public void getProcInstListRunning() {
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery()
.processInstanceNameLike("%请假%")
.list();
for (ProcessInstance pi : list) {
System.out.println("流程定义key: " + pi.getProcessDefinitionKey());
System.out.println("流程定义版本号:" + pi.getProcessDefinitionVersion());
System.out.println("流程实例Id: " + pi.getProcessInstanceId());
System.out.println("流程实例名称:" + pi.getName());
System.out.println("业务key(业务主键id):" + pi.getBusinessKey());
System.out.println("发起人:" + pi.getStartUserId());
System.out.println("流程实例状态:" + ( pi.isSuspended() ? "已挂起(暂停)" : "已激活(启动)" ) );
}
}
6.3 激活或挂起流程实例
注意:区别于5.2,5.2是修改流程定义的状态,6.3是修改流程实例的状态。
/**
* 激活或挂起流程实例
* 流程定义:
* 激活:激活流程定义后,对应所有的流程实例都可以继续向下流转
* 挂起:挂起流程定义后,对应所有的流程实例都不可以继续向下流转
* 流程实例:
* 激活:激活流程实例后,此流程实例可继续向下流转。
* 挂起:挂起流程实例后,此流程实例不可以向下流转。
*/
@Test
public void updateProcInstState() {
String procInstId = "c1b97f6f-7d0f-11f6-bc11-2c337a6d7e1d";
// 1. 查询指定流程实例的数据
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(procInstId)
.singleResult();
// 2. 判断当前流程实例的状态
if(processInstance.isSuspended()) {
// 如果是已挂起,则更新为激活状态
runtimeService.activateProcessInstanceById(procInstId);
System.out.println("激活流程实例成功");
}else {
// 如果是已激活,则更新为挂起状态
runtimeService.suspendProcessInstanceById(procInstId);
System.out.println("挂起流程实例成功");
}
}
6.4 删除流程实例
@Autowired
HistoryService historyService;
/**
* 删除流程实例:
* 涉及到到的数据表:
* ACT_RU_IDENTITYLINK
* ACT_RU_TASK
* ACT_RU_EXECUTION
*/
@Test
public void deleteProcInst() {
String procInstId = "11d21740-7f7d-11f6-9f0c-2c337a6d7e1d";
// 1. 查询指定流程实例的数据
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(procInstId)
.singleResult();
if(processInstance == null) {
System.out.println("流程实例不存在");
return;
}
// 删除流程实例(流程实例id,删除原因), 不会删除流程实例相关历史数据
runtimeService.deleteProcessInstance(procInstId, "xxx作废了当前流程申请");
// 删除流程实例历史数据
//historyService.deleteHistoricProcessInstance(procInstId);
}
七 操作任务
7.1 查询指定办理人任务
@Test
public void findWaitTask() {
// 办理人
String assignee = "snow";
// 查询个人待办任务
List<Task> list = taskService.createTaskQuery()
.taskAssignee(assignee) // 具体办理人
.orderByTaskCreateTime()
.desc()
.list();
for (Task task : list) {
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("任务id: " + task.getId());
System.out.println("任务名称:" + task.getName());
System.out.println("任务办理人:" + task.getAssignee());
}
}
7.2 查询指定候选人任务
@Test
public void findWaitTask() {
// 办理人
String assignee = "catch";
List<Task> list = taskService.createTaskQuery()
.taskCandidateOrAssigned(assignee) // 作为候选人或者是办理人
.orderByTaskCreateTime()
.desc()
.list();
for (Task task : list) {
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("任务id: " + task.getId());
System.out.println("任务名称:" + task.getName());
System.out.println("任务办理人:" + task.getAssignee());
}
}
7.3 办理个人任务
/**
* 完成任务
*/
@Test
public void completeTask() {
String taskId = "cdbdce28-7e6e-11f6-9939-2c337a6d7e1d";
// 办理前 获取到当前登录人 进行判断 不能办理别人的任务
// 判断完成后,办理
taskService.complete(taskId);
}
7.4 拾取候选人任务
候选人 是 出现在组任务中的概念,个人任务不存在候选人的说法;
在组任务中,可以指定多个办理人,多个办理人就是该组任务的候选人。
/**
* 拾取候选任务
*/
@Test
public void claimTask() {
String taskId = "6903fa10-7e79-11f6-8210-2c337a6d7e1d";
String userId = "catch";
// 注意:即使拾取任务的办理人,不在候选人中,也可以进行拾取成功。
// 所以最好在拾取之前判断是否为当前任务的候选人
/*List<IdentityLink> identityLinkList = taskService.getIdentityLinksForTask(taskId);
for (IdentityLink identityLink : identityLinkList) {
System.out.println(identityLink.getUserId());
}*/
// 拾取候选任务,将 catch 作为该组任务的办理人
taskService.claim(taskId, userId);
}
7.5 归还组任务
/**
* 任务办理人归还组任务中
*/
@Test
public void assigneeToGroupTask() {
String taskId = "6903fa10-7e79-11f6-8210-2c337a6d7e1d";
String assignee = "catch"; //办理人
// 1. 查询办理人任务
Task task = taskService.createTaskQuery()
//.processDefinitionKey("此处是key")
.taskAssignee(assignee)
.singleResult();
// 2. 归还组任务中
if(task != null) {
// 直接将办理人设置为null,即归还到了组任务中
taskService.setAssignee(task.getId(), null);
}
}
7.6 转办任务
/**
* 转办任务
*/
@Test
public void turnTask() {
String taskId = "6903fa10-7e79-11f6-8210-2c337a6d7e1d";
String assignee = "catch"; //办理人
String candidateuser = "jones"; // 转办的目标办理人
// 1. 查询办理人任务
Task task = taskService.createTaskQuery()
//.processDefinitionKey("此处写key")
.taskAssignee(assignee)
.taskId(taskId)
.singleResult();
// 将任务转办给 jones 用户
if(task != null) {
taskService.setAssignee(task.getId(), candidateuser);
}
}
7.7 办理组任务
将组任务拾取后,就成为了个人任务。办理同7.3。文章来源:https://www.toymoban.com/news/detail-457479.html
八 历史数据
8.1 查询个人已办任务
表 ACT_HI_TASKINST文章来源地址https://www.toymoban.com/news/detail-457479.html
@Autowired
HistoryService historyService;
@Test
public void findCompleteTask() {
// 查询
List<HistoricTaskInstance> taskList = historyService.createHistoricTaskInstanceQuery()
.taskAssignee("catch") // 办理人
.includeProcessVariables()
.orderByTaskCreateTime()
.desc() // 任务创建时间降序排列
.finished()
.list();
for (HistoricTaskInstance task : taskList) {
System.out.print(" 任务ID: " + task.getId());
System.out.print(" ,任务名称: " + task.getName());
System.out.print(" ,任务开始时间: " + task.getStartTime());
System.out.print(" ,任务结束时间: " + task.getEndTime());
System.out.print(" ,办理人: " + task.getAssignee());
System.out.print(" ,流程定义id: " + task.getProcessDefinitionId());
System.out.print(" ,流程实例id: " + task.getProcessInstanceId());
System.out.print(" ,业务id: " + task.getBusinessKey());
System.out.println(",流程变量:" + task.getProcessVariables());
}
}
8.2 查询流程实例的办理历史节点信息
@Test
public void historyInfo() {
// 1. 获取查询对象
HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery();
// 2. 开始查询
List<HistoricActivityInstance> list = query.processInstanceId("流程实例id")
.orderByHistoricActivityInstanceStartTime()
.asc()
.list();
for (HistoricActivityInstance hi : list) {
System.out.print("流程定义id:" + hi.getProcessDefinitionId());
System.out.print(",流程实例Id: " + hi.getProcessInstanceId());
System.out.print(",节点id: " + hi.getActivityId());
System.out.print(",节点名称:" + hi.getActivityName());
System.out.println(",任务办理人:" + hi.getAssignee());
}
}
8.3 查询已经结束的历史流程实例
@Test
public void getProcInstListFinish() {
List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery()
.orderByProcessInstanceEndTime()
.desc()
.finished()
.list();
for (HistoricProcessInstance hpi : list) {
System.out.print("流程实例id: " + hpi.getId());
System.out.print(",流程实例名称:" + hpi.getName());
System.out.print(", 流程标识key:" + hpi.getProcessDefinitionKey());
System.out.print(", 流程定义版本号:" + hpi.getProcessDefinitionVersion());
System.out.print(", 业务id:" + hpi.getBusinessKey());
System.out.print(", 流程发起人:" + hpi.getStartUserId());
System.out.print(", 开始时间:" + hpi.getStartTime());
System.out.print(", 结束时间:" + hpi.getEndTime());
System.out.println(", 删除原因详情:" + hpi.getDeleteReason());
}
}
8.4 删除已结束的历史流程实例
/**
* 删除已结束的流程实例
* ACT_HI_DETAIL
* ACT_HI_VARINST
* ACT_HI_TASKINST
* ACT_HI_PROCINST
* ACT_HI_ACTINST
* ACT_HI_IDENTITYLINK
* ACT_HI_COMMENT
*/
@Test
public void deleteFinishProcInst() {
String procInstId = "流程实例id";
// 1. 查询流程实例是否已结束
HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(procInstId)
.finished()
.singleResult();
if(instance == null) {
System.out.println("流程实例未结束或不存在");
return;
}
// 2. 删除已结束流程实例(如果实例未结束,会抛出异常,如果需要删除
historyService.deleteHistoricProcessInstance(procInstId);
}
九 加流程参数
9.1 启动时加入
@Autowired
RuntimeService runtimeService;
// 启动流程实例
@Test
public void startProcess() {
// 流程变量
Map<String, Object> variables = new HashMap<>();
// 加入参数
variables.put("duration", 2);
runtimeService.startProcessInstanceByKey("testInclusiveGateway", variables);
}
9.2 办理任务时加入流程参数
@Autowired
TaskService taskService;
@Test
public void complete() {
String taskId = "5a17a43e-8010-11f6-9aa6-2c337a6d7e1d";
// 流程变量
Map<String, Object> variables = new HashMap<>();
// 请假天数,
variables.put("duration", 5);
taskService.complete(taskId, variables);
}
到了这里,关于Activiti7(图文并茂)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!