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 的 算 法 静 …...
go-zero中间件的使用
一、自定义中间件 1、在api中在服务中定义一个中间件,名字随便取 type PostDemoReq {Name string json:"name" validate:"required" // 姓名Age int64 json:"age" validate:"required,gte1,lte130" // 年龄// optional 表示可选,omi…...
六、ESP32-S3上使用MicroPython点亮WS2812智能LED灯珠并通过web控制改变灯珠颜色优化超时和线程
实现通过ESP32S3连接Wi-Fi并使用Web页面控制WS2812灯珠的颜色,可以使用ESP32的WebServer库来创建一个简单的Web界面。通过这个界面,可以动态地控制灯珠的显示效果。 针对 五、ESP32-S3上使用MicroPython点亮WS2812智能LED灯珠并通过web控制改变灯珠颜色…...
(el-Time-Picker)操作(不使用 ts):Element-plus 中 TimePicker 组件的使用及输出想要时间格式需求的解决过程
Ⅰ、Element-plus 提供的 TimePicker 时间选择器组件与想要目标情况的对比: 1、Element-plus 提供 TimePicker 组件情况: 其一、Element-ui 自提供的 TimePicker 代码情况为(示例的代码): // Element-plus 提供的组件代码: <template>…...
UIAbility组件基础(一)
一、概述 UIAbility组件是一种包含UI的应用组件,主要用于和用户交互。UIAbility组件是系统调度的基本单元,为应用提供绘制界面的窗口。一个应用可以包含一个或多个UIAbility组件。每一个UIAbility组件实例都会在最近任务列表中显示一个对应的任务。 U…...
神经网络的数学原理
前言:Hello大家好,我是小哥谈。人工智能技术的发展与成功应用已经成为21世纪科技领域最大的新现象。然而,科学地理解人工智能原理已经超出了现有科学体系的范畴。显然,人工智能是人类科学技术发展的必然结果,人工智能科学也将是人类科学进步与发展必然实现的目标🌈 …...
Java设计模式-抽象工厂模式-一次性理解透
1. 抽象工厂模式简介 抽象工厂设计模式是创建型模式之一。抽象工厂模式与工厂模式几乎相似,只是它更像工厂中的工厂。 如果您熟悉Java 中的工厂设计模式,或看过上一篇我写的“java简单工厂模式”,您会注意到我们有一个工厂类。此工厂类根据…...
day16-测试自动化之selenium的PO模式
一、PO模式介绍 PO(Page Object)模式是一种在自动化测试中常用的设计模式,将页面的每个元素封装成一个对象,通过操作对象来进行页面的交互。 一般分为六个版本,现在大部分企业都用的V4版本,三层结构…...
Springboot+freemarker大段文本内容动态修改输出,所见即所得
场景:给领导导出数据时,需要给出一个针对专业名词的解释说明,因此会存在有大批量的、大段的文本内容。如果直接写在代码里面,没啥大问题,但是大量的拼接替换、格式样式、后续修改维护等,都不是很方便。如果…...
Kali Linux网络问题解决与静态IP配置技巧
很多用户在使用 Kali Linux 时会遇到无法联网的问题,尤其是在 VMware 虚拟机中。这种情况相当常见,一般都是没有配置DNS服务器或者网卡配置文件的IP和虚拟网络编辑器的IP不一致所导致的,下面我们将探讨如何在 Kali Linux 中配置 DNS 服务和设…...
网络状态码-经验笔记
网络状态码-经验笔记 引言 在网络通信中,HTTP(Hypertext Transfer Protocol)状态码是服务器向客户端(通常是Web浏览器)发送响应时所包含的重要信息之一。 这些状态码指示了客户端请求的结果。 了解并正确使用这些状态…...
网站建设成本计划书/知识付费网站搭建
第一部分 简 介 一. 硬盘结构简介 1. 硬盘参数释疑 到目前为止,人们常说的硬盘参数还是古老的 CHS (Cylinder/Head/Sector)参数。那么为什么要使用这些参数,它们的意义是什么?它们的取值范围是什么? 很久以前(long long ago .…...
拓者室内设计网站/千锋教育和达内哪个好
系统环境 * 3 Ubuntu 16.04 mysql 8.0.12 docker 18.06.1-ce docker-compose 1.23.0-rc3 *3 > PS ###我用的是虚拟机, 所以起了3个虚拟机,分配 IP如下### 主IP :192.168.100.100 从IP :192.168.100.101、192.168.100.102 #开…...
哪里有做网站培训的/迅雷磁力
当前pyecharts版本为1.9.0 概述 render包结构 render包位于pyecharts包顶级目录中,用于渲染图表。render包结构如下: ├─render # 渲染设置包 │ │ display.py # 定义HTML、JavaScript显示类,用于支持在notebook中嵌入输出结果 │ │…...
驻马店做网站的公司/seo的工具有哪些
viewpager.getChildCount() 非常easy误解成viewpager子页面的size。它和getCount还是有差别的 getChildCount() 是表示当前可见页size 比方:Viewpager总共3页 当到第一页时候可见页面为2(在滑动过程,可见第一张和第二张)ÿ…...
做网站要注意什么问题/百度贴吧官网网页
JVM类加载过程JVM类加载过程分为几个阶段,分别是加载、验证、准备、解析和初始化。加载是把二进制字节码载入内存,验证是校验字节流中包含的信息是否符合当要求,准备是为静态变量分配内存并设置静态变量初始值,解析是把常量池内的…...
泉州网站建设公司首选/百度免费下载安装百度
最近一直在调试ADS8364,我的系统中首先通过采样电阻采样MOSFET驱动的电磁铁的电流,采样出来是电压信号,电压信号送入隔离运放7800,在进7800之前并联了一个电容滤波,7800出来的是差分信号经过仪表放大器放大之后送入836…...