Activiti工作流

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 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);
        }
    }


  • 观察监听器执行情况
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
Vue2步骤条(Steps)
这是一个基于 Vue3 的步骤条(Steps)组件,支持高度自定义。主要属性包括步骤标题数组(stepsLabel)、步骤描述数组(stepsDesc)、步骤总数(totalSteps,默认为3)、当前选中的步骤(currentStep,默认为1)、步骤条总宽度(totalWidth,默认为900px)和描述文本最大宽度(descMaxWidth,默认为140px)。组件通过不同的样式展示已完成、进行中和未开始的状态,并支持点击切换步骤。可在需要的页面中引入并传入相关初始数据。
407 1
Vue2步骤条(Steps)
|
10月前
|
计算机视觉
RT-DETR改进策略【卷积层】| SAConv 可切换的空洞卷积 二次创新ResNetLayer
RT-DETR改进策略【卷积层】| SAConv 可切换的空洞卷积 二次创新ResNetLayer
204 12
RT-DETR改进策略【卷积层】| SAConv 可切换的空洞卷积 二次创新ResNetLayer
|
Ubuntu 网络协议 Linux
EVE-NG初次启动及WEB客户端访问
本章从虚拟机Eve模拟器启动、模拟器的启动配置、浏览器访问三个步骤讲解EVE-NG的首次启动。 1.启动模拟器 打开虚拟机环境,启动安装好的EVE-NG虚拟机,进入如下界面。
|
网络协议 Ubuntu Linux
无公网IP内网穿透使用vscode配置SSH远程ubuntu随时随地开发写代码-2
无公网IP内网穿透使用vscode配置SSH远程ubuntu随时随地开发写代码
|
存储 XML SQL
Activiti工作流框架学习笔记(一)之通用数据表详细介绍
Activiti工作流框架学习笔记(一)之通用数据表详细介绍
912 1
|
存储 安全 定位技术
ToDesk软件在权限提升中的应用
ToDesk软件在权限提升中的应用
1068 0
|
存储 SQL Cloud Native
基于Ganos的栅格引擎开展区域面雨量分析
本文介绍了由阿里云联合阿里巴巴达摩院数据库与存储实验室研发的多模态时空数据库Ganos之栅格引擎(Ganos Raster)在水利/气象领域的分析场景应用。Ganos通过在数据库中原生内置影像与格网数据的存储、检索与分析能力,为气象、水利、资源管理、应急、传媒等客户提供海量栅格数据的分析挖掘能力。通过阅读本文,用户可以更好的理解Ganos栅格模型的存储结构与相关分析能力,助力业务开发走向便捷。
|
消息中间件 NoSQL 固态存储
Spring boot集成plumelog日志系统
近几日闲来无事,工作摸鱼之时在码云上发现一个更加轻量级的分布式日志系统 PlumeLog ,就研究了一下,写了一个demo,做个记录
|
Swift 图形学 数据安全/隐私保护
Swift 各版本
介绍Swift各个历史版本
463 0
Swift 各版本
【蓝桥杯嵌入式】蓝桥杯第十届省赛真题,程序题全解析(含代码)
【蓝桥杯嵌入式】蓝桥杯第十届省赛真题,程序题全解析(含代码)
792 0