activiti7执行流程详解_activiti7 流条件-程序员宅基地

技术标签: java  Java进阶  数据库  开发语言  

什么是工作流?

官方定义:工作流是将一组任务组织起来以完成某个经营过程:定义了任务的触发顺序和触发条件,每个任务可以由一个或多个软件系统完成,也可以由一个或一组人完成,还可以由一个或多个人与软件系统协作完。

我的理解:工作流就是针对程序的业务流程进行自动化管理、执行的流。让业务的执行过程根据我们预定义好的规则不断地触发和执行。它最明显的体现就是在项目中的一些审批操作,比如说员工请假、订单审核等等类似一种的操作时业务在当前操作完成后自动的跳转进行下一操作

配置Activiti7建表

2010年5月份启动,目前使用最广泛的工作流引擎。使用activiti可以将复杂的业务流程进行抽离,业务流程按照预先定义好的规则执行。将业务流程交给activiti去管理使得当业务发生变更时不需要去大量得改动程序,降低了维护成本。

activiti使用专门的建模语言BPMN进行定义,BPMN就是一组定义好的符号语言,使用这些符号来模拟整个复杂业务的执行流程

activiti的配置文件:实际上也是利用spring来配置的xml文件,其中主要配置的就是一个数据源以及注册引擎配置类Bean实例,通过流程引擎配置类ProcefssEngineConfiguration,我这里使用的是druid连接池,然后将连接池引入到ProcefssEngineConfiguration中

<?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">

  <!--数据源配置druid -->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
    <property name="url"
              value="jdbc:mysql://localhost:3306/activiti?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC" />
    <property name="username" value="root" />
    <property name="password" value="123456" />
  </bean>
  <!--activiti单独运行的ProcessEngine配置对象(processEngineConfiguration),使用单独启动方式 默认情况下:bean的id=processEngineConfiguration -->
<!--默认创建时  该id名必须不可变 processEngineConfiguration-->
  <bean id="processEngineConfiguration"
 class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
    <!--代表数据源 -->
    <property name="dataSource" ref="dataSource"></property>
    <!--代表是否生成表结构 -->
    <property name="databaseSchemaUpdate" value="true" />
  </bean>
</beans>

第一次使用activiti需要去生成如下25张数据表,我们只需要通过获取ProcessEngine就可以自动创建

public void test01(){
    
  // 创建activiti表  后期有ProcessEngine帮助我们管理  
  // 默认创建方式
  ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
  System.out.println(processEngine);
  // 自定义创建方式
  ProcessEngineConfiguration processEngineConfigurationFromResource =
                ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("自定义文件名","自定义bean名");
}

自定义创建方式,更加灵活。activiti的配置文件命名以及processEngineConfiguration的id命名都可以自定义

databaseSchemaUpdate是指activiti的表生成策略,true标识如果数据库中已经存在则直接使用,如果不存在则先创建再使用

activiti核心组件PageEngine

activiti的核心就是ProcessEngine,就是工作流引擎。那么通过ProcessEngine可以调用不同的service接口,这些接口都封装了操作activiti生成表的方法,可以对于activiti生成的25张数据表进行不同的操作,如下:

在这里插入图片描述

像repositoryService就是用于部署流程服务,Runtime就是运行时服务,每个Service都对应有自己的数据表。我们在调用每个服务实例的方法实际上都是在操作数据库中的activiti生成的表。

activiti流程体验

activiti的使用步骤:
1、定义流程:按照BPMN使用流程定义符号对整个流程进行抽象建模
2、部署流程:将构建好的BPMN文件加载到数据库中生成表数据
3、启动流程:使用Java程序操作数据库表内容

下面我们用员工的请假审批流程做演示

1、对流程进行抽象建模在这里插入图片描述

每个步骤都有自己对应的操作,以及对应的负责人。

2、流程任务部署

// 流程任务部署
@Test
public void deploymentTest(){
    
  // (1)创建流程引擎 加载配置文件 默认加载activiti.cfg.xml文件
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // (2)获取repositoryService
  RepositoryService repositoryService = engine.getRepositoryService();
  // (3)通过repositoryService创建部署流程
  Deployment deploy = repositoryService.createDeployment()
    // (4) 添加资源文件
    .addClasspathResource("bpmn/test.bpmn20.xml")
    .addClasspathResource("bpmn/diagram.png")
    .name("员工请假审批流程")
    // (5)部署
    .deploy();
  // 查看当前流程的id、名称
  System.out.println("申请流程实例ID="+deploy.getId());
  System.out.println("申请流程示例名称="+deploy.getName());
}

下面启动一项任务流程实例,即对应项目中有员工申请了请假操作时的流程

// 启动流程实例
@Test
public void testProcess(){
    
  // 1. 创建ProcessEngine
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 2. 获取RunTimeService运行时服务
  RuntimeService service = engine.getRuntimeService();
  // 3. 根据流程定义的id启动流程  我这里时test
  ProcessInstance instance = service.startProcessInstanceByKey("test");
  // 输出内容
  System.out.println("流程定义ID="+instance.getProcessDefinitionId());
  System.out.println("流程实例ID="+instance.getId());
  System.out.println("当前活动ID="+instance.getActivityId());
}

在项目开发中,如果某一用户或者是员工想要请假,那么就会为其启动一项任务流程。启动一项任务流程之后,activiti就会在对应的运行服务表中做出一些记录。启动流程之后也就对应着我们的BPMN图走到了第二步:经理审批

那么这个时候我们通过经理这个角色就可以查询到此时经理需要处理的任务流程,如下:查询个人待处理业务

// 查询个人待执行的任务
@Test
public void testGetPersonalTaskList(){
    
// 1. 获取引擎
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2. 获取TaskService
TaskService taskService = engine.getTaskService();
// 3. 根据条件查询  以及任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("forLeave")  // 任务
.taskAssignee("zhangsan")   // 负责人
.list();
for (Task task : list) {
    
System.out.println("流程实例ID==="+task.getProcessInstanceId());
System.out.println("任务ID==="+task.getId());
System.out.println("任务负责人==="+task.getAssignee());
System.out.println("任务名称==="+task.getName());
}
}

上述程序只是查询到了某一角色的待完成任务:部门主管审批,下面是具体操作来使该角色完成任务,同样是使用流程引擎获取TaskService

@Test
public void complete(){
    
  // 获取流程引擎
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 获取任务TaskService
  TaskService taskService = engine.getTaskService();
  // 根据任务id完成任务  将上述查询道道的待完成任务id传入
  taskService.complete("2504");
}

该角色任务完成后将会跳转至下一步骤:部门经理审批。可以通过act_ru_task查看,该表就是对应某一项任务的执行状态,每一步的负责人通过之后就会跳转至下一项。同样的也可以通过act_hi_taskinst该表来查看某一项任务的执行进度,在对应的数据后会有该步骤的开始时间,如果已完成则会有完成结束时间

那么在实际项目中,我们可以首先通过获取某个角色的待完成任务放在页面上,该角色如果审批通过,那么可以将它点击通过的任务id作为形参传入上述complete方法中,以此完成该角色审批任务

下面执行部门经理审批,完成所有步骤

// 继续完成部门经理审批
@Test
public void completeTask(){
    
  // 获取流程引擎
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 获取任务TaskService
  TaskService taskService = engine.getTaskService();
  // 查询部门经理lisi待完成得请假审批任务  并获取该任务对象
  Task task = taskService.createTaskQuery()
    .processDefinitionKey("forLeave")
    .taskAssignee("lisi")
// 获取单个任务  如果有多个任务则要用list();
    .singleResult();
  // 让李四完成任务 根据任务id完成任务  将上述查询到的待完成任务id传入
  taskService.complete(task.getId());
}

processDefinitionKey当中得key也就是参数就是需要查询得任务流程的id,就是构建BPMN点击空白界面时出现的id。完成所有任务后再次查看act_ru_task运行任务表会发现刚刚执行任务流程已经被删除,这是因为任务已经结束。

我们可以查看act_hi_actinst历史任务实例表,会发现该表中拥有本次任务流程的全部步骤以及详细信息
在这里插入图片描述
可以看到各个步骤开始及完成的时间以及负责人等详细信息

zip方式批量部署流程任务

// zip批量部署任务
@Test
public void deployPrecessZip(){
    
  // 获取流程引擎
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 获取部署repositoryService
  RepositoryService service = engine.getRepositoryService();
  // 批量部署流程
  // 先读取文件
  InputStream inputStream = this.getClass()
    .getClassLoader()
    .getResourceAsStream("bpmn/bpmn.zip");
  // 将文件转换为zip流
  ZipInputStream zipInputStream = new ZipInputStream(inputStream);
  // 将zip流形式的文件交给service去读
  Deployment deploy = service.createDeployment()
    .addZipInputStream(zipInputStream)
    .deploy();
  System.out.println("部署的任务流程名称=="+deploy.getName());
  System.out.println("部署的任务流程ID=="+deploy.getId());
}

流程定义查询

流程定义查询,实际上就是查询指定的全部任务最终以list集合展示,比如在界面上需要展示当前所有申请了请假的员工的请假任务执行流程

// 查询流程定义  与上述查询某角色个人待执行任务类似
@Test
public void queryPrecessDefinition(){
    
  // 获取引擎
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 获取repositoryService
  RepositoryService service = engine.getRepositoryService();
  // 获取query查询实例
  ProcessDefinitionQuery query = service.createProcessDefinitionQuery();
  // 查询当前所有流程定义
  List<ProcessDefinition> leave = query.processDefinitionKey("forLeave")   // key
    .orderByProcessDefinitionVersion() // 版本号
    .desc()   // 倒叙
    .list();//  返回集合
  // 信息打印
  for (ProcessDefinition processDefinition : leave) {
    
    System.out.println("流程定义ID=="+processDefinition.getId());
    System.out.println("流程定义Name=="+processDefinition.getName());
    System.out.println("流程定义key=="+processDefinition.getKey());
    System.out.println("流程定义版本=="+processDefinition.getVersion());
  }
}

其实流程定义查询主要查询的就是act_re_procdef这个表中的数据

流程定义删除(删除部署)

先看程序

// 删除部署根据部署ID
@Test
public void deletedDeployment(){
    
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  RepositoryService service = engine.getRepositoryService();
  service.deleteDeployment("1");
}

流程定义的部署就是将流程任务的各种数据信息存放到数据库中的各个表上,那么删除部署的时候自然就会将所有相关的部署信息全部删除。就是你在部署的时候操作了那些表,那么删除部署的时候就还是操作这些表,将数据全部清空。

但是,如果当前流程启动了并且还未执行完毕。那么此时再根据部署ID去删除时是不会成功的,必须要删除就只能通过级联删除

级联删除

service.deleteDeployment("5001"); 
service.deleteDeployment("5001",true);

上述两种都是根据部署ID删除流程任务,第二种方式就是调用了deleteDeployment的重载方法,如果将第二个参数设置为true则表示打开级联删除。默认为false

流程资源下载

使用activiti提供的api进行下载:需要导入依赖

<!--  activiti提供的下载资源工具  -->
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.11.0</version>
</dependency>

下载的具体步骤:

// 流程资源下载
@Test
public void uploadResource() throws IOException {
    
  // 获取流程引擎
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 获取RepositoryService
  RepositoryService service = engine.getRepositoryService();
  // 通过流程定义信息获取部署ID
  ProcessDefinition leave = service.createProcessDefinitionQuery()
    .processDefinitionKey("forLeave")
    .singleResult();
  // 获取部署ID
  String deploymentId = leave.getDeploymentId();
  // 读取资源(png和bpmn)
  // 获取bpmn资源
  String pngName = leave.getDiagramResourceName();
  // 获取图片
  String bpmnName = leave.getResourceName();
  // 通过部署ID和名字获取图片资源
  // 获取 图片输入流
  InputStream pngInput = service.getResourceAsStream(deploymentId, pngName);
  // 获取bpmn资源输入流
  InputStream bpmnInput = service.getResourceAsStream(deploymentId, bpmnName);
  // 构造输出流
  File file1 = new File("E:/emo01.png");
  File file2 = new File("E:/emo02.bpmn");
  FileOutputStream fileOutputStream = new FileOutputStream(file1);
  FileOutputStream fileOutputStream2 = new FileOutputStream(file2);
  // 转换输入输出流
  IOUtils.copy(pngInput,fileOutputStream);
  IOUtils.copy(bpmnInput,fileOutputStream2);
  // 关闭流
  fileOutputStream2.close();
  fileOutputStream.close();
  bpmnInput.close();
  pngInput.close();
}

流程历史信息查看

// 查看历史流程信息
@Test
public void queryHistoryInstance(){
    
  // 获取引擎
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 获取历史服务 HistoryService
  HistoryService historyService = engine.getHistoryService();
  // 创建查询
  List<HistoricActivityInstance> list = historyService
    .createHistoricActivityInstanceQuery()
    .processInstanceId("12501")
    .orderByHistoricActivityInstanceStartTime()
    .asc() // 升序
    .list();
  // 输出查看
  for (HistoricActivityInstance hi : list) {
    
    System.out.println("该步骤ID=="+hi.getActivityId());
    System.out.println("该步骤Name=="+hi.getActivityName());
    System.out.println("任务ID=="+hi.getProcessDefinitionId());
    System.out.println("流程ID=="+hi.getProcessInstanceId());
    System.out.println("<-------------------------------------->");
  }
}

上述输出语句,Activity表示某一流程的具体某一步骤。每一个步骤都会有自己的ID以及开始和结束时间。
ProcessDefinitionID任务ID就是表示当前流程属于某一种业务的具体实例,可以是请假审批或者报销申请等。
ProcessInstance流程ID就是表示流程实例ID比如具体的某一项请假业务或者报销业务
ProcessInstance > ProcessDefinition > Activity
而流程历史信息查看实际上就是查询了act_hi_actinst表

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yuqu1028/article/details/129308724

智能推荐

【政考网答疑】为什么公务员招录限制35岁以下?-程序员宅基地

文章浏览阅读937次。政考网每日一答,今日咱们讨论的问题是为什么公务员招录限制35岁以下?众所周知,无论是各地省考还是国考,均会要求考生年龄在18周岁以上、35周岁以下(应届硕士和博士经招录机关同意,可放宽到40岁),那么,公务员招录考试为何会限制35岁以下报考呢?这样的要求是否合理?1、高龄人员的可塑性相对不强相比较应届毕业生或者刚毕业不久的大学生,35以上的考生在身体素质上的优势并不明显,特别是一些基层岗位,条件艰苦,高龄考生的岗位匹配度相对较低。古语云,“三十而立,四十而不惑。”高龄考生已从过...

物理驱动深度学习(PINN)代码_pinn代码-程序员宅基地

物理驱动深度学习(PINN)代码的摘要是:论文总结了相关代码,包括物理驱动深度学习框架和使用先验字典进行加速训练的改进代码。代码链接可在论文中找到。

025目标检测表情检测识别yolov5pyqt_python_yolov5表情识别 表情-程序员宅基地

文章浏览阅读198次。其中data文件夹下存放的图像数据集,包括alert(注意力集中)、non_vigilant(漫不经心)、tired(疲劳)等。运行02train.py会将txt文本中的图像数据读取进行模型的训练,最后保存在runs文件夹下。运行04pyqt界面.py会弹出一个可视化的ui界面,通过点击按钮检测自己感兴趣的图片。运行03detector_photo.py可以实现对单张图片的检测。运行01makeTxt.py会将数据集图片路径保存在txt文本中。_yolov5表情识别 表情

【Linux】Linux的消息队列_linux c消息队列例子-程序员宅基地

文章浏览阅读1w次,点赞3次,收藏43次。消息队列亦称报文队列,也叫做信箱。意思是说,这种通信机制传递的数据具有某种结构,而不是简单的字节流。消息队列的工作机制如下所示: 消息的结构用户空间的消息缓冲区为在进程与内核之间传递消息,无论发送进程还是接收进程,都需要在进程空间中用消息缓冲区来暂存消息。该消息缓冲区的结构定义如下:struct msgbuf { long mtype; /* 消息的类型..._linux c消息队列例子

软件安装:RPM、SRPM 与 YUM 功能_pgpool srpm 是什么-程序员宅基地

文章浏览阅读596次。一、Linux 界的两大主流:RPM 与 DPKG  目前在 Linux 界软件安装方式最常见的有两种,分别是: - dpkg:只要是派生于 Debian 的其它 Linux 大多使用 dpkg 这个机制来管理软件,包括 Ubuntu、B2D 等。 - RPM:CentOS、Red Hat 等都是使用它。二、什么是 RPM 与 SRPM  RPM 全名是“RedHat Package Manag_pgpool srpm 是什么

运动学与动力学约束下的在线机器人时间最优轨迹规划算法-程序员宅基地

文章浏览阅读1.3k次,点赞23次,收藏33次。时间最优、轨迹规划、动力学约束、在线

随便推点

无人机侦测设备知识,无人机侦测频谱仪市场应用前景分析-程序员宅基地

文章浏览阅读531次,点赞3次,收藏5次。综上所述,无人机侦测频谱仪市场应用前景广阔,将受益于技术发展、5G时代、物联网、航空航天、汽车智能化等多个领域的推动。未来,随着国内市场的不断扩大和国际市场的逐步开拓,国产无人机侦测频谱仪的市场份额有望进一步提升。当前,无人机侦测频谱仪正朝着小型化、高灵敏度、多功能等方向发展,这些技术上的进步为无人机侦测频谱仪在多个行业中的应用提供了有力支持。无人机侦测频谱仪可以为消费电子产品的研发和生产提供频谱分析和监测服务,推动消费电子产品的不断升级和迭代。5G技术的广泛应用,为无人机侦测频谱仪带来了巨大的市场需求。

关注安防行业 聚焦公共安防系统-程序员宅基地

文章浏览阅读74次。以强化城市治安管理为导向的“平安城市”和“平安工程”建设不断升温,公共安防以核心角色的身份走进大众视野。城市建设的重大成果之一是各种各样的公共区域的设施完善和服务提升。重点公共安防场所在于公交车站、机场、地铁站、火车站等公共交通转换场所,而根据社会职能的重要性和安全偏重性,重点场所囊括了幼儿园、学校、医院、金融机构、商业场所等公共区域。公共安防关系每个人..._安防公共区域定义

php5.6 mysql nginx_centos7 安装php5.6+nginx1.11+mysql5.7-程序员宅基地

文章浏览阅读177次。本文是介绍使用源码编译安装,包括具体的编译参数信息。正式开始前,编译环境gcc g++ 开发库之类的需要提前装好。安装make:yum -y install gcc automake autoconf libtool make安装g++:yum install gcc gcc-c++一般我们都需要先装pcre, zlib,前者为了重写rewrite,后者为了gzip压缩。1.选定源码目录可以是任何..._centos yum部署nginx+php5.6+mysql5.7

android最新扫描,Android下实现雷达扫描效果(学习)-程序员宅基地

文章浏览阅读412次。Android UI前几天看见一篇文章Android 雷达扫描动画效果实现 就学习了一下,在此做了笔记并做了一些修改自定义ScanRadar继承了View控件初始化几个画笔private void initPaint() {//用来绘画直线的画笔mPaintLine = new Paint(Paint.ANTI_ALIAS_FLAG); // 消除锯齿mPaintLine.setAntiAlias..._android雷达扫描设备效果

使用OpenCV进行直播(附代码)-程序员宅基地

文章浏览阅读961次。点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达生活中我们不止一次地使用过直播,但从未想过如何通过编程实现。大家好,我们在这里向大家介绍如何使用OpenCV和pyth..._opencv直播

封装是面向对象编程中的一种核心概念,它是指将类的内部数据和方法组合到一个抽象的数据类型中-程序员宅基地

文章浏览阅读80次。对于类的外部用户来说,它们无法访问和修改类的内部数据,只能通过类的公共函数来调用和更新数据。这样,在类实现和修改时,可以避免对用户的影响,也避免了用户恶意直接访问和修改内部数据的情况发生。总之,封装是面向对象编程中的一种重要思想,它通过隐藏类的内部实现机制,对外部提供更安全、更可靠和更易用的接口来访问和使用类的属性和方法。封装是面向对象编程中的一种核心概念,它是指将类的内部数据和方法组合到一个抽象的数据类型中,并且对外部隐藏了类的内部实现机制,只提供外部接口来访问和使用类的属性和方法。

推荐文章

热门文章

相关标签