Springboot整合Flowable入门-学习笔记
目录
1、定义流程(画图)
2、Springboot部署流程
3、Springboot删除所有流程
4、Springboot根据 流程部署ID 查询 流程定义ID
5、Springboot启动(发起)流程
6、Springboot查询任务
6.1全部任务
6.2我的任务(代办任务)
7、springboot审批任务
1、审批通过
2、驳回
8、Springboot流程状态管理
1、流程定义
1、挂起《流程定义》
2、激活《流程定义》
2、流程实例
1、挂起《流程实例》
2、激活《流程实例》
9、资源
1.单元测试代码
2.xml代码
看了很多文档,总结一个完整的工作流包含以下步骤:
- 定义流程: 创建一个BPMN 2.0格式的流程定义文件。
- 部署流程: 将流程定义文件部署到Flowable引擎中。
- 启动流程实例: 使用部署好的流程定义启动一个新的流程实例。
- 执行任务: 查询和完成流程实例中的任务
ps:我使用的工具是 IDEA2019, 环境JDK8,Springboot版本2.5.15,Flowable版本6.8.0
大家先搞懂一个概念:是先有 流程定义 才会有 流程实例
1、定义流程(画图)
现在有很多优秀的画BPMN2.0开源系统。
我这里使用的是IDEA的插件:Flowable BPMN visualizer

安装好插件之后,在资源目录创建一个processes(flowable默认读取resources/processes下的流程文件)

创建BPMN文件

创建BPMN视图

开始画图:(整个流程必须是有一个起点,一个任务,一个终点)

我这里画一个请假流程:(开始-人事审核-主管审核-结束)


用户任务(UserTask)属性解释:
-
id:
- 描述:
User Task的唯一标识符。 - 示例:
id="sid-bb849ec0-1482-4057-a447-c8d68adc9ca2"
- 描述:
-
name:
- 描述:
User Task的显示名称。 - 示例:
name="人事审核"
- 描述:
-
assignee:
- 描述: 指定执行此任务的单个用户。
- 示例:
flowable:assignee="cpw"
-
candidateUsers:
- 描述: 任务的候选用户列表。这些用户都有资格完成任务。
- 示例:
flowable:candidateUsers="cpw,zhangsan,lisi" (通过逗号隔开)
-
candidateGroups:
- 描述: 任务的候选用户组列表。这些用户组中的用户都有资格完成任务。
- 示例:
flowable:candidateGroups="managers"
-
formKey:
- 描述: 指定与任务关联的表单的键,用于在任务执行时显示相应的表单。
- 示例:
flowable:formKey="approveForm"
-
dueDate:
- 描述: 任务的到期日期,可以是一个具体日期或相对时间表达式。
- 示例:
flowable:dueDate="${now() + 5 days}"
-
priority:
- 描述: 任务的优先级,数值越高表示优先级越高。
- 示例:
flowable:priority="50"
-
documentation:
- 描述: 任务的文档说明,可以提供任务的详细描述。
- 示例:
<documentation>This task requires approval from the manager.</documentation>
-
extensions:
- 描述: 自定义属性,可以添加额外的配置。例如,自定义表单字段或执行特定操作

连接节点

点击一个图案 按住拖动右上角的箭头进行连线
好了,完整的一个图出来了、完整的xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef"><process id="flowDemo" name="flowDemo" isExecutable="true"><startEvent id="sid-bb849ec0-1482-4057-a447-c8d68adc9ca2" name="开始"/><userTask id="sid-c30c0960-a986-4cd6-80e2-bae02d6af707" name="人事审核" flowable:assignee="cpw"></userTask><userTask id="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39" name="主管审核" flowable:assignee="zhuguan"/><endEvent id="sid-712aab8a-a060-4363-b3eb-cd3fa9215808"/><sequenceFlow id="sid-2c879163-8816-4dd7-89bb-bf1a4485fef4" sourceRef="sid-bb849ec0-1482-4057-a447-c8d68adc9ca2" targetRef="sid-c30c0960-a986-4cd6-80e2-bae02d6af707"/><sequenceFlow id="sid-2fca88ff-d597-430c-a5e9-edfefbdd2569" sourceRef="sid-c30c0960-a986-4cd6-80e2-bae02d6af707" targetRef="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39"/><sequenceFlow id="sid-e8b6c848-1c54-4fdb-a8b0-e085d715c906" sourceRef="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39" targetRef="sid-712aab8a-a060-4363-b3eb-cd3fa9215808"/></process><bpmndi:BPMNDiagram id="BPMNDiagram_flowDemo"><bpmndi:BPMNPlane bpmnElement="flowDemo" id="BPMNPlane_flowDemo"><bpmndi:BPMNShape id="shape-d460f24b-1aac-4028-b625-929601acd3c0" bpmnElement="sid-bb849ec0-1482-4057-a447-c8d68adc9ca2"><omgdc:Bounds x="-375.0" y="-37.25" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-ad3ec780-dd25-42f1-8e53-6ed0608d5b91" bpmnElement="sid-c30c0960-a986-4cd6-80e2-bae02d6af707"><omgdc:Bounds x="-282.75" y="-62.25" width="100.0" height="80.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-2a1947d2-0365-4dd8-8083-9bdaa7bea300" bpmnElement="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39"><omgdc:Bounds x="-134.25" y="-62.25" width="100.0" height="80.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-c748377a-bdcd-4892-b001-3621cf05589a" bpmnElement="sid-712aab8a-a060-4363-b3eb-cd3fa9215808"><omgdc:Bounds x="15.625" y="-37.25" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-a51efb51-2d88-4eae-9ffe-baa2e50cd0e5" bpmnElement="sid-2c879163-8816-4dd7-89bb-bf1a4485fef4"><omgdi:waypoint x="-345.0" y="-22.25"/><omgdi:waypoint x="-282.75" y="-22.25"/></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="edge-69253e56-2532-4829-b515-d27caeeb275a" bpmnElement="sid-2fca88ff-d597-430c-a5e9-edfefbdd2569"><omgdi:waypoint x="-182.75" y="-22.25"/><omgdi:waypoint x="-134.25" y="-22.25"/></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="edge-86a73120-1957-453e-b988-7f2ede9f75dc" bpmnElement="sid-e8b6c848-1c54-4fdb-a8b0-e085d715c906"><omgdi:waypoint x="-34.25" y="-22.25"/><omgdi:waypoint x="-10.011603" y="-22.25"/><omgdi:waypoint x="15.625" y="-22.25"/></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>
2、Springboot部署流程
有两种使用Flowable类的方法:
第一种是使用ProcessEngine,它里面包含了所有flowable的核心类,我们可以查看源码:

第二种是直接使用***Service
有哪些Service可以查看flowable的jar包下engine里的Serivce类

现在开始部署流程:(使用Springboot单元测试)
@SpringBootTest
@DisplayName("流程测试")
public class UnitTest {//第一种方法使用ProcessEngine@Resourceprivate ProcessEngine processEngine;//第二种方法使用若干个 ***Service//@Autowired//private RuntimeService runtimeService;//@Autowired//private RepositoryService repositoryServiceService;@DisplayName("部署流程")@Testvoid deployFlow(){Deployment deploy = processEngine.getRepositoryService().createDeployment() //创建部署对象.addClasspathResource("processes/flowDemo.bpmn20.xml") // 默认是在resources/processes下寻找,当然你可以自定义目录.name("第一个Flowable案例") //设置流程的名字.deploy(); //deploy部署流程System.out.println("部署流程ID为:"+deploy.getId()); //获取到当前流程的ID}
}
运行结果:获取到部署成功的流程的ID

部署成功之后会涉及到以下数据表:
ACT_GE_BYTEARRAY
- 描述: 存储二进制数据,如流程定义的BPMN XML文件,PNG图片和其他附加数据。
ACT_RE_DEPLOYMENT
- 描述: 存储流程部署的信息。每次部署一个新版本的流程定义时,会在此表中创建一条记录。
ACT_RE_PROCDEF
- 描述: 存储流程定义的元数据,包括流程的ID、版本、名称和相关的部署ID。
部署成功之后查看 ACT_RE_DEPLOYMENT 表,你会发现创建的两条数据

因为项目启动时会自动部署flowable默认位置下的流程文件,所以会出现两个流程,这时候我们可以关闭自动部署。
可以根据你的需求加上以下配置:
flowable:# 关闭定时任务async-executor-activate: true# 将databaseSchemaUpdate设置为true。如果Flowable发现数据库的结构与应用所需的结构不一致的时候,会自动帮你更新或者新增表或者表结构。database-schema-update: true# 项目启动时会自动部署默认位置下的流程文件,false表示不检查,true表示检查,默认为truecheck-process-definitions: false
也可以根据需求更改存放路径和后缀名:
flowable.process-definition-location-prefix:classpath*:/processes/
flowable.process-definition-location-suffixes: **.bpmn20.xml,**.bpmn,

3、Springboot删除所有流程
@DisplayName("删除全部流程")@Testvoid deleteAllFlowable(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();// 删除所有流程定义repositoryService.createProcessDefinitionQuery().list().forEach(pd ->repositoryService.deleteDeployment(pd.getDeploymentId(), true));System.out.println("删除成功");}
4、Springboot根据 流程部署ID 查询 流程定义ID
注意:一个 流程部署ID 可以对应多个 流程定义ID
@DisplayName("根据 流程部署ID 查询 流程定义ID ")@Testvoid selectProcessByDeployId(){String deployId = "dd77ca7c-554e-11ef-93dc-581122450312";//从上一个流程获取到的部署IDList<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery().deploymentId(deployId).list();for (ProcessDefinition processDefinition : list) {System.out.println("deployId对应的流程定义ID为:"+processDefinition.getId());}}
运行结果:

流程定义ID由流程定义的name和一串uuid组成。
5、Springboot启动(发起)流程
启动流程有很多种方法,主要使用的是两种:一种是根据 流程定义的ID,一种是根据流程定义的KEY(标识)

最好是使用 流程定义ID(随机生成),因为KEY就是流程定义的名字可能会重复(实际业务中注意新增流程的时候,如果选择KEY来查询流程,记得做唯一判断)
PS:前面关于部署和定义使用的是RepositoryService,接下来操作流程则使用RuntimeService
@DisplayName("启动流程")@Testvoid startProcess(){// 前面关于部署和定义使用的是RepositoryService,接下来操作流程则使用RuntimeServiceRuntimeService runtimeService = processEngine.getRuntimeService();// act_re_procdef(流程定义)表中的idString processId = "flowDemo:1:dd9eb36f-554e-11ef-93dc-581122450312";runtimeService.startProcessInstanceById(processId);//String processKey = "flowDemo";//runtimeService.startProcessInstanceByKey(processKey);System.out.println("启动流程成功");}
运行结果

启动流程后下面的表发生变化:
-
流程实例表 (
ACT_RU_EXECUTION)- 记录流程实例的当前状态和运行时信息。
- 启动流程后,新增一条记录,记录新流程实例的信息。
-
任务表 (
ACT_RU_TASK)- 记录运行时任务的信息。
- 如果启动流程后有用户任务,会在这个表中新增一条或多条任务记录。
-
历史流程实例表 (
ACT_HI_PROCINST)- 记录历史流程实例的信息。
- 启动流程后,新增一条记录,用于记录历史流程实例的信息。
-
历史任务表 (
ACT_HI_TASKINST)- 记录历史任务的信息。
- 如果启动流程后有用户任务,会在这个表中新增历史任务记录。
-
历史活动实例表 (
ACT_HI_ACTINST)- 记录历史活动实例的信息。
- 启动流程后,每个活动节点(如任务、网关等)都会在这个表中新增一条记录。
-
任务参与者表 (
ACT_RU_IDENTITYLINK)- 记录任务的参与者信息。
- 如果启动的流程涉及到任务分配,会在这个表中新增记录
6、Springboot查询任务
PS:涉及到任务则使用TaskService方法
6.1全部任务
@DisplayName("全部任务查询")@Testvoid findAllTask(){List<Task> list = processEngine.getTaskService().createTaskQuery().list();for (Task task : list) {System.out.println("任务ID:"+task.getId()+"---任务名称:"+task.getName()+"---任务当前办理人:"+task.getAssignee());}}

运行结果:

6.2我的任务(代办任务)
@DisplayName("我的(代办)任务查询")@Testvoid findMyTask(){//任务查询 通过TaskService来实现TaskService taskService = processEngine.getTaskService();List<Task> list = taskService.createTaskQuery().taskAssignee("cpw").list(); //流程设置的 用户任务的办理人--人事//List<Task> list = taskService.createTaskQuery().taskAssignee("zhuguan").list(); //流程设置的 用户任务的办理人--主管for (Task task : list) {System.out.println("任务ID:"+task.getId()+"---任务名称:"+task.getName()+"---任务当前办理人:"+task.getAssignee());}}
7、springboot审批任务

发起流程之后,会从开始节点流到人事审核节点
根据上面查询到的任务ID进行操作:
1、审批通过
@DisplayName("任务审批通过")@Testvoid completeTask(){//任务相关操作通过TaskService来实现TaskService taskService = processEngine.getTaskService();String taskId = "df2f8b51-5559-11ef-8d43-581122450312";taskService.complete(taskId);System.out.println("审批通过");}
运行结果:

此时流程就会跑到:人事审核节点跑到了主管审核节点

验证:这时候我们查询一下主管的代办任务,就会发现任务跑到主管这里来了
@DisplayName("我的(代办)任务查询")@Testvoid findMyTask(){//任务查询 通过TaskService来实现TaskService taskService = processEngine.getTaskService();//List<Task> list = taskService.createTaskQuery().taskAssignee("cpw").list(); //流程设置的 用户任务的办理人--人事List<Task> list = taskService.createTaskQuery().taskAssignee("zhuguan").list(); //流程设置的 用户任务的办理人--主管for (Task task : list) {System.out.println("任务ID:"+task.getId()+"---任务名称:"+task.getName()+"---任务当前办理人:"+task.getAssignee());}}

审批任务影响以下表:
ACT_RU_TASK 表
-
操作: 删除任务记录。
-
描述: 当任务完成后,任务记录会从该表中删除。这个表存储的是正在运行的任务。
ACT_RU_IDENTITYLINK 表
-
操作: 删除相关任务的身份链接记录。
-
描述: 当任务完成后,相关的用户和组的身份链接记录会从该表中删除。这个表存储的是任务与用户/组之间的关系,如任务的分配者、候选人和参与者。
ACT_HI_TASKINST 表
-
操作: 插入或更新任务历史记录。
-
描述: 当任务完成后,会在该表中插入一条新的历史任务实例记录,或者更新现有的任务实例记录,以反映任务的完成时间和状态。
ACT_HI_ACTINST 表
-
操作: 插入或更新活动历史记录。
-
描述: 当任务完成后,会在该表中插入一条新的历史活动实例记录,或者更新现有的活动实例记录,以反映任务的完成。
ACT_HI_IDENTITYLINK 表
-
操作: 插入或更新身份链接历史记录。
-
描述: 当任务完成后,与任务相关的身份链接信息会在该表中插入新的历史记录或者更新现有的记录。
-----------------------------------------------------------------------------------------------------------------------
主管再次审批,则就到了结束节点即任务完成,act_ru表(流程运行实例表)相关的记录就会被完善

PS:注意在人事审核节点的时候任务审批通过之后,将会生成新的任务ID流向主管审核

2、驳回
我们再部署第二个流程来进行驳回操作,接下来重复简单讲解(因为上面的已经说完了)
部署流程:

查询流程:

发起流程:

查询我的任务:

审核任务:

好了,终于又回到主管审核节点~ 重头戏来了~

驳回:
根据业务驳回可以是驳回到上一个节点,或者是驳回到起点重新开始。
这里讲一下驳回到起点重新开始。
1、在查询任务的时候,我们加上查询当前任务的流程实例ID

@DisplayName("我的(代办)任务查询")@Testvoid findMyTask(){//任务查询 通过TaskService来实现TaskService taskService = processEngine.getTaskService();//List<Task> list = taskService.createTaskQuery().taskAssignee("cpw").list(); //流程设置的 用户任务的办理人--人事List<Task> list = taskService.createTaskQuery().taskAssignee("zhuguan").list(); //流程设置的 用户任务的办理人--主管for (Task task : list) {System.out.println("任务ID:"+task.getId()+"---任务名称:"+task.getName()+"---任务当前办理人:"+task.getAssignee()+"---任务的流程实例ID:"+task.getProcessInstanceId());}}
2、根据实例ID获取到流程实例,然后根据流程实例获取到流程定义,然后取消当前流程实例,重新启动新的流程实例 (驳回流程)

以下是运行的过程的参数变化:

运行结果:

验证:看看流程是否从 主管审核 退回到起点 人事审核

ok兄弟们,驳回成功
@DisplayName("任务驳回")@Testvoid rejectTask(){String processInstanceId = "9cd07051-555d-11ef-ba69-581122450312";RuntimeService runtimeService = processEngine.getRuntimeService();RepositoryService repositoryService = processEngine.getRepositoryService();// 获取当前流程实例ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();System.out.println("获取当前流程实例:"+processInstance);// 获取流程定义ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();System.out.println("获取当前流程定义:"+processDefinition);// 取消当前流程实例runtimeService.deleteProcessInstance(processInstanceId, "驳回流程");System.out.println("取消流程实例ID:"+processInstanceId);// 启动新的流程实例runtimeService.startProcessInstanceById(processDefinition.getId());System.out.println("流程ID:"+processDefinition.getId()+"启动成功!!!");}
8、Springboot流程状态管理
重要的事情说三遍:是先有 流程定义 才会有 流程实例 才会有 流程任务
PS:操作流程使用RepositoryService方法
1、流程定义
查询所有的流程定义
@DisplayName("查询流程定义状态")@Testvoid selectProcessDefinition(){List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery().list();for (ProcessDefinition processDefinition : list) {//流程定义是否挂起 false为否(fasle表示该流程定义的状态为:激活) true为是(true表示该流程定义的状态为:挂起)System.out.println("流程定义的ID:"+processDefinition.getId()+"---流程定义的Name:"+processDefinition.getName()+"---该流程定义是否挂起:"+processDefinition.isSuspended());}}
运行结果:

processDefinition.isSuspended()查询流程定义是否挂起
false为否(fasle表示该流程定义的状态为:激活)
true为是(true表示该流程定义的状态为:挂起)
由此可以判断这两个《流程定义》都是激活状态
1、挂起《流程定义》
当一个流程定义被挂起时,新的流程实例将不能基于该流程定义启动,但已经运行的流程实例不受影响。
挂起流程定义主要有两种方法,一种是通过id 另一种是通过key,这里使用id来演示

@DisplayName("挂起流程定义")@Testvoid stopProcessDefinition(){String processDefinitionId = "flowDemo2:1:82ce3927-555c-11ef-859f-581122450312";processEngine.getRepositoryService().suspendProcessDefinitionById(processDefinitionId);System.out.println("流程定义ID为:"+processDefinitionId+"挂起成功!");}
运行结果:

检验:
第一种检验方法:

第二种方法:

当流程定义被挂起时,我们来测试一下新的流程实例能不能启动?

2、激活《流程定义》
当一个挂起的流程定义被激活时,新的流程实例可以基于该流程定义启动。
跟挂起流程定义同理,激活流程定义也是主要通过两种方法来激活(ID,KEY)

我们继续使用ID来激活:
@DisplayName("激活流程定义")@Testvoid startProcessDefinition(){String processDefinitionId = "flowDemo2:1:82ce3927-555c-11ef-859f-581122450312";processEngine.getRepositoryService().activateProcessDefinitionById(processDefinitionId);System.out.println("流程定义ID为:"+processDefinitionId+"激活成功!");}
运行结果:

检验:

好了,我好兄弟又激活回来了,我们来看看能否发起新的流程实例?

成功!
2、流程实例
操作流程实例使用RuntimeService方法
查询所有在运行的流程实例:
@DisplayName("查询流程实例状态")@Testvoid selectProcessInstance(){List<ProcessInstance> list = processEngine.getRuntimeService().createProcessInstanceQuery().list();for (ProcessInstance processInstance : list) {//processDefinition.isSuspended()查询流程定义是否挂起 false为否(fasle表示该流程定义的状态为:激活) true为是(true表示该流程定义的状态为:挂起)System.out.println("流程实例的ID:"+processInstance.getId()+"---流程实例的Name:"+processInstance.getName()+"---该流程实例是否挂起:"+processInstance.isSuspended());}}
运行结果:

1、挂起《流程实例》
当一个流程实例被挂起时,该实例中的任务将不能被执行,不能继续进行下一步的操作。
@DisplayName("挂起流程实例")@Testvoid stopProcessInstance(){String processInstanceId = "93012f42-5569-11ef-bb69-581122450312";processEngine.getRuntimeService().suspendProcessInstanceById(processInstanceId);System.out.println("流程实例ID为:"+processInstanceId+"挂起成功!");}
检验:

找到该挂起的流程实例下的用户任务,看看能否作业?


2、激活《流程实例》
当一个挂起的流程实例被激活时,该实例可以继续执行,进行下一步的操作。
@DisplayName("激活流程实例")@Testvoid startProcessInstance(){String processInstanceId = "93012f42-5569-11ef-bb69-581122450312";processEngine.getRuntimeService().activateProcessInstanceById(processInstanceId);System.out.println("流程实例ID为:"+processInstanceId+"激活成功!");}
运行结果:

检验:

那兄弟们再执行一下该流程实例的任务看看

OKOK 审批通过!!
完结撒花~~~~
----------------------------------------------------------------------------------------------------------------------------
9、资源
1.单元测试代码
附上全部测试代码:
@SpringBootTest
@DisplayName("流程测试")
public class UnitTest {//第一种方法使用ProcessEngine@Resourceprivate ProcessEngine processEngine;//第二种方法使用若干个 ***Service//@Autowired//private RuntimeService runtimeService;//@Autowired//private RepositoryService repositoryServiceService;@DisplayName("部署流程")@Testvoid deployFlow(){Deployment deploy = processEngine.getRepositoryService().createDeployment() //创建部署对象.addClasspathResource("processes/flowDemo2.bpmn20.xml") // 默认是在resources/processes下寻找,当然你可以自定义目录.name("第二个Flowable案例") //设置流程的名字.deploy(); //deploy部署流程System.out.println("流程部署ID为:"+deploy.getId()); //获取到当前流程的ID}@DisplayName("根据 流程部署ID 查询 流程定义ID ")@Testvoid selectProcessByDeployId(){String deployId = "82aa5d74-555c-11ef-859f-581122450312";//从上一个流程获取到的部署IDList<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery().deploymentId(deployId).list();for (ProcessDefinition processDefinition : list) {System.out.println("deployId对应的流程定义ID为:"+processDefinition.getId());}}@DisplayName("启动流程")@Testvoid startProcess(){// 前面关于部署和定义使用的是RepositoryService,接下来操作流程则使用RuntimeServiceRuntimeService runtimeService = processEngine.getRuntimeService();// act_re_procdef(流程定义)表中的idString processId = "flowDemo2:1:82ce3927-555c-11ef-859f-581122450312";runtimeService.startProcessInstanceById(processId);//String processKey = "flowDemo";//runtimeService.startProcessInstanceByKey(processKey);System.out.println("启动流程成功");}@DisplayName("全部任务查询")@Testvoid findAllTask(){List<Task> list = processEngine.getTaskService().createTaskQuery().list();for (Task task : list) {System.out.println("任务ID:"+task.getId()+"---任务名称:"+task.getName()+"---任务办理人:"+task.getAssignee());}}@DisplayName("我的(代办)任务查询")@Testvoid findMyTask(){//任务查询 通过TaskService来实现TaskService taskService = processEngine.getTaskService();List<Task> list = taskService.createTaskQuery().taskAssignee("cpw").list(); //流程设置的 用户任务的办理人--人事//List<Task> list = taskService.createTaskQuery().taskAssignee("zhuguan").list(); //流程设置的 用户任务的办理人--主管for (Task task : list) {System.out.println("任务ID:"+task.getId()+"---任务名称:"+task.getName()+"---任务当前办理人:"+task.getAssignee()+"---任务的流程实例ID:"+task.getProcessInstanceId());}}@DisplayName("任务审批通过")@Testvoid completeTask(){//任务相关操作通过TaskService来实现TaskService taskService = processEngine.getTaskService();String taskId = "93043c87-5569-11ef-bb69-581122450312";taskService.complete(taskId);System.out.println("审批通过");}@DisplayName("任务驳回")@Testvoid rejectTask(){String processInstanceId = "9cd07051-555d-11ef-ba69-581122450312";RuntimeService runtimeService = processEngine.getRuntimeService();RepositoryService repositoryService = processEngine.getRepositoryService();// 获取当前流程实例ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();System.out.println("获取当前流程实例:"+processInstance);// 获取流程定义ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();System.out.println("获取当前流程定义:"+processDefinition);// 取消当前流程实例runtimeService.deleteProcessInstance(processInstanceId, "驳回流程");System.out.println("取消流程实例ID:"+processInstanceId);// 启动新的流程实例runtimeService.startProcessInstanceById(processDefinition.getId());System.out.println("流程ID:"+processDefinition.getId()+"启动成功!!!");}@DisplayName("查询流程定义状态")@Testvoid selectProcessDefinition(){List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery().list();for (ProcessDefinition processDefinition : list) {//processDefinition.isSuspended()查询流程定义是否挂起 false为否(fasle表示该流程定义的状态为:激活) true为是(true表示该流程定义的状态为:挂起)System.out.println("流程定义的ID:"+processDefinition.getId()+"---流程定义的Name:"+processDefinition.getName()+"---该流程定义是否挂起:"+processDefinition.isSuspended());}}@DisplayName("挂起流程定义")@Testvoid stopProcessDefinition(){String processDefinitionId = "flowDemo2:1:82ce3927-555c-11ef-859f-581122450312";processEngine.getRepositoryService().suspendProcessDefinitionById(processDefinitionId);System.out.println("流程定义ID为:"+processDefinitionId+"挂起成功!");}@DisplayName("激活流程定义")@Testvoid startProcessDefinition(){String processDefinitionId = "flowDemo2:1:82ce3927-555c-11ef-859f-581122450312";processEngine.getRepositoryService().activateProcessDefinitionById(processDefinitionId);System.out.println("流程定义ID为:"+processDefinitionId+"激活成功!");}@DisplayName("查询流程实例状态")@Testvoid selectProcessInstance(){List<ProcessInstance> list = processEngine.getRuntimeService().createProcessInstanceQuery().list();for (ProcessInstance processInstance : list) {//processDefinition.isSuspended()查询流程定义是否挂起 false为否(fasle表示该流程定义的状态为:激活) true为是(true表示该流程定义的状态为:挂起)System.out.println("流程实例的ID:"+processInstance.getId()+"---流程实例的Name:"+processInstance.getName()+"---该流程实例是否挂起:"+processInstance.isSuspended());}}@DisplayName("挂起流程实例")@Testvoid stopProcessInstance(){String processInstanceId = "93012f42-5569-11ef-bb69-581122450312";processEngine.getRuntimeService().suspendProcessInstanceById(processInstanceId);System.out.println("流程实例ID为:"+processInstanceId+"挂起成功!");}@DisplayName("激活流程实例")@Testvoid startProcessInstance(){String processInstanceId = "93012f42-5569-11ef-bb69-581122450312";processEngine.getRuntimeService().activateProcessInstanceById(processInstanceId);System.out.println("流程实例ID为:"+processInstanceId+"激活成功!");}@DisplayName("删除全部流程")@Testvoid deleteAllFlowable(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = processEngine.getRepositoryService();// 删除所有流程定义repositoryService.createProcessDefinitionQuery().list().forEach(pd ->repositoryService.deleteDeployment(pd.getDeploymentId(), true));System.out.println("删除成功");}}
2.xml代码
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef"><process id="flowDemo" name="flowDemo" isExecutable="true"><startEvent id="sid-bb849ec0-1482-4057-a447-c8d68adc9ca2" name="开始"/><userTask id="sid-c30c0960-a986-4cd6-80e2-bae02d6af707" name="人事审核" flowable:assignee="cpw"></userTask><userTask id="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39" name="主管审核" flowable:assignee="zhuguan"/><endEvent id="sid-712aab8a-a060-4363-b3eb-cd3fa9215808"/><sequenceFlow id="sid-2c879163-8816-4dd7-89bb-bf1a4485fef4" sourceRef="sid-bb849ec0-1482-4057-a447-c8d68adc9ca2" targetRef="sid-c30c0960-a986-4cd6-80e2-bae02d6af707"/><sequenceFlow id="sid-2fca88ff-d597-430c-a5e9-edfefbdd2569" sourceRef="sid-c30c0960-a986-4cd6-80e2-bae02d6af707" targetRef="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39"/><sequenceFlow id="sid-e8b6c848-1c54-4fdb-a8b0-e085d715c906" sourceRef="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39" targetRef="sid-712aab8a-a060-4363-b3eb-cd3fa9215808"/></process><bpmndi:BPMNDiagram id="BPMNDiagram_flowDemo"><bpmndi:BPMNPlane bpmnElement="flowDemo2" id="BPMNPlane_flowDemo"><bpmndi:BPMNShape id="shape-d460f24b-1aac-4028-b625-929601acd3c0" bpmnElement="sid-bb849ec0-1482-4057-a447-c8d68adc9ca2"><omgdc:Bounds x="-375.0" y="-37.25" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-ad3ec780-dd25-42f1-8e53-6ed0608d5b91" bpmnElement="sid-c30c0960-a986-4cd6-80e2-bae02d6af707"><omgdc:Bounds x="-282.75" y="-62.25" width="100.0" height="80.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-2a1947d2-0365-4dd8-8083-9bdaa7bea300" bpmnElement="sid-fc70937d-9773-4db8-acb3-a3af3e6ccb39"><omgdc:Bounds x="-133.5509" y="-62.9491" width="100.0" height="80.0"/></bpmndi:BPMNShape><bpmndi:BPMNShape id="shape-c748377a-bdcd-4892-b001-3621cf05589a" bpmnElement="sid-712aab8a-a060-4363-b3eb-cd3fa9215808"><omgdc:Bounds x="15.625" y="-37.25" width="30.0" height="30.0"/></bpmndi:BPMNShape><bpmndi:BPMNEdge id="edge-a51efb51-2d88-4eae-9ffe-baa2e50cd0e5" bpmnElement="sid-2c879163-8816-4dd7-89bb-bf1a4485fef4"><omgdi:waypoint x="-345.0" y="-22.25"/><omgdi:waypoint x="-282.75" y="-22.25"/></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="edge-69253e56-2532-4829-b515-d27caeeb275a" bpmnElement="sid-2fca88ff-d597-430c-a5e9-edfefbdd2569"><omgdi:waypoint x="-182.75" y="-22.25"/><omgdi:waypoint x="-133.5509" y="-22.9491"/></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="edge-86a73120-1957-453e-b988-7f2ede9f75dc" bpmnElement="sid-e8b6c848-1c54-4fdb-a8b0-e085d715c906"><omgdi:waypoint x="-33.550903" y="-22.9491"/><omgdi:waypoint x="-10.011603" y="-22.25"/><omgdi:waypoint x="15.625" y="-22.25"/></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>
相关文章:
Springboot整合Flowable入门-学习笔记
目录 1、定义流程(画图) 2、Springboot部署流程 3、Springboot删除所有流程 4、Springboot根据 流程部署ID 查询 流程定义ID 5、Springboot启动(发起)流程 6、Springboot查询任务 6.1全部任务 6.2我的任务(代办任务) 7、…...
C语言常见的题目
1、 从源码到可执行文件会经历怎样的过程? 预编译:去掉空格、注释,处理预定义的指令,生成处理后的源代码文件。 编译:翻译成汇编代码,生成汇编文件。 汇编:翻译成机器码,生成一个或…...
Android13适配记录
多语言支持,此功能在国内被阉割 配置后在设置内可以选择 <?xml version"1.0" encoding"utf-8"?> <locale-config xmlns:android"http://schemas.android.com/apk/res/android"><locale android:name"zh" …...
Android TV上OTT PWA应用开发的播放器选择:video.js vs exoplayer
跨平台 OTT PWA 应用开发,最方便的当然是选用 video.js 库。但是既然是安卓平台,exoplayer 看起来总是最稳妥的选择 介绍 Exoplayer 是 Android media3 的一个实现,以前是独立出来的,现在已经合并到 androidx.media3 中了。 Vid…...
24.8.14 《CLR via C#》 笔记12
第十五章 枚举类型和位标志 使用枚举类型而不是硬编码的理由:枚举类型更易编写,阅读和维护;枚举类型是强类型枚举类型是值类型,不能定义任何方法,属性或事件,可利用扩展方法向枚举类型添加方法枚举类型定义…...
P2801 教主的魔法
[题目通道](教主的魔法 - 洛谷) 摘要 分块,是一种优雅的暴力,它通过对数列分段,完成对数列一些区间操作和区间查询的操作,是一种根号算法。 这篇学习笔记&题解是本萌新在学习分块过程中的一些感悟,希望能够帮助…...
Go 语言channel的应用场景及使用技巧
通过反映的方式执行 select 语句。这在处理有很多 case 子句,尤其是不定长 case 子句的情况时非常有用。 1. 使用反射操作 select 和 channel 使用 select 语句可以处理 chan 的 send 和 recv, send 和 recv 都可以作为 case 子句。如果需要同时处理两个 chan, 则可以写成下面…...
QLabel设置图像的方法+绘制文本换行显示
1、QLabel设置图像有两种方法 (1) void setPicture(const QPicture &); (2) void setPixmap(const QPixmap &); QPicture和QPixmap都是继承于QPaintDevice,它们都可以通过加载图片的方式获取:bool load(QIODevice *dev, const char *format …...
LVS原理及相关配置
1. 描述以及工作原理 1. 什么是 LVS linux virtural server 的简称,也就是 linxu 虚拟机服务器,这是一个 由章文嵩博士发起的开源项目,官网是 http://www.linuxvirtualserver.org,现在 lvs 已经是 linux 内核标 准的一部分,使用…...
webrtc一对一视频通话功能实现
项目效果 实现原理 关于原理我就不做说明,直接看图 WebRTC建立的时序图 系统用例逻辑 搭建环境 turn服务器:Ubuntu24.04搭建turn服务器 mkcert的安装和使用:配置https访问 必须使用https协议, 由于浏览器的安全策略导致的&am…...
通道(channel)传递数据的例子写一个
当然!以下是一个简单的 Go 程序示例,展示了如何使用通道(channel)在两个 goroutine 之间传递数据。示例代码 go package mainimport ("fmt""time" )// 发送数据到通道的 goroutine func sendData(ch chan int…...
Vue3+Echarts+饼图环形图
记得给容器宽高 <div id"leftChartguawang" style"height: 28vh"></div> 配置函数 const leftChartguawang () > {const chartBox echarts.init(document.getElementById(leftChartguawang))let datas [[{ name: 居民节能建筑, value…...
Python while编程题目|AI悦创Python一对一教学辅导
你好,我是悦创。 以下是十道有创意的while循环编程题目,每道题目都有一定的难度,适合锻炼编程逻辑和思维能力。 题目1:旋转字符串 描述:给定一个字符串,每次循环将字符串的第一个字符移到末尾࿰…...
C语言 | Leetcode C语言题解之第324题摆动排序II
题目: 题解: static inline void swap(int *a, int *b) {int c *a;*a *b;*b c; }static inline int partitionAroundPivot(int left, int right, int pivot, int *nums) {int pivotValue nums[pivot];int newPivot left;swap(&nums[pivot], &a…...
Docker③_VMware虚拟机和Docker的备份与恢复
目录 1. VMware虚拟机的快照备份 1.1 VMware本机的快照备份 1.2 VMware快照备份到另一电脑 2. Docker知识点 2.1 Docker镜像和容器的关系 2.2 Docker的存储卷 2.3 Docker命令简介 2.4 删除Anylink镜像 3. Docker备份和恢复 3.1 确定要回滚的容器和版本 3.2 备份当前…...
【EMC专题】ESD抑制器简要介绍
在ESD保护器件中可以分为陶瓷基类型和半导体基类型。其中有一类陶瓷基类型,使用的机制是电极间放电方法的产品就是ESD抑制器。本文章简要介绍了ESD抑制器的特点、基本结构和特性。 ESD抑制器的特点 ESD抑制器是间隙型的ESD(静电放电 Electrostatic Discharge)对策保护元件,…...
贷齐乐系统最新版SQL注入(绕过WAF可union select跨表查询)
目录 标题:贷齐乐系统最新版SQL注入(绕过WAF可union select跨表查询) 内容: 一,环境部署 二,源码分析 三,sql注入 总结: [回到顶部](#article_top) 一&am…...
『大模型笔记』虚拟机(Virtual Machine,VM)与Docker对比!
『大模型笔记』虚拟机(Virtual Machine,VM)与Docker对比! 文章目录 一. 虚拟机(Virtual Machine,VM)与Docker对比!1. 定义这两种技术2. 工作原理3. 关于如何选择适合工作负载的技术的指导二. 参考文献Docker 只是一个轻量级的虚拟机吗?虽然二者确实有一个共同点,即 虚…...
基于SpringBoot+Vue框架的租车管理系统
文章目录 一、项目介绍二、项目类型三、技术栈介绍1.客户端技术栈2.服务端技术栈 四、项目创新点五、项目功能介绍1.客户端功能2.服务端功能 六、项目的主要截图页面如下展示1.客户端展示2.服务端展示 七、项目源码 一、项目介绍 大家好,我是执手天涯,…...
HAProxy基本配置及参数实操
目录 编辑什么是负载均衡 为什么用负载均衡 四层和七层的区别 实验环境 实验步骤 webserver上安装nginx 启动nginx 安装haproxy 编辑配置文件 多进程 多线程 SORRY SERVER 访问重定向 maxconne最大可承受连接 socat 工具 常用示例 ha p r ox y 的 算 法 静 …...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
