一行代码,实现请假审批流程(Java版)

图片

  • 首先画一个流程图

  • 测试流程图

  • activiti 项目基础配置

  • activiti 工作流引擎数据库设计

  • 工作流引擎API 介绍

  • 什么是BPMN流程图

  • 工作流引擎同类对比

  • 继续学习方向

  • 总结


工作流审批功能是办公OA系统核心能力,如果让你设计一个工作流审批系统,你会吗?千万不要小瞧OA内部系统的复杂性,大家可以头脑风暴思考一下实现方案。

要明白工作流审批涉及多个用户的任务流转,多个流程分支跳转,虽然是办公内部系统,但是这个系统并不简单如果没有强大的工作流引擎,难以高效扩展旧流程,难以增加新流程,工作流审批将成为公司所有人的噩梦

但是在使用 activiti开源工作流引擎后,一切痛苦与噩梦均烟消云散~

activiti 支持新增流程非常简单,只需要两步

  • 画个流程图

  • 搭配前端页面

首先画一个流程图

将文章开头的需求,转化为 activiti 流程图,使用Idea 安装 actiBPM 插件,创建该流程图,文件命名apply.mpmn,实现请假流程的二级审批能力

  • 一级主管审批

  • 超过3天,二级主管审批。

图片

img

测试流程图

首先创建工作流引擎,部署流程图
//创建工作流引擎
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();//RepositoryService用于部署流程图
RepositoryService repositoryService = engine.getRepositoryService();//部署请假流程图
repositoryService.createDeployment().addClasspathResource("processes/apply.bpmn").deploy();

部署流程图,这部分工作一般放在工作流的后台系统,开发创建好流程图以后,上传部署到系统中。无需开发修改代码

repositoryService.createDeployment().addClasspathResource("processes/apply.bpmn").deploy();这行代码负责部署流程图到流程引擎。工作流引擎会解析该流程图文件,创建流程模版,接下来就可以在使用该流程模版,发起流程实例了。

员工zhang3,发起新流程,设置审批人
Map<String, Object> variableMap = new HashMap<>();
variableMap.put("applyUser", "zhang3");
variableMap.put("supervisor", "li4");
variableMap.put("upperSupervisor", "wang5");

员工zhang3 提出请假申请,在发起新流程时,通过OA其他系统,查到zhang3的一级主管是li4,二级主管是wang5,于是设置上审批人。有人疑问,以下变量applyUser等,是系统默认的,还是在哪里指定的?在创建主管审批节点时,指定审批人变量 ${applyUser}

图片

img

想象一下,如果请假类的流程均可能需要一二级主管审批,是不是可以在发起流程时,统一填充一二级主管 审批人变量。这部分代码是不是就是通用的,新增流程时无需二次修改了。

发起一个新流程
ProcessInstance instance = runtimeService.startProcessInstanceByKey("apply_processor_1", variableMap);

如上代码指定了全流程审批人,发起了一个新流程。有人会疑问apply_processor_1 是什么?在哪里指定的,这是流程模版的 Key,在使用Idea插件画流程图时,需要指定流程图的Key

图片

img

申请人设置请假天数
TaskService taskService = engine.getTaskService();
Task firstTask = taskService.createTaskQuery().taskAssignee("zhang3").singleResult();taskService.complete(firstTask.getId(), Maps.newHashMap("day", 4));

创建并开启流程实例后,工作流引擎相当于帮你执行了 流程图的 开始节点,然后流程执行到 请假申请节点,此时通过 taskService 查询 zhang3的 处理任务。TaskService是通过第一步ProcessEngine 获取到的,主要用于任务查询。

有人会疑问,为什么要区分创建流程、处理申请人审批任务两个步骤,我们在提请假申请时,只需要提申请一步就完成了。实际上 提请假申请时,系统会帮你创建好流程,然后自动替你完成审批。

为什么工作流引擎要区分为两步呢?所有的流程图都需要经过 开始节点,也都需要结束节点,如此设计方式,可以让工作流引擎的抽象层次更高。它可以在开始和结束时点建立事件通知,维护流程状态的完整性。

接下来,zhang3 通过 taskService完成该任务,并且设置变量 day=4,即请假天数是4天。

一级主管审批任务
Task secondTask = taskService.createTaskQuery().taskAssignee("li4").singleResult();taskService.setVariable(secondTask.getId(), "result1", true);

接下来,zhang3和 领导li4 说,”我家里有事要请假,辛苦4哥审批一下“,领导在自己的审批后台查询 审批任务,查到后,通过了审批任务。

有人会疑问,怎么标识 审批通过和不通过呢?result1 是什么东西?系统默认的,还是在何处甚至的变量? 在流程图上配置的

图片

img

工作流引擎没有审批通过不通过的概念。当流程上存在  A 和 B 两个分支时,流程图上可以使用排他网关进行分支判定。如请假流程图中,一级审批结果就是一个排他网关。在网关的下游分支上配置如果要走A分支,应该满足哪些条件;走B分支,要满足哪个条件;

而排他网关上并没有配置路由条件。例如在一级主管审批后,流程上设置新的变量 result1,经过排他网关时,A分支 是 #{result1==true} 判定通过,于是走了A分支。工作流引擎负责检查网关的下级分支的条件是否满足,哪个条件满足走哪个分支。

也就是说上一个任务在处理时,并不知道接下来走哪个分支,也没有指定走哪个分支,而是将自己的处理结果放到流程变量中,在排他网关的下游分支条件上根据流程变量进行判断,接下来走哪个分支,这就是工作流引擎对于流程的抽象。

工作流引擎负责驱动流程到排他网关,至于走哪个分支,由分支上的条件决定!这个思维一定要记住哦~

继续剩余的流程

一级主管审批通过后,需要判断是否需要二级审批,这时又有一个排他网关,使用的条件就是 #{day>3},或者 #{day<=3}

在第2步中,zhang3 申请了 4天的假期,于是走到了二级主管审批页面。二级主管wang5 在审批任务列表中,找到了zhang3的请假申请,然后点击了通过。

Task thirdTask = taskService.createTaskQuery().taskAssignee("wang5").singleResult();
if (thirdTask != null) {taskService.setVariable(thirdTask.getId(), "result2", true);log.warn("用户任务完成,流程ID:{}, 任务名称:{}, id:{}, assignee:{}", thirdTask.getProcessInstanceId(), thirdTask.getName(), thirdTask.getId(), thirdTask.getAssignee(), thirdTask.getDelegationState());taskService.complete(thirdTask.getId());
} else {log.warn("没有查到二级主管审批任务");
}

接下来流程上没有其他审批任务,但是引擎依然会继续驱动流程,如下图中二级主管审批结果通过或拒绝,分别进入到两个不同的终点。

图片

img

值得一提的是,上面的代码仅仅是各个审批人在处理审批任务时,必要的代码、通用的代码。

如审批是否通过这一流程变量,完全可以统一规范,无需二次开发。例如 二级主管审批通过设置了 result2,实际上可以使用 二级主管审批这个节点的id 后缀,如 result_10来代表执行结果,规范以后,流程审批代码更为统一。新增流程模版时,在审批任务节点,审批通过或审批拒绝均不需要再次开发代码。

惊喜的事情是:我们没有开发任何一行流程驱动和分支判定相关的代码。

但是我们依然需要处理前端页面。因为请假申请页面上,不同的假期类型需要的表单参数不同,需要新增前端页面,新增发起流程的后端接口。

相比整体的流程控制代码,这部分开发工作量大大降低,难度更是大大降低。

接下来,我提出一个问题,如果新增一个需求,要求请病假的时候,需要HR审批,你知道如何修改流程图,支持新的处理流程吗?

很简单,在审批通过终节点的前面,再加一个排他网关,判断请假类型== ”带薪病假“,如果条件通过则增加HR审批节点,如果条件不通过,则直接走到 审批通过的 终点。不需要开发一行代码就能支持需求哦~

有了工作流引擎 activiti ,新增和修改流程 都变得非常简单,它帮我们完成了所有的流程驱动和分支判定工作。这就是工作流引擎的价值之一。

如何查看完整的执行流程图

通过流程引擎 ProcessEngine获取HistoryService;通过流程实例id,拿到流程执行的所有节点。执行记录如下图,可完整看到流程执行的完整过程。

List<HistoricActivityInstance> activityInstanceList = 
historyService.createHistoricActivityInstanceQuery().processInstanceId(instance.getId()).list();
for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {log.warn("activityName:{},activityType:{}, assignee:{}, taskId:{}",historicActivityInstance.getActivityName(),historicActivityInstance.getActivityType(),historicActivityInstance.getAssignee(),historicActivityInstance.getTaskId());

图片

img

activiti 项目基础配置

首先引入activiti pom
<dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter-basic</artifactId><version>5.23.0</version>
</dependency>

我使用的是activiti 5.x,配套的 Springboot starter parent

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.4</version><relativePath/> 
</parent>
配置数据源和工作流引擎配置类

activiti 需要使用方提供数据库配置,在项目启动时,activiti 会自动检查数据库是否包含activiti 相关的表,如果不包含,会自动帮你建表。

在学习activiti 的过程中,我没有安装MySQL,而是使用H2 内存数据库,该数据库在Java进程中,随JVM同生共死,无需担心重复启动,数据被污染等问题,非常适合学习activiti 的时候使用

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="org.h2.Driver"/><!--<property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;mode=MySQL;TRACE_LEVEL_SYSTEM_OUT=2"/>--><property name="url" value="jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;mode=MySQL;"/></bean><bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><property name="dataSource" ref="dataSource"/><property name="databaseSchemaUpdate" value="true"/></bean><context:component-scan base-package="com.muppet.activiti"/>
</beans>

H2 需要的POM如下

<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope>
</dependency>
全部示例代码
@SpringBootTest(classes = {ActivitiStartApplication.class})
class ActivitiStartApplicationTests {public static final Logger log = LoggerFactory.getLogger(ActivitiStartApplicationTests.class);@Autowiredprivate ApplyTaskListener applyTaskListener;@Testvoid contextLoads() {System.out.println("启动成功");ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();RepositoryService repositoryService = engine.getRepositoryService();RuntimeService runtimeService = engine.getRuntimeService();TaskService taskService = engine.getTaskService();HistoryService historyService = engine.getHistoryService();repositoryService.createDeployment().addClasspathResource("processes/apply.bpmn").deploy();Map<String, Object> variableMap = new HashMap<>();variableMap.put("applyUser", "zhang3");variableMap.put("supervisor", "li4");variableMap.put("upperSupervisor", "wang5");ProcessInstance instance = runtimeService.startProcessInstanceByKey("apply_processor_1", variableMap);Task firstTask = taskService.createTaskQuery().taskAssignee("zhang3").singleResult();log.warn("用户任务完成,流程ID:{}, 任务名称 :{}, id:{}, assignee:{}", firstTask.getProcessInstanceId(), firstTask.getName(), firstTask.getId(), firstTask.getAssignee());taskService.complete(firstTask.getId(), Maps.newHashMap("day", 4));Task secondTask = taskService.createTaskQuery().taskAssignee("li4").singleResult();taskService.setVariable(secondTask.getId(), "result1", true);log.warn("用户任务完成流程ID:{}, 任务名称 :{}, id:{}, assignee:{}", secondTask.getProcessInstanceId(), secondTask.getName(), secondTask.getId(), secondTask.getAssignee());taskService.complete(secondTask.getId());Task thirdTask = taskService.createTaskQuery().taskAssignee("wang5").singleResult();if (thirdTask != null) {taskService.setVariable(thirdTask.getId(), "result2", true);log.warn("用户任务完成,流程ID:{}, 任务名称:{}, id:{}, assignee:{}", thirdTask.getProcessInstanceId(), thirdTask.getName(), thirdTask.getId(), thirdTask.getAssignee(), thirdTask.getDelegationState());taskService.complete(thirdTask.getId());} else {log.warn("没有查到二级主管审批任务");}log.warn("流程执行过程如下");List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(instance.getId()).list();for (HistoricActivityInstance historicActivityInstance : activityInstanceList) {log.warn("activityName:{},activityType:{}, assignee:{}, taskId:{}",historicActivityInstance.getActivityName(),historicActivityInstance.getActivityType(),historicActivityInstance.getAssignee(),historicActivityInstance.getTaskId());}}
}

activiti 工作流引擎数据库设计

为了保证流程的可靠性和可恢复性,工作流引擎通常会将流程实例的状态和数据持久化存储到中。在流程执行过程中,引擎会不断地更新数据库中的状态数据。activiti 共包含了一系列用于存储流程定义、运行时数据以及历史记录的表。

1. 流程定义相关表

ACT_RE_*系列表:主要包括流程定义(Process Definitions)、流程资源(Resources)和其他静态信息的存储。

2. 运行时数据表

ACT_RU_*系列表:这些表存放了流程实例执行过程中的实时数据,如任务(Tasks)、流程实例(Process Instances)、变量(Variables)、执行对象(Executions)等。

3. 历史数据表

ACT_HI_*系列表:当流程实例结束或达到特定条件时,相关的运行时数据会被迁移到历史表中,以供后期审计、报告分析之用。

4. 身份和权限表

ACT_ID_*系列表:主要用于存储用户、组以及相关的身份和权限信息。

5. 其他辅助表

包括事件日志表:(Event Log)、作业及定时器表(Job and Timer entities)等,它们服务于调度、异步处理等功能需求。

工作流引擎API 介绍

图片

img

  • ProcessEngine: 表示Activiti工作流引擎的入口,用于获取各种管理API操作的对象。

  • RepositoryService: 用于管理流程定义的API,包括流程的部署和删除等操作。

  • RuntimeService: 用于管理流程实例的API,包括启动、暂停和删除流程实例等操作。

  • TaskService: 用于管理任务的API,包括创建、完成和查询任务等操作。

  • HistoryService: 用于查询历史记录的API,包括查询已完成的任务、流程实例和变量等信息。

这5个Service我们已经很熟悉了,现在跟大家介绍这部分API,大家应该更容易理解了。

其中 ProcessEngine我们用来获取各类Service类,RepositoryService 用来部署流程图,RuntimeService用来创建流程图实例、TaskService用来查询任务和完成任务;HistoryService用来查看流程执行过程。

什么是BPMN流程图

BPMN(‌Business Process Modeling Notation)‌是一种流程建模的通用和标准语言,‌用来绘制业务流程图,‌以便更好地让各部门之间理解业务流程和相互关系。‌

BPMN 1.0规范于2004年5月对外发布,‌而BPMN 2.0标准由OMG于2011年推出,‌对BPMN进行了重新定义。‌

Activiti 是由 jBPM 的创建者 Tom Baeyens 离开 JBoss 之后建立的项目,构建在开发 jBPM 版本 1 到 4 时积累的多年经验的基础之上,旨在创建下一代的 BPM 解决方案。

同时 Activiti 选择了 Apache 许可,一方面是希望 Activiti 能有更长久的生命力,因为它不受任何个人或是公司的控制而是属于整个社区,另一方面更是希望这个宽松的许可能够让 Activiti BPM 引擎和 BPMN2.0 被更广泛的采纳、使用和商业化。

Idea actiBPM 插件如何安装

https://www.cnblogs.com/No2-explorer/p/11032469.html

Idea新建的流程图是什么

本质是一个XML,可以通过BPMN 可视化工具解析,如下XML代码是 请假流程图

创建一个二级主管审批节点

<userTask activiti:assignee="${upperSupervisor}" activiti:async="false" activiti:exclusive="true" id="_10" name="二级主管审批"/>

创建一个审批结果的分支

分支上包含连接了哪两个节点,以及分支的条件表达式 #{result2==true}

<sequenceFlow id="_19" sourceRef="_17" targetRef="_8"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==false}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_20" sourceRef="_17" targetRef="_11"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==true}]]></conditionExpression>
</sequenceFlow>

请假流程图完整XML

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1723259512248" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema"><process id="apply_processor_1" isClosed="false" isExecutable="true" processType="None"><startEvent id="_4" name="开始"><extensionElements><activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/></extensionElements></startEvent><userTask activiti:assignee="#{applyUser}" activiti:async="false" activiti:exclusive="true" id="_5" name="请假申请"/><userTask activiti:assignee="${supervisor}" activiti:async="false" activiti:exclusive="true" id="_6" name="主管审批"><extensionElements><activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/></extensionElements></userTask><exclusiveGateway gatewayDirection="Unspecified" id="_7" name="一级审批结果"/><endEvent id="_8" name="审批不通过"><extensionElements><activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/></extensionElements></endEvent><exclusiveGateway gatewayDirection="Unspecified" id="_9" name="天数验证2"/><userTask activiti:assignee="${upperSupervisor}" activiti:async="false" activiti:exclusive="true" id="_10" name="二级主管审批"/><endEvent id="_11" name="审批通过"><extensionElements><activiti:executionListener class="com.muppet.activiti.listener.ApplyTaskListener" event="end"/></extensionElements></endEvent><sequenceFlow id="_2" sourceRef="_4" targetRef="_5"/><sequenceFlow id="_3" sourceRef="_5" targetRef="_6"/><sequenceFlow id="_12" sourceRef="_6" targetRef="_7"/><sequenceFlow id="_13" sourceRef="_7" targetRef="_8"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result1==false}]]></conditionExpression></sequenceFlow><sequenceFlow id="_14" sourceRef="_7" targetRef="_9"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result1==true}]]></conditionExpression></sequenceFlow><sequenceFlow id="_15" sourceRef="_9" targetRef="_10"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{day>3}]]></conditionExpression></sequenceFlow><sequenceFlow id="_16" sourceRef="_9" targetRef="_11"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{day<=3}]]></conditionExpression></sequenceFlow><exclusiveGateway gatewayDirection="Unspecified" id="_17" name="审批结果2"/><sequenceFlow id="_18" sourceRef="_10" targetRef="_17"/><sequenceFlow id="_19" sourceRef="_17" targetRef="_8"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==false}]]></conditionExpression></sequenceFlow><sequenceFlow id="_20" sourceRef="_17" targetRef="_11"><conditionExpression xsi:type="tFormalExpression"><![CDATA[#{result2==true}]]></conditionExpression></sequenceFlow></process><bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram"><bpmndi:BPMNPlane bpmnElement="apply_processor_1"><bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4"><omgdc:Bounds height="32.0" width="32.0" x="525.0" y="170.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5"><omgdc:Bounds height="55.0" width="85.0" x="495.0" y="285.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6"><omgdc:Bounds height="55.0" width="85.0" x="500.0" y="390.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7" isMarkerVisible="false"><omgdc:Bounds height="32.0" width="32.0" x="520.0" y="495.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_8" id="Shape-_8"><omgdc:Bounds height="32.0" width="32.0" x="295.0" y="825.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_9" id="Shape-_9" isMarkerVisible="false"><omgdc:Bounds height="32.0" width="32.0" x="515.0" y="600.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_10" id="Shape-_10"><omgdc:Bounds height="55.0" width="95.0" x="485.0" y="720.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="55.0" width="95.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_11" id="Shape-_11"><omgdc:Bounds height="32.0" width="32.0" x="720.0" y="600.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNShape bpmnElement="_17" id="Shape-_17" isMarkerVisible="false"><omgdc:Bounds height="32.0" width="32.0" x="510.0" y="810.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNShape><bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13" sourceElement="_7" targetElement="_8"><omgdi:waypoint x="520.1953352769677" y="511.0000000000001"/><omgdi:waypoint x="310.0" y="700.0"/><omgdi:waypoint x="310.0" y="825.0808012104277"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_7"><omgdi:waypoint x="535.9999999999999" y="444.97894395853575"/><omgdi:waypoint x="535.9999999999999" y="495.19533527696785"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_15" id="BPMNEdge__15" sourceElement="_9" targetElement="_10"><omgdi:waypoint x="530.9999999999999" y="632.3606171769437"/><omgdi:waypoint x="530.9999999999999" y="719.6793002915451"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_14" id="BPMNEdge__14" sourceElement="_7" targetElement="_9"><omgdi:waypoint x="533.4999999999999" y="524.4297052154194"/><omgdi:waypoint x="533.4999999999999" y="602.2445273261599"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_16" id="BPMNEdge__16" sourceElement="_9" targetElement="_11"><omgdi:waypoint x="546.8934865508442" y="616.0"/><omgdi:waypoint x="719.6793107624761" y="616.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_19" id="BPMNEdge__19" sourceElement="_17" targetElement="_8"><omgdi:waypoint x="526.0000000000001" y="842.0919987042433"/><omgdi:waypoint x="420.0" y="880.0"/><omgdi:waypoint x="311.0" y="857.0256933063517"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="4.0" x="0.0" y="-10.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_18" id="BPMNEdge__18" sourceElement="_10" targetElement="_17"><omgdi:waypoint x="526.0000000000001" y="774.9649065975595"/><omgdi:waypoint x="526.0000000000001" y="810.6344887161214"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_2" id="BPMNEdge__2" sourceElement="_4" targetElement="_5"><omgdi:waypoint x="541.0" y="202.23782176749842"/><omgdi:waypoint x="541.0" y="285.1689882302127"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_3" id="BPMNEdge__3" sourceElement="_5" targetElement="_6"><omgdi:waypoint x="540.0" y="339.5907569376957"/><omgdi:waypoint x="540.0" y="389.6933376525213"/><bpmndi:BPMNLabel><omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge><bpmndi:BPMNEdge bpmnElement="_20" id="BPMNEdge__20" sourceElement="_17" targetElement="_11"><omgdi:waypoint x="541.3655112838787" y="826.0"/><omgdi:waypoint x="659.0" y="724.0"/><omgdi:waypoint x="719.6793107624761" y="616.0"/><bpmndi:BPMNLabel><omgdc:Bounds height="1.0" width="127.0" x="0.0" y="76.0"/></bpmndi:BPMNLabel></bpmndi:BPMNEdge></bpmndi:BPMNPlane></bpmndi:BPMNDiagram>
</definitions>

工作流引擎同类对比

比较流行的 Camunda 和 Flowable 都是基于activiti 开发的。

图片

img

同类框架对比

图片

 

 

3个框架的使用流程基本一致

使用流程 5板斧
  • 定义BPMN流程图, 使用建模工具设计定义流程图。

  • 部署流程, 将BPMN流程图部署到工作流引擎中。

  • 启动流程实例, 通过工作流引擎基于流程模版,启动新流程实例。

  • 执行任务, 流程执行中,引擎会为任务节点创建任务,分配给对应执行人。

  • 监听事件, 开发者可以注册监听器来捕获流程执行过程中的各种事件,例如任务完成、流程结束等。

  • 查询和监控, 工作流引擎通常提供了查询和监控功能,允许开发者和管理人员查看流程实例的状态、任务执行情况以及历史数

继续学习方向

  • 事件类型和事件监听。

  • 任务类型;接受任务、服务任务、脚本任务学习

  • 任务监听和执行监听器

  • 表单管理

  • 顺序流程和网关(并行网关等)

  • 性能和扩展;ID生成和 分库分表

总结

  • activiti 工作流引擎适用场景,涉及多用户参与的流程管理。

  • activiti 工作流开发分两步 1)设计流程图 2)部署流程图 3) 发起和驱动流程实例

  • 新增流程模版,无需开发代码驱动流程和分支判定。仅需要适配前端页面和简单的后端交互类接口。

  • 最后说一句(求关注!别白嫖!)

  • 如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

  • 关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458727.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SDK5(note中)

在原有SDK5(note上)里的代码上添加了 timer的消息 LRESULT OnCreate(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {OutputDebugString(_T("[11syy]WM_CREATE\n"));//创建一个计时器SetTimer(hwnd, 1, 1000, nullptr);return TRUE; }LRESULT OnClese(HWND …

全星魅 北斗三号船载终端的优势和领域利用

QM43BS型北斗三号船载终端&#xff1a;开启航海通信与定位新时代 在当今这个信息化高速发展的时代&#xff0c;航海领域对于通信与定位技术的需求愈发迫切。深圳市全民北斗科技有限公司&#xff0c;作为北斗技术应用领域的佼佼者&#xff0c;针对数传通信和位置服务应用&#x…

Python 实现深度学习模型预测控制--预测模型构建

链接&#xff1a;深度学习模型预测控制 链接&#xff1a;WangXiaoMingo/TensorDL-MPC: DL-MPC(deep learning model predictive control) is a software toolkit developed based on the Python and TensorFlow frameworks, designed to enhance the performance of tradition…

你了解kafka消息队列么?

消息队列概述 一. 消息队列组件二. 消息队列通信模式2.1 点对点模式2.2 发布/订阅模式 三. 消息队列的优缺点3.1 消息队列的优点3.2 消息队列的缺点 四. 总结 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&…

Android Junit 单元测试 | 依赖配置和编译报错解决

问题 为什么在依赖中添加了testImplement在build APK的时候还是会报错&#xff1f;是因为没有识别到test文件夹是test源代码路径吗&#xff1f; 最常见的配置有: implementation - 所有源代码集(包括test源代码集)中都有该依赖库.testImplementation - 依赖关系仅在test源代码…

理解磁盘结构---CHS---LAB---文件系统

1&#xff0c;初步了解磁盘 机械磁盘是计算机中唯的一个机械设备&#xff0c; 特点是慢&#xff0c;容量大&#xff0c;价格便宜。 磁盘上面的光面&#xff0c;由数不清的小磁铁构成&#xff0c;我们知道磁铁是有n&#xff0f;&#xff53;极的&#xff0c;这刚好与二进制的&…

selenium脚本编写及八大元素定位方法

selenium脚本编写 上篇文章介绍了selenium环境搭建&#xff0c;搭建好之后就可以开始写代码了 基础脚本,打开一个网址 from selenium import webdriver driver webdriver.Chrome()#打开chrome浏览器 driver.get(https://www.baidu.com) #打开百度 打开本地HTML文件 上篇…

利用Kubernetes原生特性实现简单的灰度发布和蓝绿发布

部分借鉴地址: https://support.huaweicloud.com/intl/zh-cn/bestpractice-cce/cce_bestpractice_10002.html 1.原理介绍 用户通常使用无状态负载 Deployment、有状态负载 StatefulSet等Kubernetes对象来部署业务&#xff0c;每个工作负载管理一组Pod。以Deployment为例&#x…

Macos m系列芯片环境下安装python3以及mysqlclient流程以及遇到的一系列问题

最近升级了生产力&#xff0c;换了m3的mbp&#xff0c;迁移项目的时候遇到的一系列python mysqlclient的环境问题&#xff0c;这里总结记录一下。 设备&#xff1a;Macbook Pro m3系统&#xff1a;macos Sonoma 14.6最终成功的python版本&#xff1a;Python3.9.1最终系统环境下…

STL-常用容器-list

1list基本概念 **功能&#xff1a;**将数据进行链式存储 链表&#xff08;list&#xff09;是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的 链表的组成&#xff1a;链表由一系列结点组成 结点的组成&#xff1a;一个是存储…

基于django的志愿者社团管理系统

大家是不是常常遇到校园社团活动组织混乱、统计繁琐的困扰&#xff1f;其实我以前在做毕业设计的时候也头疼这些问题。后来&#xff0c;我们开发了一个基于Django的志愿者社团管理系统&#xff0c;可以帮你轻松解决这些麻烦&#xff01;&#x1f4cc; &#x1f680; 核心功能模…

Opensearch集群部署【docker、服务器、Helm多种部署方式】

操作系统兼容性 我们建议在 Red Hat Enterprise Linux (RHEL) 或使用systemd的基于 Debian 的 Linux 发行版上安装 OpenSearch &#xff0c;例如 CentOS、Amazon Linux 2 和 Ubuntu Long-Term Support (LTS)。OpenSearch 应该适用于大多数 Linux 发行版&#xff0c;但我们只测…

Java题集练习4

Java题集练习4 1 异常有什么用&#xff1f; 用来找到代码中产生的错误 防止运行出错2 异常在java中以什么形式存在&#xff1f; 异常在java中以类的形式存在&#xff0c;分为运行时异常和编译期异常&#xff0c;他们都在类Exception中3 异常是否可以自定义&#xff1f;如何自…

衡石分析平台系统分析人员手册-导入图表库图表

导入图表库图表​ 本文讲述在仪表盘中如何使用图表库图表&#xff0c;如果您还不了解图表库&#xff0c;请先点击链接了解它的功能和作用。 在数据集市中建立图表库后&#xff0c;分析人员可以在应用创作中引用图表库图表&#xff0c;快速的进行数据分析工作。 导入图表库图…

【建造&机械】木材运输车辆检测系统源码&数据集全套:改进yolo11-GhostHGNetV2

改进yolo11-SPPF-LSKA等200全套创新点大全&#xff1a;木材运输车辆检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.28 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片…

ubuntu 20.04编译驱动报gcc-12 not found错误

最近在自己安装的Ubuntu 系统上编译自定义驱动&#xff0c;发现无法编译.ko,错误如下&#xff1a; 按照如下操作&#xff0c;发现可以解决&#xff0c;记录下&#xff0c;主要是Ubuntu缺少g-12的包 安装包以后发现可以正常编译

什么是微服务中的反应性扩展?

大家好&#xff0c;我是锋哥。今天分享关于【什么是微服务中的反应性扩展&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; 什么是微服务中的反应性扩展&#xff1f; Reactive Extensions 也称为 Rx。这是一种设计方法&#xff0c;我们通过调用多个服务来收集结果…

电脑程序变化监控怎么设置?实时监控电脑程序变化的五大方法,手把手教会你!

​在现代办公和信息安全领域&#xff0c;实时监控电脑程序变化是一项至关重要的任务。 无论是企业内网安全、员工行为审计&#xff0c;还是个人电脑的隐私保护&#xff0c;了解并设置有效的监控方法都是必不可少的。 本文将详细介绍五种电脑程序变化监控的方法&#xff0c;帮助…

DEVOPS: 集群伸缩原理

概述 阿里云 K8S 集群的一个重要特性&#xff0c;是集群的节点可以动态的增加或减少有了这个特性&#xff0c;集群才能在计算资源不足的情况下扩容新的节点&#xff0c;同时也可以在资源利用 率降低的时候&#xff0c;释放节点以节省费用理解实现原理&#xff0c;在遇到问题的…

华为原生鸿蒙操作系统的发布有何重大意义和影响:

#1024程序员节 | 征文# 一、华为原生鸿蒙操作系统的发布对中国的意义可以从多个层面进行分析&#xff1a; 1. 技术自主创新 鸿蒙操作系统的推出标志着中国在操作系统领域的自主创新能力的提升。过去&#xff0c;中国在高端操作系统方面依赖于外国技术&#xff0c;鸿蒙的发布…