Activiti工作流

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: Activiti工作流

分享一下手把手教你如何玩转Activiti工作流


还未完善


场景:学校    


主角:阿毛  ,   班主任   ,教务处处长


问题:有一天,阿毛到学校,感觉到身体不舒服,然后想跟班主任请假,然后班主任告诉阿毛说,你想请假,那么就必须要请假条,这个上面必须要我同意,然后再拿到教务处去盖章,然后交给我,这样才可以进行请假。。阿毛,想着,怎么请个假都这么麻烦,这么多层次处理问题,能不能简便一点。。。。好烦好烦~!!~~


分析:从上面的小例子,我们可以很明显的得到一个结论,就是:


请假流程:阿毛------》提交申请-----》班主任审批-----》教务处审批-----》请假成功


也就是说,这种问题就是一种流式的控制管理,当然,这是最简单的,因为里面没有包含着回馈,相当于只是一个方向。其实,到这里,Activiti的定义就已经出来了。。。。。


大纲:


20181225181003343.png

20181225181036500.png

20181225181108928.png

之前做过一些项目没有用到工作流,都是以状态控制整个流程,现在系统化的将工作流分享一下



20181225181213862.png


20181225181324532.png



20181225181336748.png


20181225181433853.png


20181225181450126.png


20181225181529562.png

20181225181640343.png

20181225181749968.png

20181225181846890.png

使用activiti-designer-5.18.0.zip


2.创建SpringBoot工程


①编辑POM文件

<!-- 继承SpringBoot官方指定的父工程 -->
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.12.RELEASE</version>
</parent>
<dependencies>
  <!-- MySQL驱动 -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
  </dependency>
  <!-- 数据库连接池 -->
  <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.5</version>
  </dependency>
  <!-- MyBatis场景启动器 -->
  <dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>1.1.1</version>
  </dependency>
  <!-- Activiti场景启动器 -->
  <dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter-basic</artifactId>
    <version>5.21.0</version>
  </dependency>
  <!-- 基本场景启动器 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
  </dependency>
  <!-- SpringBoot测试支持 -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
  </dependency>
</dependencies>


特别说明:请把mybatis-spring-boot-starter的依赖放在activiti-spring-boot-starter-basic的前面以避免jar包冲突问题。jar包冲突的表现是:java.lang.ClassNotFoundException: org.apache.ibatis.annotations.Mapper。


依赖顺序和依赖传递的关系: mybatis-spring-boot-starter依赖mybatis-3.4.0.jar,而activiti-spring-boot-starter-basic依赖mybatis-3.3.0.jar,对于这种情况,Maven以顺序靠前的为准。现在如果导入的是mybatis-3.3.0.jar就会抛出异常,而mybatis-3.4.0.jar是正确的,所以要让mybatis-spring-boot-starter的依赖放在activiti-spring-boot-starter-basic的前面。


②主启动类

@SpringBootApplication
public class MainApplication {
  public static void main(String[] args) {
    SpringApplication.run(MainApplication.class, args);
  }
}


③测试类

@RunWith(SpringRunner.class)
@SpringBootTest
public class ProcessTest {
  @Autowired
  private ProcessEngine processEngine;
  @Test
  public void createTable() {
    System.out.println(processEngine);
  }
}


④yml配置

spring:
  datasource:
    name: mydb
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://127.0.0.1:3306/db_activiti
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver


⑤特别操作


在src/main/java目录下创建processes目录,否则将抛出异常:java.io.FileNotFoundException: class path resource [processes/] cannot be resolved to URL because it does not exist。processes目录的作用是存放bpmn流程定义文件,并对bpmn文件进行自动部署。如果创建到src/main/resources目录下则要求processes目录下至少存在一个文件(任意)。


三、流程操作


1.创建第一个流程图


20181225183100632.png

20181225183220183.png

20181225184157490.png


2.流程部署


①Java代码


注意:如果放在processes目录下会被自动部署,下面的Java代码是手动部署。

@RunWith(SpringRunner.class)
@SpringBootTest
public class ProcessTest {
    @Autowired
    private RepositoryService repositoryService;
    @Test
    public void test01ProcessDefinitionDeployment() {
        //部署:将流程定义文件中流程定义信息存入数据库
        repositoryService
            .createDeployment()     //创建部署构建器对象
            .addClasspathResource("MyProcess.bpmn")   //添加要部署的流程定义文件名
            .deploy();      //执行部署
    }
}


②数据库表分析


[1]act_ge_bytearray表


二进制数据表,存储了流程定义图形的XML文件和图片信息

保存流程定义的xml信息

保存流程定义的图片


[2]act_re_deployment表


部署信息表,存储了部署的相关信息(部署时间)


[3]act_re_procdef表


流程定义数据表,存储了当前流程图形的相关信息(id,name,版本号)


③解决乱码问题

import org.activiti.spring.SpringProcessEngineConfiguration;
import org.activiti.spring.boot.ProcessEngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ActivitiConfig implements ProcessEngineConfigurationConfigurer {
    @Override
    public void configure(SpringProcessEngineConfiguration processEngineConfiguration) {
        processEngineConfiguration.setActivityFontName("宋体");
        processEngineConfiguration.setLabelFontName("宋体");
    }
}


3.流程查询


①第一步:创建ProcessDefinitionQuery对象


    @Test
    public void test02ProcessDefinitionQuery() {
        //1.创建流程定义查询对象
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        //2.调用对应的方法执行查询
        List<ProcessDefinition> list = query.list();
        //3.遍历集合
        for (ProcessDefinition processDefinition : list) {
            String key = processDefinition.getKey();
            String name = processDefinition.getName();
            int version = processDefinition.getVersion();
            System.err.println("key="+key);
            System.err.println("name="+name);
            System.err.println("version="+version);
        }
    }


20181225185451290.png


20181225190009365.png

20181225190216627.png


注意2:创建了新的流程定义需要再次执行部署,再次启动流程。


6.查询任务和7.完成任务


@Test
    public void test03TaskQuery() {
        //创建任务查询对象
        TaskQuery query = taskService.createTaskQuery();
        //根据流程定义key和委托人查询待办任务
        List<Task> list = query.processDefinitionKey("MyProcess").taskAssignee("zuzu").list();
        for (Task task : list) {
            String assignee = task.getAssignee();
            String taskId = task.getId();
            String taskName = task.getName();
            //taskService需要使用@Autowired注解装配
            taskService.complete(task.getId());//完成任务,使任务进入下一步
            System.err.println("assignee="+assignee);
            System.err.println("taskId="+taskId);
            System.err.println("taskName="+taskName);
        }
    }


8.查询流程实例历史


//1.创建流程实例的历史查询对象,historyService使用@Autowired注解注入
HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery();
HistoricProcessInstance instance = 
    query.processInstanceId("7501") //流程实例的id
      .finished()//调用这个方法后未完成的流程实例会返回null
      .singleResult();
System.out.println(instance);


9.将任务指派给一个组


20181225194553490.png

注意1:创建的新的流程定义要指定一个新的id。

注意2:创建了新的流程定义需要再次执行部署,再次启动流程。

20181225195304188.png


10.领取任务


    @Test
    public void test04Claim() {
        //1.创建任务查询对象
        TaskQuery taskQuery = taskService.createTaskQuery();
        //2.根据任务委托组查询任务列表
        List<Task> list = taskQuery.taskCandidateGroup("jingli").list();
        //3.遍历
        for (Task task : list) {
            String taskId = task.getId();
            String userId = "jingli";
            //3.指派任务
            taskService.claim(taskId, userId);
        }
    }


提示:可以根据任务委托人在领取任务前和领取任务后分别查询任务,之前没有之后有即为正确。

TaskQuery query = taskService.createTaskQuery();//创建任务查询对象
List<Task> list = query.processDefinitionKey("MyProcess") //指定流程定义
      .taskAssignee("jingli")//指定委托人
      .list();//执行查询
for (Task task : list) {
  System.out.println(task);
}


11.流程变量


①在创建流程定义时指定流程变量


例如:用变量的方式指定委托人

创建MyProcess02.bpmn

20181225201704556.png

20181225201715182.png


创建后正常部署

    @Test
    public void test01ProcessDefinitionDeployment() {
        //部署:将流程定义文件中流程定义信息存入数据库
        repositoryService
            .createDeployment()     //创建部署构建器对象
            .addClasspathResource("MyProcess02.bpmn")   //添加要部署的流程定义文件名
            .deploy();      //执行部署
    }


②启动


    @Test
    public void test05StartProcessInstance() {
        //1.查询流程定义对象
        ProcessDefinition processDefinition = 
                repositoryService.createProcessDefinitionQuery()
                                 .processDefinitionKey("MyProcess02")
                                 .latestVersion()
                                 .singleResult();
        //2.获取流程定义的id
        String processDefinitionId = processDefinition.getId();
        //3.使用RunTimeService根据流程定义id启动流程定义
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);
        //4.打印processInstance对象
        System.err.println(processInstance);
    }


带有变量的流程启动时,如果第一个任务中就存在变量,那么启动的时候就必须给出变量值。否则将抛出异常:org.activiti.engine.impl.javax.el.PropertyNotFoundException: Cannot resolve identifier 'erzi'

2018122520363038.png


    @Test
    public void test06StartProcessInstanceWithVariable() {
        //1.查询流程定义对象
        ProcessDefinition processDefinition = 
                repositoryService.createProcessDefinitionQuery()
                                 .processDefinitionKey("MyProcess02")
                                 .latestVersion()
                                 .singleResult();
        //2.获取流程定义的id
        String processDefinitionId = processDefinition.getId();
        //※为第一个任务指定流程变量
        Map<String,Object> variables = new HashMap<String, Object>();
        variables.put("erzi", "zhangsan");
        //3.使用RunTimeService根据流程定义id启动流程定义
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, variables);
        //4.打印processInstance对象
        System.err.println(processInstance);
    }


③完成任务


如果完成当前任务时,下一个任务中包含变量,那么此时也必须给出下一个任务的变量值

    @Test
    public void test7CompleteTaskWithVariables() {
        List<Task> list = 
                taskService.createTaskQuery() //创建任务查询对象
                           .processDefinitionKey("MyProcess02")//指定流程定义
                           .taskAssignee("zhangsan")//指定委托人
                           .list();//执行查询
        for (Task task : list) {
            String taskId = task.getId();
            //在完成当前任务时,为下一个任务指定流程变量
            Map<String,Object> variables = new HashMap<String, Object>();
            variables.put("sunzi", "lisi");
            //完成当前任务时为下一个任务指定变量值
            taskService.complete(taskId, variables);//完成任务,使任务进入下一步
            //Unknown property used in expression: ${sunzi}
            //taskService.complete(taskId);
        }
    }


12.排他网关


①创建流程定义


20181225205636107.png

20181225205730764.png

部署、启动

    @Test
    public void test01ProcessDefinitionDeployment() {
        //部署:将流程定义文件中流程定义信息存入数据库
        repositoryService
            .createDeployment()     //创建部署构建器对象
            .addClasspathResource("MyProcess03.bpmn")   //添加要部署的流程定义文件名
            .deploy();      //执行部署
    }



    @Test
    public void test05StartProcessInstance() {
        //1.查询流程定义对象
        ProcessDefinition processDefinition = 
                repositoryService.createProcessDefinitionQuery()
                                 .processDefinitionKey("MyProcess03")
                                 .latestVersion()
                                 .singleResult();
        //2.获取流程定义的id
        String processDefinitionId = processDefinition.getId();
        //3.使用RunTimeService根据流程定义id启动流程定义
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);
        //4.打印processInstance对象
        System.err.println(processInstance);
    }


②完成第一个任务时为排他网关指定变量值


20181225210559380.png


    @Test
    public void test7CompleteTaskWithVariables() {
        List<Task> list = 
                taskService.createTaskQuery() //创建任务查询对象
                           .processDefinitionKey("MyProcess03")//指定流程定义
                           .taskAssignee("zhangsan")//指定委托人
                           .list();//执行查询
        for (Task task : list) {
            String taskId = task.getId();
            //在完成当前任务时,为下一个任务指定流程变量
            Map<String,Object> variables = new HashMap<String, Object>();
            variables.put("day", "5");
            //完成当前任务时为下一个任务指定变量值
            taskService.complete(taskId, variables);//完成任务,使任务进入下一步
            //Unknown property used in expression: ${sunzi}
            //taskService.complete(taskId);
        }
    }

20181225210650906.png

20181225211001441.png

20181225211035528.png


20181225211100851.png

20181225211136888.png


2018122521125185.png

[3]创建流程监听器类

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
public class YesListener implements ExecutionListener {
    private static final long serialVersionUID = 1L;
    @Override
    public void notify(DelegateExecution execution) throws Exception {
        System.err.println("YesListener触发了!!!");
    }
}



import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
public class NoListener implements ExecutionListener {
    private static final long serialVersionUID = 1L;
    @Override
    public void notify(DelegateExecution execution) throws Exception {
        System.err.println("NoListener触发了!!!");
    }
}


[4]绑定监听器

20181225212456179.png


④操作


  • 部署
    /**
     * 部署
     */
    @Test
    public void test01ProcessDefinitionDeployment() {
        //部署:将流程定义文件中流程定义信息存入数据库
        repositoryService
            .createDeployment()     //创建部署构建器对象
            .addClasspathResource("MyProcess04.bpmn")   //添加要部署的流程定义文件名
            .deploy();      //执行部署
    }


  • 启动
    /**
     * 启动流程
     */
    @Test
    public void test05StartProcessInstance() {
        //1.查询流程定义对象
        ProcessDefinition processDefinition = 
                repositoryService.createProcessDefinitionQuery()
                                 .processDefinitionKey("MyProcess04")
                                 .latestVersion()
                                 .singleResult();
        //2.获取流程定义的id
        String processDefinitionId = processDefinition.getId();
        //3.使用RunTimeService根据流程定义id启动流程定义
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId);
        //4.打印processInstance对象
        System.err.println(processInstance);
    }


  • 完成任务时为排他网关指定flag变量值
    /**
     * 完成任务
     */
    @Test
    public void test7CompleteTaskWithVariables() {
        List<Task> list = 
                taskService.createTaskQuery() //创建任务查询对象
                           .processDefinitionKey("MyProcess04")//指定流程定义
                           .taskAssignee("zhangsan")//指定委托人
                           .list();//执行查询
        for (Task task : list) {
            String taskId = task.getId();
            //在完成当前任务时,为下一个任务指定流程变量
            Map<String,Object> variables = new HashMap<String, Object>();
            variables.put("flag", "true");
            //完成当前任务时为下一个任务指定变量值
            taskService.complete(taskId, variables);//完成任务,使任务进入下一步
            //Unknown property used in expression: ${sunzi}
            //taskService.complete(taskId);
        }
    }


  • 观察监听器执行情况
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
XML Java 关系型数据库
Activiti工作流引擎介绍
Activiti工作流引擎介绍
|
设计模式 安全 Java
Activiti工作流学习笔记(四)——工作流引擎中责任链模式的建立与应用原理
过滤器链就像一条铁链,中间的每个过滤器都包含对另一个过滤器的引用,从而把相关的过滤器链接起来,就像一条链的样子。这时请求线程如蚂蚁一样,会沿着这条链一直爬过去-----即,通过各过滤器调用另一个过滤器引用方法chain.doFilter(request, response),实现一层嵌套一层地将请求传递下去,当该请求传递到能被处理的过滤器时,就会被处理,处理完成后转发返回。通过过滤器链,可实现在不同的过滤器当中对请求request做拦截增加,且过滤器之间彼此互不干扰。
79 0
|
存储 XML SQL
Activiti工作流框架学习笔记(一)之通用数据表详细介绍
Activiti工作流框架学习笔记(一)之通用数据表详细介绍
488 1
|
存储 XML Java
Activiti工作流与业务整合实战
Activiti工作流与业务整合实战
540 0
Activiti工作流与业务整合实战
|
XML 存储 Java
工作流 jbpm(图文并茂)
工作流 jbpm(图文并茂)
|
XML 存储 API
工作流 jbpm(图文并茂)(下)
工作流 jbpm(图文并茂)
|
API 容器
Activiti工作流引擎的使用、思考与总结(上)
Activiti工作流引擎的使用、思考与总结
220 0
Activiti工作流引擎的使用、思考与总结(下)
Activiti工作流引擎的使用、思考与总结
159 0
|
安全 Java 关系型数据库
Activiti工作流引擎整合系列【收藏可做笔记系列】
Activiti工作流引擎整合系列【收藏可做笔记系列】
Activiti工作流引擎整合系列【收藏可做笔记系列】
|
数据库
FluentMybatis 项目工程化、常规操作(增删改查)(二) | FluentMybatis实践(2)
FluentMybatis 项目工程化、常规操作(增删改查)(二) | FluentMybatis实践
FluentMybatis 项目工程化、常规操作(增删改查)(二) | FluentMybatis实践(2)