首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Activiti工作流

Activiti工作流

作者头像
Java廖志伟
发布2022-09-28 14:38:18
6100
发布2022-09-28 14:38:18
举报
文章被收录于专栏:高级开发进阶高级开发进阶

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

还未完善

场景:学校    

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

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

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

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

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

大纲:

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

使用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.创建第一个流程图

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);
        }
    }

注意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.将任务指派给一个组

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

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

创建后正常部署

    @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'

    @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.排他网关

①创建流程定义

部署、启动

    @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);
        
    }

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

    @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);
        }
    }

[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]绑定监听器

④操作

部署

    /**
     * 部署
     */
    @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);
        }
    }

观察监听器执行情况

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2.创建SpringBoot工程
    • ①编辑POM文件
      • ②主启动类
        • ③测试类
          • ④yml配置
            • ⑤特别操作
            • 三、流程操作
              • 1.创建第一个流程图
                • 2.流程部署
                  • ①Java代码
                  • ②数据库表分析
                  • ③解决乱码问题
                • 3.流程查询
                  • ①第一步:创建ProcessDefinitionQuery对象
                • 6.查询任务和7.完成任务
                  • 8.查询流程实例历史
                    • 9.将任务指派给一个组
                      • 10.领取任务
                        • 11.流程变量
                          • ①在创建流程定义时指定流程变量
                          • ②启动
                          • ③完成任务
                        • 12.排他网关
                          • ①创建流程定义
                          • ②完成第一个任务时为排他网关指定变量值
                          • ④操作
                      相关产品与服务
                      对象存储
                      对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档