什么是工作流?

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

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

配置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&useUnicode=true&characterEncoding=UTF-8&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张数据表进行不同的操作,如下:

activiti5 spring 工作流 activiti7工作流_spring

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

activiti流程体验

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

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

1、对流程进行抽象建模

activiti5 spring 工作流 activiti7工作流_数据库_02

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

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历史任务实例表,会发现该表中拥有本次任务流程的全部步骤以及详细信息

activiti5 spring 工作流 activiti7工作流_System_03


可以看到各个步骤开始及完成的时间以及负责人等详细信息

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表