Activiti工作流的入门实践

686 阅读5分钟

导语

为什么我们需要用到工作流? 它能给我们带来什么?

我的个人理解如下:

  • 过去的工作流就是一种状态机,但随着业务复杂度的提升,对于状态的维护越来越难
  • 现在的工作流可以处理复杂业务场景,他就类似钉钉的审批流程,我们可以自定义审批流(在activiti工作流中称之为审批流定义,这也是activiti的开始)
  • activiti就相当于别人帮我们写好了工作流, 我们只需要调用他们提供的接口(服务)来生成审批流的数据库记录
  • 基于BPMN的画图来定义审批流非常直观

什么样的工作适合工作流

  • 分成不同阶段。
  • 有多方人员(组织)参与。
  • 重复重复再重复的流程。
  • 有流程标准化,分工明晰化,责任明确化的需求。
  • 凡是平时工作中需要走流程,而且经常会被卡住的地方,也许应该使用工作流。
  • 工作流就是责任流,每一件由用户参与的工作都意味着一份责任。
  • 工作流实现的应该是由不同人员参与的一个流程。所以工作流的环节设置就和现实中的职位设置息息相关。
  • 工作流是从上帝的视角,俯瞰整个业务流程,而每个环节的参与者看到的只是的自己的工作。
  • 工作流是用来固化一个流程的,如果职位变动频繁,说明业务模式不明朗,是不适合使用工作流的。工作流用在业务模式相对明朗的情况下来固化流程,确定责任,明晰分工。

市面上开源主流的三大工作引擎activiti, flowable, Camunda

对比项/引擎Activiti-7.xFlowable-6.xCamunda
商业化
路线(Roadmap)工具型轻量&工具型
BPMN2引擎
建模工具内容BPMN2BPMN2/CMMN/DMNBPMN2/CMMN/DMN
扩展节点(Mule\Http等)×
Spring Boot
Spring Cloud××
Web控制台
Rest接口

大致流程

  1. 定义审批流程
  2. 启动流程实例
  3. 获取未操作的任务列表 (可指定人,例如查看指向自己的审批操作列表,查看哪个xd需要请假的,我给批一下^_^)
  4. 审批操作
  5. 其他人也可以新增备注
  6. 查看该审批流的历史信息

3a374288c2f3bc5fde810a0d1d2aa2b.png

开始

IDEA安装activiti插件

7967bd1b0396f96d4736908195e9e3f.png

它可以帮助我们画审批流程,然后自动生成xml文件,这也就是之前说的审批流定义,就像下图:

7c9edb02ab5ebde7b1a519f94e753a3.png

这原理实际上就是BPMN,想要了解BPMN可以移步官网 (bpmn.io/)

pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.7.4</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.imason</groupId>
   <artifactId>activiti</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>activiti</name>
   <description>activiti</description>
   <properties>

   </properties>

   <dependencies>
      <!--数据库连接池-->
      <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.2.4</version>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-web</artifactId>
      </dependency>
      <!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring-boot-starter -->
      <dependency>
         <groupId>org.activiti</groupId>
         <artifactId>activiti-spring-boot-starter</artifactId>
         <version>7.1.0.M6</version>
      </dependency>

      <!-- 流程图-->
      <dependency>
         <groupId>org.activiti</groupId>
         <artifactId>activiti-diagram-rest</artifactId>
         <version>5.23.0</version>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>
      <!-- mysql数据库 -->
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <scope>runtime</scope>
      </dependency>
      <!-- 测试 -->
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>
</project>

bpmn的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: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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
  <process id="leaveProcess-test" name="leaveProcess-test" isExecutable="true">
    <startEvent id="sid-4c854455-30a7-4807-b98a-48a426c35e65"/>
    <userTask id="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29" name="部门经理审批" activiti:assignee="李四"/>
    <userTask id="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89" name="人事审批" activiti:assignee="王五"/>
    <sequenceFlow id="sid-d5e5c009-8d92-430e-a35d-59ae1bc1e835" sourceRef="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29" targetRef="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89"/>
    <endEvent id="sid-b80fbf85-aac6-45f2-b789-0e00e026e176"/>
    <sequenceFlow id="sid-3d52546e-bd9a-44df-8509-236a16b4e906" sourceRef="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89" targetRef="sid-b80fbf85-aac6-45f2-b789-0e00e026e176"/>
    <sequenceFlow id="sid-f5cb4f54-ef5e-4bce-819a-81abba6484d3" sourceRef="sid-4c854455-30a7-4807-b98a-48a426c35e65" targetRef="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29"/>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_demo2">
    <bpmndi:BPMNPlane bpmnElement="leaveProcess-test" id="BPMNPlane_demo2">
      <bpmndi:BPMNShape id="shape-6ee1978a-fc56-406b-9eb6-e6c4c8ab4c92" bpmnElement="sid-4c854455-30a7-4807-b98a-48a426c35e65">
        <omgdc:Bounds x="-160.0" y="-120.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="sid-fb6fa50f-beb0-468c-9994-26bfe0a0ee18" bpmnElement="sid-7d84a68f-abd0-4715-ae01-0e773aaf7d29">
        <omgdc:Bounds x="-172.5" y="-64.75" width="55.0" height="30.000002"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="sid-b2c42948-b4bc-42fd-9f67-bee07b00fe43" bpmnElement="sid-57055cb5-b5f0-4e21-83ab-5fca6160cb89">
        <omgdc:Bounds x="-172.5" y="-16.75" width="55.0" height="30.000002"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-9e36c60c-2ee5-433d-90b8-8fa9de582333" bpmnElement="sid-d5e5c009-8d92-430e-a35d-59ae1bc1e835">
        <omgdi:waypoint x="-145.0" y="-34.75"/>
        <omgdi:waypoint x="-145.0" y="-16.75"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNShape id="shape-d151c3dd-a423-4403-bc5b-0682954e6611" bpmnElement="sid-b80fbf85-aac6-45f2-b789-0e00e026e176">
        <omgdc:Bounds x="-160.0" y="35.0" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-b9e1b61b-8bb5-4f52-9665-c5a746819437" bpmnElement="sid-3d52546e-bd9a-44df-8509-236a16b4e906">
        <omgdi:waypoint x="-145.0" y="13.25"/>
        <omgdi:waypoint x="-145.0" y="35.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-e41e6c32-06a3-45a7-93c4-f8d56fd6e26b" bpmnElement="sid-f5cb4f54-ef5e-4bce-819a-81abba6484d3">
        <omgdi:waypoint x="-145.0" y="-90.0"/>
        <omgdi:waypoint x="-145.0" y="-64.75"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

在resources中新增activiti.cfg.xml文件

<?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"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                 http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/contex
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd">
         <!--数据库连接池-->
     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
       <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
       <property name="url" value="jdbc:mysql://xxx:xxx/platform_activiti?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true&amp;useSSL=false&amp;nullCatalogMeansCurrent=true" />
       <property name="username" value="xxx" />
       <property name="password" value="xxxx" />
     </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"/>
     </bean>
</beans>

Service总览

Service接口说明
RepositoryServiceActiviti的资源管理接口
RuntimeServiceActiviti的流程运行管理接口
TaskServiceActiviti的任务管理接口
HistoryServiceActiviti的历史管理接口
ManagementServiceActiviti的引擎管理接口
  • RepositoryService,是Activiti的资源管理接口,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此Service将流程定义文件的内容部署到计算机中。
  • RuntimeService,是Activiti的流程运行管理接口,可以从这个接口中获取很多关于流程执行相关的信息。
  • TaskService,是Activiti的任务管理接口,可以从这个接口中获取任务的信息。
  • HistoryService,是Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会包含很多数据(根据配置),比如流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径,等等。
  • ManagementService,是Activiti的引擎管理接口,提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti系统的日常维护。

部署审批流程 (定义流程就是画图的那部分)

@Test
public void testDeploy(){
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    //获取RepositoryService对象
    RepositoryService repositoryService = processEngine.getRepositoryService();
    //进行部署
    Deployment deployment = repositoryService.createDeployment()
         .addClasspathResource("processes/leave.bpmn20.xml")
         .addClasspathResource("processes/leave.png")
         .name("请假流程")
         .deploy();
   
    System.out.println("流程部署ID:" + deployment.getId());
    System.out.println("流程部署名称:" + deployment.getName());
}

启动流程实例

@Test
public void testStartProcess(){
    //创建ProcessEngine对象
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    System.out.println(processEngine);
    RuntimeService runtimeService = processEngine.getRuntimeService();
    //根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的
    ProcessInstance instance = runtimeService.
            startProcessInstanceByKey("leaveProcess-test");
    //获取流程实例的相关信息
    System.out.println("流程定义的id = " + instance.getProcessDefinitionId());
    System.out.println("流程实例的id = " + instance.getId());
}

查看未操作的任务列表(领导: 谁给我发了请假审批,我瞅瞅)

@Test
public void testSelectTodoTaskList(){
  //任务负责人
  String assignee = "李四";
  //创建ProcessEngine对象
  ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  //获取TaskService
  TaskService taskService = processEngine.getTaskService();
  //获取任务集合
  List<Task> taskList = taskService.createTaskQuery()
       .processDefinitionKey("leaveProcess-test")
       .taskAssignee(assignee)
       .list();
  //遍历任务列表
  for(Task task:taskList){
    System.out.println("流程定义id = " + task.getProcessDefinitionId());
    System.out.println("流程实例id = " + task.getProcessInstanceId());
    System.out.println("任务id = " + task.getId());
    System.out.println("任务名称 = " + task.getName());
  }
}

领导审批 (李四:部门经理)

//任务负责人
String assignee = "李四";
//创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//获取TaskService
TaskService taskService = processEngine.getTaskService();
//获取任务集合
List<Task> taskList = taskService.createTaskQuery()
     .processDefinitionKey("leaveProcess-test")
     .taskAssignee(assignee)
     .list();
//遍历任务列表
for(Task task:taskList){
  taskService.complete(task.getId());
}

新增备注(王五: 人事 人事:这也没个备注啥的,我补充下)

@Test
public void testAddComment(){
  //任务负责人
  String assignee = "王五";
  //创建ProcessEngine对象
  ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  //获取TaskService
  TaskService taskService = processEngine.getTaskService();
  //获取任务集合
  List<Task> taskList = taskService.createTaskQuery()
       .processDefinitionKey("leaveProcess-test")
       .taskAssignee(assignee)
       .list();
  //遍历任务列表
  for(Task task:taskList){
    //在任务执行之前任务添加批注信息
    taskService.addComment(task.getId(),task.getProcessInstanceId(),task.getName()+"审批通过");
    taskService.complete(task.getId());
  }
}

查看审批的历史 (我:发起人 我:审核通过没?我还着急回老家呢,看看去)

@Test
public void testSelectHistoryTask(){
      //流程实例ID
      String processInstanceId = "17501";
      //任务审核人
      String taskAssignee = "王五";
      //创建ProcessEngine对象
      ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
      //获取historyService
      HistoryService historyService = processEngine.getHistoryService();
      //获取taskService
      TaskService taskService = processEngine.getTaskService();
      //获取历史审核信息
      List<HistoricActivityInstance> list = historyService
           .createHistoricActivityInstanceQuery()
//           .activityType("userTask")//只获取用户任务
           .processInstanceId(processInstanceId)
           .taskAssignee(taskAssignee)
//           .finished()
           .list();
      for(HistoricActivityInstance instance: list){
        System.out.println("任务名称:"+instance.getActivityName());
        System.out.println("任务开始时间:"+instance.getStartTime());
        System.out.println("任务结束时间:"+instance.getEndTime());
        System.out.println("任务耗时:"+instance.getDurationInMillis());
        //获取审核批注信息
        List<Comment> taskComments = taskService.getTaskComments(instance.getTaskId());
        if(taskComments.size()>0){
          //在控制台输出
          System.out.println("审批批注:"+taskComments.get(0).getFullMessage());
         }
       }
    }

结语

参考了以下文章做出的自己的总结, 参考地址: juejin.cn/post/710653…