当前位置: 首页 > news >正文

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、定义流程&#xff08;画图&#xff09; 2、Springboot部署流程 3、Springboot删除所有流程 4、Springboot根据 流程部署ID 查询 流程定义ID 5、Springboot启动(发起)流程 6、Springboot查询任务 6.1全部任务 6.2我的任务&#xff08;代办任务&#xff09; 7、…...

C语言常见的题目

1、 从源码到可执行文件会经历怎样的过程&#xff1f; 预编译&#xff1a;去掉空格、注释&#xff0c;处理预定义的指令&#xff0c;生成处理后的源代码文件。 编译&#xff1a;翻译成汇编代码&#xff0c;生成汇编文件。 汇编&#xff1a;翻译成机器码&#xff0c;生成一个或…...

Android13适配记录

多语言支持&#xff0c;此功能在国内被阉割 配置后在设置内可以选择 <?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 应用开发&#xff0c;最方便的当然是选用 video.js 库。但是既然是安卓平台&#xff0c;exoplayer 看起来总是最稳妥的选择 介绍 Exoplayer 是 Android media3 的一个实现&#xff0c;以前是独立出来的&#xff0c;现在已经合并到 androidx.media3 中了。 Vid…...

24.8.14 《CLR via C#》 笔记12

第十五章 枚举类型和位标志 使用枚举类型而不是硬编码的理由&#xff1a;枚举类型更易编写&#xff0c;阅读和维护&#xff1b;枚举类型是强类型枚举类型是值类型&#xff0c;不能定义任何方法&#xff0c;属性或事件&#xff0c;可利用扩展方法向枚举类型添加方法枚举类型定义…...

P2801 教主的魔法

[题目通道](教主的魔法 - 洛谷) 摘要 分块&#xff0c;是一种优雅的暴力&#xff0c;它通过对数列分段&#xff0c;完成对数列一些区间操作和区间查询的操作&#xff0c;是一种根号算法。 这篇学习笔记&题解是本萌新在学习分块过程中的一些感悟&#xff0c;希望能够帮助…...

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&#xff0c;它们都可以通过加载图片的方式获取&#xff1a;bool load(QIODevice *dev, const char *format …...

LVS原理及相关配置

1. 描述以及工作原理 1. 什么是 LVS linux virtural server 的简称&#xff0c;也就是 linxu 虚拟机服务器&#xff0c;这是一个 由章文嵩博士发起的开源项目&#xff0c;官网是 http://www.linuxvirtualserver.org,现在 lvs 已经是 linux 内核标 准的一部分&#xff0c;使用…...

webrtc一对一视频通话功能实现

项目效果 实现原理 关于原理我就不做说明&#xff0c;直接看图 WebRTC建立的时序图 系统用例逻辑 搭建环境 turn服务器&#xff1a;Ubuntu24.04搭建turn服务器 mkcert的安装和使用&#xff1a;配置https访问 必须使用https协议&#xff0c; 由于浏览器的安全策略导致的&am…...

通道(channel)传递数据的例子写一个

当然&#xff01;以下是一个简单的 Go 程序示例&#xff0c;展示了如何使用通道&#xff08;channel&#xff09;在两个 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一对一教学辅导

你好&#xff0c;我是悦创。 以下是十道有创意的while循环编程题目&#xff0c;每道题目都有一定的难度&#xff0c;适合锻炼编程逻辑和思维能力。 题目1&#xff1a;旋转字符串 描述&#xff1a;给定一个字符串&#xff0c;每次循环将字符串的第一个字符移到末尾&#xff0…...

C语言 | Leetcode C语言题解之第324题摆动排序II

题目&#xff1a; 题解&#xff1a; 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跨表查询)

目录 标题&#xff1a;贷齐乐系统最新版SQL注入&#xff08;绕过WAF可union select跨表查询&#xff09; 内容&#xff1a; 一&#xff0c;环境部署 二&#xff0c;源码分析 三&#xff0c;sql注入 总结&#xff1a; [回到顶部]&#xff08;#article_top&#xff09; 一&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.服务端展示 七、项目源码 一、项目介绍 ​大家好&#xff0c;我是执手天涯&#xff0c;…...

HAProxy基本配置及参数实操

目录 ​编辑什么是负载均衡 为什么用负载均衡 四层和七层的区别 实验环境 实验步骤 webserver上安装nginx 启动nginx 安装haproxy 编辑配置文件 多进程 多线程 SORRY SERVER 访问重定向 maxconne最大可承受连接 socat 工具 常用示例 ha p r ox y 的 算 法 静 …...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...