Spring Boot + Activiti 完美结合,快速实现工作流~

news2025/8/8 19:56:52

概念

工作流。通过计算机对业务流程自动化执行管理,主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。

Activiti7

介绍

Activiti是一个工作流引擎,Activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由Activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

在使用activiti之前,首先需要编写activiti.cfg.xml配置文件。并且引入相关依赖。

<dependencies>
    <!--activiti的核心包-->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-engine</artifactId>
        <version>6.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring</artifactId>
        <version>6.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-model</artifactId>
        <version>6.0.0</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-converter</artifactId>
        <version>6.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-json-converter</artifactId>
        <version>6.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-layout</artifactId>
        <version>6.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.activiti.cloud</groupId>
        <artifactId>activiti-cloud-services-api</artifactId>
        <version>7-201710-EA</version>
    </dependency>
    <dependency>
        <groupId>aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.5.4</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.40</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.1.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.21</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

activiti.cfg.xml

activiti的引擎配置文件,包括:ProcessEngineConfiguration的定义、数据源定义、事务管理器等。其实就是一个Spring配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--dbcp连接池-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="maxActive" value="3"/>
        <property name="maxIdle" value="1"/>
    </bean>

    <!--在默认方式下,bean的id固定为processEngineConfiguration-->
    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!--配置数据库相关信息-->
        <property name="dataSource" ref="dataSource"/>
        <!--
            activiti数据库表处理策略
                false(默认值):检查数据库的版本和依赖库的版本,如果不匹配就抛出异常
                true:构建流程引擎时,执行检查,如果需要就执行更新。如果表不存在,就创建。
                create-drop:构建流程引擎时创建数据库报表,关闭流程引擎时就删除这些表。
                drop-create:先删除表再创建表。
                create:构建流程引擎时创建数据库表,关闭流程引擎时不删除这些表
        -->
        <property name="databaseSchemaUpdate" value="true"/>
        <property name="asyncExecutorActivate" value="false"/>
        <property name="mailServerHost" value="mail.my-corp.com"/>
        <property name="mailServerPort" value="5025"/>
    </bean>

</beans>

Activiti流程框架,在前期主要需要了解的就是数据库表的创建、流程的部署、流程的启动和各个阶段任务的完成。

流程引擎配置类

流程引擎配置类(ProcessEngineConfiguration),通过ProcessEngineConfiguration 可以创建工作流引擎 ProceccEngine。

工作流引擎的创建

工作流引擎的创建主要有两种方式:默认创建方式和一般创建方式

默认创建方式

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);

一般创建方式

//使用自定义方式创建
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
//获取流程引擎对象:通过 ProcessEngineConfiguration 创建 ProcessEngine,此时会创建数据库
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();

当创建好工作流引擎后,对应的数据库中会自动生成25张数据库表。

ACT_GE_PROPERTY中会先展示下一次流程的ID(next.dbid),并且在下一次流程部署的时候,对下一次流程的ID进行赋值。

Activiti表说明

这里以表名的前缀进行说明:

Service服务接口

Activiti中还有许多的Service服务接口。这些Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们可以使用这些接口操作服务对应的数据表。

Service创建方式

通过ProcessEngine创建Service方式:

Runtimeservice runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();

Service总览

  • RepositoryService

Activiti 的资源管理类,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此service将流程定义文件的内容部署到计算机。除了部署流程定义以外,还可以查询引擎中的发布包和流程定义。

暂停或激活发布包,对应全部和特定流程定义。暂停意味着它们不能再执行任何操作了,激活是对应的反向操作。获得多种资源,像是包含在发布包里的文件,或引擎自动生成的流程图。获得流程定义的pojo版本,可以用来通过java解析流程,而不必通过xml。

  • Runtimeservice

Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息

  • Taskservice

Activiti的任务管理类。可以从这个类中获取任务的信息。

  • Historyservice

Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径,等等。这个服务主要通过查询功能来获得这些数据。

  • ManagementService

Activiti的引擎管理类,提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti 系统的日常维护。

流程图符号说明

BPMN插件

使用IDEA进行开发,建议下载一个插件。actiBPM插件,直接搜索下载。

流程符号、画流程图

流程符号:事件Event,活动Activity,网关Gateway,流向

使用流程设计器画出流程图

  • 创建bpmn文件,在流程设计器使用流程符号来表达流程,指定流程的key,指定任务负责人

  • 生成png文件

  • 创建的bpmn文件要放在resourse下的bpmn文件夹下。

注意:当前任务流程的ID不能是数字开头。

找到本地的文件,选择notepad打开

<?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="m1611283406582" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="myEvection" isClosed="false" isExecutable="true" name="出差申请" processType="None">
    <startEvent id="_2" name="StartEvent"/>
    <userTask activiti:assignee="zhangsan" activiti:exclusive="true" id="_3" name="创建出差申请"/>
    <userTask activiti:assignee="jerry" activiti:exclusive="true" id="_4" name="经理审批"/>
    <userTask activiti:assignee="jack" activiti:exclusive="true" id="_5" name="总经理审批"/>
    <userTask activiti:assignee="rose" activiti:exclusive="true" id="_6" name="财务审批"/>
    <endEvent id="_7" name="EndEvent"/>
    <sequenceFlow id="_8" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_9" sourceRef="_3" targetRef="_4"/>
    <sequenceFlow id="_10" sourceRef="_4" targetRef="_5"/>
    <sequenceFlow id="_11" sourceRef="_5" targetRef="_6"/>
    <sequenceFlow id="_12" sourceRef="_6" targetRef="_7"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#FFFFFF;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="myEvection">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <omgdc:Bounds height="32.0" width="32.0" x="185.0" y="0.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <omgdc:Bounds height="55.0" width="85.0" x="160.0" y="85.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <omgdc:Bounds height="55.0" width="85.0" x="160.0" y="185.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="55.0" width="85.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="160.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="160.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">
        <omgdc:Bounds height="32.0" width="32.0" x="185.0" y="475.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_7">
        <omgdi:waypoint x="201.0" y="445.0"/>
        <omgdi:waypoint x="201.0" y="475.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_2" targetElement="_3">
        <omgdi:waypoint x="201.0" y="32.0"/>
        <omgdi:waypoint x="201.0" y="85.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_3" targetElement="_4">
        <omgdi:waypoint x="202.5" y="140.0"/>
        <omgdi:waypoint x="202.5" y="185.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_5" targetElement="_6">
        <omgdi:waypoint x="202.5" y="340.0"/>
        <omgdi:waypoint x="202.5" y="390.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_4" targetElement="_5">
        <omgdi:waypoint x="202.5" y="240.0"/>
        <omgdi:waypoint x="202.5" y="285.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

流程的操作

部署流程

使用 Activiti 提供的 API 把流程图的内容写入到数据库中

属于资源操作类,使用 RepositoryService

  • 单文件部署:把bpmn文件和png文件逐个处理

  • 压缩包部署:把bpmn文件和png文件打成压缩包来处理

  • 部署操作表:act_re_deploymentact_re_procdefact_ge_bytearray

/
 * 流程部署
 */
public void deployment() {
    // 创建 ProcessEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 使用 service 进行流程的部署,定义一个流程的名字,把bpmn和png部署到数据中
    Deployment deployment = repositoryService.createDeployment()
            .name("出差申请流程") //流程图标的名字
            .addClasspathResource("bpmn/evection.bpmn") //bpmn文件
            .addClasspathResource("bpmn/evection.png") //bpmn文件生成的
            .deploy();
    // 输出部署信息
    System.out.println("流程部署ID:" + deployment.getId());
    System.out.println("流程部署名字:" + deployment.getName());
}

有时候我们会有多个流程,需要创建多个bpmn流程文件,这个时候想要同时部署,我们可以对bpmn文件进行打包压缩,使用Zip包进行批量的部署

/
 * 使用Zip包进行批量的部署
 */
@Test
public void deployProcessByZip() {
    // 获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 流程部署
    // 读取资源包文件,构造成 InputStream
    InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");
    // 使用 InputStream 构造 ZipInputStream
    ZipInputStream zipInputStream = new ZipInputStream(inputStream);
    // 使用压缩包的流,进行流程的部署
    Deployment deploy = repositoryService.createDeployment()
            .addZipInputStream(zipInputStream)
            .deploy();
    // 输出
    System.out.println("流程部署的ID:" + deploy.getId());
    System.out.println("流程部署的名称:" + deploy.getName());
}

操作的数据库表:

  • act_ge_bytearray

  • act_ge_property

  • act_re_deployment

  • act_re_procdef

启动流程实例

流程部署完成以后,需要启动流程实例。使用 RuntimeService 根据流程定义的 key进行启动。

核心代码:

/
 * 启动流程
 */
public void starProcess() {
    // 创建 ProcessEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取 RunTimeService
    RuntimeService runtimeService = processEngine.getRuntimeService();
    // 根据流程定义的ID启动流程
    ProcessInstance instance = runtimeService.startProcessInstanceByKey("myEvection");
    // 输出内容
    System.out.println("流程定义ID:" + instance.getProcessDefinitionId());
    System.out.println("流程实例的ID:" + instance.getId());
    System.out.println("当前活动的ID:" + instance.getActivityId());
}

任务查询

使用 TaskService ,根据流程定义的 key ,任务负责人来进行查询

核心代码:

/
 * 查询个人待执行的任务
 */
@Test
public void findPersonalTaskList() {
    // 获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 获取TaskService
    TaskService taskService = processEngine.getTaskService();
    // 根据流程的key和任务的负责人去查询任务
    List<Task> taskList = taskService.createTaskQuery()
            .processDefinitionKey("myEvection")  // 流程的key
            .includeProcessVariables()
            .taskAssignee("zhangsan")           // 要查询的负责人
            .list();
    // 输出
    for (Task task : taskList) {
        System.out.println("流程实例的ID:" + task.getProcessInstanceId());
        System.out.println("任务的ID:" + task.getId());
        System.out.println("任务的负责人:" + task.getAssignee());
        System.out.println("任务的名称:" + task.getName());
    }
}

任务完成

使用 TaskService ,用任务 ID 直接完成任务。

核心代码:

/
 * 完成个人任务
 */
@Test
public void completTask() {
    String key = "testCandidiate";
    String assignee = "张三1"; //任务的负责人
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = processEngine.getTaskService();
    Task task = taskService.createTaskQuery()
            .processDefinitionKey(key)
            .taskAssignee(assignee)
            .singleResult();
    if (task != null) {
        taskService.complete(task.getId());
    }
}

关于流程实例的挂起和激活

全部流程实例的挂起和激活

/
 * 全部流程实例的挂起和激活
 */
@Test
public void suspendAllProcessInstance() {
    // 1.获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.获取 RepositoryService
    RepositoryService repositoryService = processEngine.getRepositoryService();
    // 3.查询流程定义
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey("myEvection")
            .singleResult();
    // 4.获取当前流程定义的实例是否都是挂起状态
    boolean flag = processDefinition.isSuspended();
    // 5.获取流程定义的ID
    String id = processDefinition.getId();
    // 6.判断是否挂起状态。是:改为激活;否:改为挂起
    if (flag) {
        // 改为激活. 参数1:流程定义的ID,参数2:是否激活,参数3:激活时间
        repositoryService.activateProcessDefinitionById(id, true, null);
        System.out.println("流程定义ID:" + id + "已激活");
    } else {
        // 改为挂起. 参数1:流程定义的ID;参数2:是否挂起;参数3:挂起时间
        repositoryService.suspendProcessDefinitionById(id, true, null);
        System.out.println("流程定义ID:" + id + "已挂起");
    }
}

单个流程实例的挂起和激活

/
 * 单个流程实例的挂起和激活
 */
@Test
public void suspendSingleProcessInstance() {
    // 1.获取流程引擎
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // 2.获取 RuntimeService
    RuntimeService runtimeService = processEngine.getRuntimeService();
    // 3.通过 RuntimeService 获取流程实例对象
    ProcessInstance instance = runtimeService.createProcessInstanceQuery()
            .processInstanceId("17501")
            .singleResult();
    // 4.得到当前流程实例的暂停状态
    boolean flag = instance.isSuspended();
    // 5.获取流程实例的ID
    String instanceId = instance.getId();
    // 6.判断是否暂停。是:改为激活;否:改为暂停
    if (flag) {
        runtimeService.activateProcessInstanceById(instanceId);
        System.out.println("流程实例ID:" + instanceId + "已激活");
    } else {
        runtimeService.suspendProcessInstanceById(instanceId);
        System.out.println("流程实例ID:" + instanceId + "已暂停");
    }
}

注意:流程实例在挂起的状态下是无法进行下一步操作的。

流程变量

我们在使用流程变量的时候。如果我们将一个对象存储到一个流程变量中,那么这个对象需要实现Serializable接口。

/
 * 出差申请中的流程变量对象
 */
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Evection implements Serializable {

    private Long id;                //主键ID
    private Integer days;            //出差天数
    private String evectionName;    //出差单名字
    private Date startTime;         //出差开始时间
    private Date endTime;           //出差结束时间
    private String address;         //目的地
    private String reason;          //出差原因

}

流程变量的作用域

  • 整个流程实例、任务、执行实例。

  • 默认:整个流程实例。

使用方法

在属性上使用UEL表达式 ${assignee}assignee就是一个流程变量的名称。

在连线上使用UEL表达式 ${days<=3},days就是一个流程变量名称,返回结果为true或者false。

Activiti有很多种方式设置流程变量,这里简单介绍两种:

启动流程时设置流程变量

/
 * 启动流程
 */
@Test
public void startProcess() {
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = processEngine.getRuntimeService();
    // 流程变量map
    Map<String, Object> map = new HashMap<>();
    // 设置流程变量
    Evection evection = new Evection();
    evection.setDays(2);
    // 把流程变量的pojo放入map
    map.put("evection", evection);
    map.put("assignee0", "张三");
    map.put("assignee1", "李经理");
    map.put("assignee2", "王财务");
    map.put("assignee3", "赵总经理");
    runtimeService.startProcessInstanceByKey("myProcess_1", map);
}

任务办理时设置

/
 * 完成任务
 */
@Test
public void completTask() {
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = processEngine.getTaskService();
    Evection evection = new Evection();
    evection.setDays(2);
    Map<String, Object> map = new HashMap<>();
    map.put("evection", evection);
    Task task = taskService.createTaskQuery()
            .processDefinitionKey("myProcess_2")
            .taskAssignee("王财务0")
            .singleResult();
    if (task != null) {
        String taskId = task.getId();
        // 完成任务
        taskService.complete(taskId, map);
    }
}

网关

用来控制流程的走向

排他网关——ExclusiveGateway

用来在流程中实现决策,当流程执行到这个网关,所有的分支都会判断条件是否为true,如果为true,则执行该分支。

注意:排他网关只会选择一个作为true的分支执行,如果有两个分支都为true,排他网关会选择ID值比较小的一条分支去执行。

如果从排他网关出去的流程所有的条件都不满足,则会抛出异常。

并行网关——ParallelGateway

并行网关,允许流程分成多条分支,也可以把多分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:

  • fork分支:并行后的所有外出顺序流,为每个顺序流都创建一个并发分支

  • join汇聚:所有到达并行网关,在此等待的分支,直到所有进入顺序流的分支都到达以后,流程就会通过汇聚网关。

注意:如果同一个并行网关有多个进入和多个外出顺序流,它就同时具有分支和汇聚功能,这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。

与其他网关的主要区别是:并行网关不会解析条件,即使顺序流中定义了条件,也会被忽略。

并行网关需要所有分支的全部运行完了,才会汇聚,继续向下执行。

包含网关——InclusiveGateway

包含网关可以看成是排他网关和并行网关的结合体,和排他网关一样,可以在外出顺序流上定义条件,包含网关会解析它们,但是主要的区别是:包含网关可以选择多于一条顺序流,这和并行网关一样。

包含网关的功能是基于进入和外出顺序流的。

  • 分支:所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行,会为每一个顺序流创建一个分支。

  • 汇聚:所有并行分支到达包含网关,会进入等待状态,直到每个包含流程token的进入顺序流的分支都到达。这是和并行网关最大的不同。

事件网关——EventGateway

Activiti和Spring的整合开发

配置文件:

<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
         http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--工作流引擎配置对象-->
    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <!--数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--使用Spring的事务管理器-->
        <property name="transactionManager" ref="transactionManager"/>
        <!--
        数据库策略:
            false:默认值。activiti在启动时,会对比数据库表中保存的版本。如果没有表或者版本不匹配,将抛出     异常。
            true:activiti会对数据库中所有表进行更新操作,如果表不存在,则会自动创建。
            create_drop:在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。
            drop-create:在activiti启动时删除原来的旧表,然后再创建新表(不需要手动关闭引擎)。
        -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>

    <!--配置数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/actspring"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="maxActive" value="3"/>
        <property name="maxIdle" value="1"/>
    </bean>

    <!-- 流程引擎对象 -->
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
    </bean>

    <!--资源服务-->
    <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService"/>
    <!--流程管理-->
    <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService"/>
    <!--任务管理-->
    <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService"/>
    <!--历史管理-->
    <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/>

    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--传播行为-->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>
</beans>

Activiti和SpringBoot的整合开发

配置文件:

spring:
  application:
    name: actspringboot
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/actspring?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
    username: root
    password: root
  activiti:
    # false:默认值。activiti在启动时,会对比数据库表中保存的版本。如果没有表或者版本不匹配,将抛出异常
    # true:activiti会对数据库中所有表进行更新操作,如果表不存在,则会自动创建
    # create_drop:在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
    # drop-create:在activiti启动时删除原来的旧表,然后再创建新表(不需要手动关闭引擎)
    # 线上一般使用false,开发中使用true
    database-schema-update: true
    # 自动部署验证设置:true-开启(默认)、false-关闭
    check-process-definitions: false
    # 开启历史表
    db-history-used: true
    # 历史记录存储等级
    history-level: full
server:
  port: 8082

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数百个模型放在面前,金融机构要如何高效管理

疫情推动金融机构加速数字化转型。依托人工智能、大数据等技术&#xff0c;金融机构建立各类模型&#xff0c;特别是以机器学习为代表的数据模型&#xff0c;被广泛运用在风险计量、客户准入、资本计量、拨备计提、客户管理、反洗钱、反欺诈、精准营销等领域。在用户行为模式改…

11.24总结二叉树

目录 一.将二叉搜索树变成有序链表 二.从前序遍历和中序遍历构建二叉树 三.从中序遍历和后续遍历创建字符串 四.二叉树创立字符串 五.订正题目 六.排序子序列 七.二叉树非递归遍历 1.前序遍历 3.后续遍历 一.将二叉搜索树变成有序链表 我们的思路就是因为一颗二叉搜索…

如何通过短链接跳转到小程序,或者跳转至小程序webview一个h5页面

theme: channing-cyan 一、需求背景&#xff1a; 公司需要通过发送短信携带短链接&#xff0c;用户点击短链接跳转到小程序的某个页面&#xff0c;然后打开小程序该页面webviewh5页面&#xff0c;然后链接携带参数。 使用技术&#xff1a; 主要是用小程序的云开发&#x…

HashMap为什么会发生死循环?

Java的HashMap是线程不安全的&#xff0c;所以在jdk1.7中&#xff0c;多线程的HashMap扩容采用头插法会发生死循环问题。为什么会发生这种情况呢&#xff1f; 正常扩容 当我们向HashMap中添加值的时候&#xff0c;调用的是Put()方法。 public V put(K key, V value) {//如果…

使用Prometheus监控docker compose方式部署的ES

需求 收集 ES 的指标, 并进行展示和告警; 现状 ES 通过 docker compose 安装所在环境的 K8S 集群有 Prometheus 和 AlertManager 及 Grafana 方案 复用现有的监控体系, 通过: Prometheus 监控 ES. 具体实现为: 采集端 elasticsearch_exporter 可以监控的指标为: NameTy…

使用 TensorFlow 构建计算机视觉模型

什么是计算机视觉&#xff1f; 计算机视觉 (CV) 是现代人工智能 (AI) 和机器学习 (ML) 系统的主要任务。它正在加速行业中的几乎每个领域&#xff0c;使组织能够彻底改变机器和业务系统的工作方式。 在学术上&#xff0c;它是计算机科学的一个成熟领域&#xff0c;数十年的研…

异常(Exception)

随着面向对象的结束&#xff0c;我们的JavaSE也就接近了尾声&#xff0c;还有两个章节没有去梳理&#xff0c;常用类和异常&#xff0c;本章先讲异常&#xff0c;剩下的常用类后面再来补。 废话不多说&#xff0c;直接开始本章的内容。 1. 认识异常 引出&#xff1a; 假设 n…

数据结构与算法_二叉树(BST树)_面试题总结

这篇笔记记录二叉树相关的常考题。 1 BST树区间元素搜索问题 **解决方法&#xff1a;**利用BST树的中序遍历&#xff0c;中序遍历后输出的是从小到大的顺序。 // 求满足区间的元素值 [i,j];void findValues(vector<T> &vec, int i, int j){// 封装一个递归接口 fin…

精华推荐 | 【深入浅出RocketMQ原理及实战】「性能原理挖掘系列」透彻剖析贯穿RocketMQ的系统服务底层原理以及高性能存储设计挖掘深入

设计背景 消息中间件的本身定义来考虑&#xff0c;应该尽量减少对于外部第三方中间件的依赖。一般来说依赖的外部系统越多&#xff0c;也会使得本身的设计越复杂&#xff0c;采用文件系统作为消息存储的方式。 RocketMQ存储机制 消息中间件的存储一般都是利用磁盘&#xff0…

基于node.js的学生管理系统设计

目 录 摘 要 I Abstract II 第1章 绪论 1 1.1选题背景和意义 1 1.1.1选题背景 1 1.1.2选题意义 1 1.2国内外研究现状、发展动态 2 1.2.1国内研究现状 2 1.2.2国外研究现状 3 1.2.3发展动态 3 1.3研究内容 4 第2章 Node.js软件说明 5 2.1 Node.js概述 5 2.2 Node.js的模块 6 2.3…

语义信息概述

语义信息概述 什么叫语义信息 无论在图像&#xff0c;文本&#xff0c;语音处理领域等&#xff0c;我们常看到一个词&#xff0c;“语义信息”。&#xff08;有意义的数据提供的信息&#xff09; 维基百科中的解释&#xff1a; 语义信息&#xff08;英语&#xff1a;semantic…

【基础算法Ⅰ】算法入门篇

目录 进入算法世界 1.输入输出 1.1输入输出 1.2快读 2.位运算 2.1运算符 2.2位运算 3.枚举 3.1枚举的引入 3.2枚举的简单理解 3.3枚举简介 3.4 枚举算法实例 算法复杂度 时间复杂度 进入算法世界 瑞士著名的科学家Niklaus Wirth教授曾提出&#xff1a;数据结构算…

在C#方法中 out、ref、in、params 关键字的小结

out&#xff1a;关键字&#xff1a; 指定的参数在进入函数时会清空自己&#xff0c;必须在函数内部赋初值 ref关键字&#xff1a; 指定的参数必须在进入函数时赋初值&#xff0c;在函数内部可以重新赋值 In关键字&#xff1a; 指定的参数必须在进入函数时赋初值&#xff0c;…

C++入门教程||C++while循环

whlie 语法 C 中 while 循环的语法&#xff1a; while(condition) {statement(s); } 在这里&#xff0c;statement(s) 可以是一个单独的语句&#xff0c;也可以是几个语句组成的代码块。condition 可以是任意的表达式&#xff0c;当为任意非零值时都为真。当条件为真时执行…

Java.md

sa一、基础篇 网络基础 TCP三次握手 1、OSI与TCP/IP 模型2、常见网络服务分层3、TCP与UDP区别及场景4、TCP滑动窗口&#xff0c;拥塞控制5、TCP粘包原因和解决方法6、TCP、UDP报文格式 HTTP协议 1、HTTP协议1.0_1.1_2.02、HTTP与HTTPS之间的区别3、Get和Post请求区别4、HTTP常见…

Python实现BP神经网络ANN单隐层回归模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 20世纪80年代中期&#xff0c;David Runelhart。Geoffrey Hinton和Ronald W-llians、DavidParker等人分…

SpringCloud 组件Gateway服务网关【gateway快速入门】

目录 1&#xff1a;Gateway服务网关 1.1&#xff1a;为什么需要网关 1.2&#xff1a;gateway快速入门 1&#xff09;&#xff1a;创建gateway服务&#xff0c;引入依赖 2&#xff09;&#xff1a;编写启动类 3&#xff09;&#xff1a;编写基础配置和路由规则 4&#xf…

啥牌子的无线蓝牙耳机好用?无线蓝牙耳机推荐2022

蓝牙耳机这几年技术越好越高&#xff0c;其最大的魅力就是随时随地听音乐&#xff0c;无论是上下班还是日常使用&#xff0c;出门携带也方便&#xff0c;市面上的蓝牙耳机众多&#xff0c;很多人不知道该如何选择&#xff0c;下面整理了几款音质清晰&#xff0c;综合性能优秀的…

分享25个JSP源码,总有一款适合您

链接&#xff1a;https://pan.baidu.com/s/17ug7A_b2nHgu-x1K-GIVlQ?pwd6367 提取码&#xff1a;6367 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载后可以看到。 renren-security轻量级权限管理系统 renr…

Linux C应用编程-1-文件IO

1.open与close #include <stdio.h> //IO操作需要包含的头文件 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h>char filename[] "text.txt";int main(void) {int fd;fd open(filename, O_RD…