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

我是程序汪

共 36282字,需浏览 73分钟

 ·

2024-10-25 11:44

来源:juejin.cn/post/7401773397312782399

  • 首先画一个流程图
  • 测试流程图
  • 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);
   
   @Autowired
   private ApplyTaskListener applyTaskListener;

   @Test
   void 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

同类框架对比

img
img

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

使用流程 5板斧

  • 定义BPMN流程图, 使用建模工具设计定义流程图。
  • 部署流程, 将BPMN流程图部署到工作流引擎中。
  • 启动流程实例, 通过工作流引擎基于流程模版,启动新流程实例。
  • 执行任务, 流程执行中,引擎会为任务节点创建任务,分配给对应执行人。
  • 监听事件, 开发者可以注册监听器来捕获流程执行过程中的各种事件,例如任务完成、流程结束等。
  • 查询和监控, 工作流引擎通常提供了查询和监控功能,允许开发者和管理人员查看流程实例的状态、任务执行情况以及历史数

继续学习方向

  • 事件类型和事件监听。
  • 任务类型;接受任务、服务任务、脚本任务学习
  • 任务监听和执行监听器
  • 表单管理
  • 顺序流程和网关(并行网关等)
  • 性能和扩展;ID生成和 分库分表

总结

  • activiti 工作流引擎适用场景,涉及多用户参与的流程管理。
  • activiti 工作流开发分两步 1)设计流程图 2)部署流程图 3) 发起和驱动流程实例
  • 新增流程模版,无需开发代码驱动流程和分支判定。仅需要适配前端页面和简单的后端交互类接口。

    

程序汪接私活项目目录,2023年总结

Java项目分享  最新整理全集,找项目不累啦 07版

程序汪10万接的无线共享充电宝项目,开发周期3个月

程序汪1万接的企业官网项目,开发周期15天

程序汪8万接的共享口罩项目,开发周期1个月

程序汪8万块的饮水机物联网私活项目经验分享

程序汪接的4万智慧餐饮项目

程序汪8万接的自助洗车小程序
程序汪9万接的无人自助洗宠物机项目,开发周期40天


欢迎添加程序汪个人微信 itwang005  进粉丝群或围观朋友圈

浏览 347
2点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
2点赞
评论
收藏
分享

手机扫一扫分享

分享
举报