【笔记整理】Activiti工作流的学习笔记

Activiti基础篇

1、什么是工作流

1.1 工作流简介

工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。

以请假举例说明:

  1. 大多数公司请假流程:

    员工打电话(或网聊)向上级提出请假申请——上级口头同意——上级将请假记录下来——月底将请假记录上交公司——公司将请假录入电脑

  2. 采用工作流技术的公司的请假流程:

    员工使用账户登录系统——点击请假——上级登录系统点击允许

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

1.2 工作流系统

一个软件系统中具有工作流的功能,我们把它称为工作流系统,一个系统中工作流的功能是什么?就是对系统的业务流程进行自动化管理,所以工作流是建立在业务流程的基础上,所以一个软件的系统核心根本上还是系统的业务流程,工作流只是协助进行业务流程管理。即使没有工作流业务系统也可以开发运行,只不过有了工作流可以更好的管理业务流程,提高系统的可扩展性。

1.3 适用行业

消费品行业,制造业,电信服务业,银证险等金融服务业,物流服务业,物业服务业,物业管理,大中型进出口贸易公司,政府事业机构,研究院所及教育服务业等,特别是大的跨国企业和集团公司。

1.4 具体应用

  1. 关键业务流程:订单、报价处理、合同审核、客户电话处理、供应链管理等
  2. 行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请、购买申请、日报
    周报等凡是原来手工流转处理的行政表单。
  3. 人事管理类:员工培训安排、绩效考评、职位变动处理、员工档案信息管理等。
  4. 财务相关类:付款请求、应收款处理、日常报销处理、出差报销、预算和计划申请等。
  5. 客户服务类:客户信息管理、客户投诉、请求处理、售后服务管理等。
  6. 特殊服务类: ISO 系列对应流程、质量管理对应流程、产品数据信息管理、贸易公司报关处理、物流公司货物跟踪处理等各种通过表单逐步手工流转完成的任务均可应用工作流软件自动规范地实施。

1.5 工作流技术的优点

实现工作流程的自动化,提高了企业运营效率、改善企业运营的灵活性和适应性,提高量化考核业务处理的效率、减少时间的浪费

而手工处理工作流程,一方面无法对整个流程状况进行有效跟踪、了解,另一方面难免会出现人为的失误和时间上的延时导致效率低下,特别是无法进行量化统计,不利于查询、报表及绩效评估。

1.6 实现方式

在没有专门的工作流引擎之前,我们之前为了实现流程控制,通常的做法就是采用状态字段的值来
跟踪流程的变化情况。这样不用角色的用户,通过状态字段的取值来决定记录是否显示。

针对有权限可以查看的记录,当前用户根据自己的角色来决定审批是否合格的操作。如果合格将状

态字段设置一个值,来代表合格;当然如果不合格也需要设置一个值来代表不合格的情况。

这是一种最为原始的方式。通过状态字段虽然做到了流程控制,但是当我们的流程发生变更的时候,

这种方式所编写的代码也要进行调整。

2、什么是Activiti

2.1 Activiti介绍

Alfresco 软件在 2010 年 5 月 17 日宣布 Activiti 业务流程管理(BPM)开源项目的正式启动, 其首席架构师由业务流程管理 BPM 的专家 Tom Baeyens 担任, Tom Baeyens 就是原来 jbpm 的架构师,而 jbpm 是一个非常有名的工作流引擎,当然 activiti 也是一个工作流引擎。
Activiti 是一个工作流引擎, activiti 可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言(BPMN2.0)进行定义,业务系统按照预先定义的流程进行执行,实现了业务系统的业务流程由 activiti 进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

官方网站: https://www.activiti.org/

2.2 BPM

BPM(Business Process Management),即业务流程管理,是一种以规范化的构造端到端的卓越
业务流程为中心,以持续的提高组织业务绩效为目的系统化方法,常见商业管理教育如 EMBA、MBA
等均将 BPM 包含在内。
企业流程管理主要是对企业内部改革,改变企业职能管理机构重叠、中间层次多、流程不闭环
等,做到机构不重叠、业务不重复,达到缩短流程周期、节约运作资本、提高企业效益的作用。

2.3 BPM软件

BPM 软件在企业中应用领域广泛, 凡是有业务流程的地方都可以 BPM 软件进行管理,比如企业人事办公管理、采购流程管理、公文审批流程管理、财务管理等。

2.4 BPMN

BPMN(Business Process Model And Notation) - 业务流程模型和符号, 是由 BPMI(BusinessProcess Management Initiative)开发的一套标准的业务流程建模符号,使用 BPMN 提供的符号可以创建业务流程。 2004 年 5 月发布了 BPMN1.0 规范.BPMI 于 2005 年 9 月并入 OMG(The ObjectManagement Group 对象管理组织)组织。 OMG 于 2011 年 1 月发布 BPMN2.0 的最终版本。

Bpmn 图形其实是通过 xml 表示业务流程

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="myProcess" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="创建请假单"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1"
targetRef="usertask1"></sequenceFlow>
<userTask id="usertask2" name="部门经理审核"></userTask>
<sequenceFlow id="flow2" sourceRef="usertask1"
targetRef="usertask2"></sequenceFlow>
<userTask id="usertask3" name="人事复核"></userTask>
<sequenceFlow id="flow3" sourceRef="usertask2"
targetRef="usertask3"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow4" sourceRef="usertask3"
targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="130.0"
y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="210.0"
y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="360.0"
y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
<omgdc:Bounds height="55.0" width="105.0" x="510.0"
y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="660.0"
y="160.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="165.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="210.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="315.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="360.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="465.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="510.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="615.0" y="177.0"></omgdi:waypoint>
<omgdi:waypoint x="660.0" y="177.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

3、Activiti如何使用

3.1 部署activity

Activiti 是一个工作流引擎(其实就是一堆 jar 包 API),业务系统使用 activiti 来对系统的业务流程进行自动化管理,为了方便业务系统访问(操作)activiti 的接口或功能,通常将 activiti 环境与业务系统的环境集成在一起。

3.2 流程定义

使用 activiti 流程建模工具(activity-designer)定义业务流程(.bpmn 文件) 。bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程。
如果使用其它公司开发的工作引擎一般都提供了可视化的建模工具(Process Designer)用于生
成流程定义文件,建模工具操作直观,一般都支持图形化拖拽方式、 多窗口的用户界面、 丰富的过
程图形元素、过程元素拷贝、粘贴、删除等功能。

3.3 流程定义部署

向 activiti 部署业务流程定义(.bpmn 文件)。使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件(一般情况还需要一块儿部署业务流程的图片.png)

3.4 启动一个流程实例(ProcessInstance)

启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响,就好比定义一个 java 类,实例化两个对象一样,部署的流程就好比 java 类,启动一个流程实例就好比 new 一个 java 对象。

3.5 用户查询待办任务(Task)

因为现在系统的业务流程已经交给 activiti 管理,通过 activiti 就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些 activiti帮我们管理了,而不像上边需要我们在 sql语句中的where条件中指定当前查询的状态值是多少。

3.6 用户办理任务

用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采购单创建后由部门经理审核,这个过程也是由 activiti 帮我们完成了,不需要我们在代码中硬编码指
定下一个任务办理人了。

3.6 流程结束

当任务办理完成没有下一个任务结点了,这个流程实例就完成了。

4、Activiti应用

4.1 基本使用

4.1.1 创建一个Maven项目,导入相关依赖
<properties>
    <slf4j.version>1.6.6</slf4j.version>
    <log4j.version>1.2.12</log4j.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-engine</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-model</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <!--bpmn 转换-->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-converter</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <!--bpmn json数据转换-->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-json-converter</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <!--bpmn 布局-->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-layout</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <!--activity云支持-->
    <dependency>
        <groupId>org.activiti.cloud</groupId>
        <artifactId>activiti-cloud-services-api</artifactId>
        <version>7.0.0.Beta1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.29</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- log start -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <!-- log end -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
</dependencies>
4…1.2 添加一个日志文件log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\log\activiti.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
4.1.3 添加Activiti配置文件

默认使用方式是在resources下创建activiti.cfg.xml,默认方式的名称是不能修改的

两种配置方式:是否单独配置数据源

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration" id="processEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc://mysql:activiti?characterEncoding=utf-8&amp;serverTimezone=Homgkong"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="XuHuan172."/>
    </bean>
</beans>
4.1.4 创建一个工具类,调用Activiti的工具类生成activiti需要的表结构
public class test01 {
    /**
     * 生成Activiti的相关表结构
     */
    @Test
    public void test(){
        //使用classpath下的activiti.cfg.xml中的配置来创建ProcessEngine对象
        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
        System.out.println(engine);
    }
}

创建表结构

创建表结构2

执行后的数据库结果展示:

表结构

4.2 表结构介绍

Activiti 的表都以 ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的 API 对应。

  • ACT_RE_* : RE表示 repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。_
  • _ACT_RU* : RU表示 runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。
  • ACT_HI_* : HI表示 history。 这些表包含历史数据,比如历史流程实例, 变量,任务等等。
  • ACT_GE_* : GE 表示 general。 通用数据, 用于不同场景下。

4.3 ProcessEngine的创建方式

ProcessEngine工作流引擎,相当于一个门面接口,通过 ProcessEngineConfiguration 创建 processEngine,通过
ProcessEngine 创建各个 service 接口。

前面我们使用的是默认的方式调用classpath下的配置文件

//使用classpath下的activiti.cfg.xml中的配置来创建ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();

我们还可以自定义的加载需要的配置文件

/**
* 自定义方式加载配置文件
*/
@Test
public void test02(){
    //首先创建ProcessEngineConfiguration对象
    ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
    //通过ProcessEngineConfiguration对象构建ProcessEngine对象
    ProcessEngine processEngine = configuration.buildProcessEngine();
}

4.4 Service

4.4.1 Service创建方式

通过 ProcessEngine 创建 Service, Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口。

RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
......
4.4.2 Service总览
类别说明
RepositoryServiceactiviti 的资源管理类
RuntimeServiceactiviti 的流程运行管理类
TaskServiceactiviti 的任务管理类
HistoryServiceactiviti 的历史管理类
ManagerServiceactiviti 的引擎管理类

加粗为常用的Service

4.5 绘制流程图

4.5.1 安装插件

18年版本之后idea没有actiBPM插件了,可以去官方网址搜查插件下载到本地导入进去

官方网址:https://plugins.jetbrains.com/plugin/7429-actibpm/versions

4.5.2 Palette(画板)

在 eclipse 或 idea 中安装 actiBPM 插件即可使用,画板中包括以下结点:
Connection—连接
Event—事件
Task—任务
Gateway—网关
Container—容器
Boundary event—边界事件
Intermediate event- -中间事件
流程图设计完毕保存生成.bpmn 文件。

4.5.3 流程绘制

流程图

指定流程的主键

流程主键指定

指定任务负责人

指定负责人

5、Activiti流程操作

5.1 流程的部署

5.1.1 单个文件部署
//1、获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
//2、获取RepositoryService进行部署
RepositoryService service = engine.getRepositoryService();
//3、使用RepositoryService进行部署操作
Deployment deployment = service.createDeployment()
    .disableSchemaValidation()
    .addClasspathResource("bpmn/evection.bpmn")   //添加bpmn资源
    .addClasspathResource("bpmn/evection.png")
    .name("出差申请流程")
    .deploy();
//输出流程的相关信息
System.out.println("流程的ID:"+deployment.getId());
System.out.println("流程的名字:"+deployment.getName());
5.1.2 部署zip文件
//定义zip文件的输入流
InputStream inputStream=this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");
//对inputStream做装饰
ZipInputStream zipInputStream =new ZipInputStream(inputStream);

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService service = engine.getRepositoryService();
Deployment deployment = service.createDeployment().addZipInputStream(zipInputStream).name("出差流程").deploy();
//输出流程的相关信息
System.out.println("流程的ID:"+deployment.getId());
System.out.println("流程的名字:"+deployment.getName());
5.1.3 操作数据表

流程定义部署后操作activiti中的三张表

act_re_procdef: 流程定义表,部署每个新的流程定义都会在这张表增加一条记录

在这里插入图片描述

act_re_deployment: 流程定义部署表,每部署一次就增加一条记录

act_ge_bytearray: 流程资源表,流程部署的bpmn文件和png图片会保存在该表zhong

在这里插入图片描述

5.2 启动流程实例

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService service = engine.getRuntimeService();
//根据流程定义的ID启动流程
String id="evection";
ProcessInstance processInstance = service.startProcessInstanceByKey(id);
//输出流程的相关信息
System.out.println("流程定义的ID:"+processInstance.getProcessDefinitionId());
System.out.println("流程实例的ID:"+processInstance.getId());
System.out.println("流程活动的ID:"+processInstance.getActivityId());

流程启动结果输出

5.3 任务查询

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
//任务查询需要获取一个TaskService对象
TaskService taskService = engine.getTaskService();
String assignee ="zhangsan";
//根据流程的key和任务负责人查询任务
List<Task> list = taskService.createTaskQuery()
    .processDefinitionKey("evection")
    .taskAssignee(assignee)
    .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());
}

任务查询结果

5.4 流程任务处理

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
Task task = taskService.createTaskQuery()
    .processDefinitionKey("evection")
    .taskAssignee("lisi")
    .singleResult();
//完成任务
taskService.complete(task.getId());

5.5 流程定义的查询

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
    .processDefinitionKey("evection")
    .orderByProcessDefinitionVersion()  //按照流程版本进行降序排序
    .desc()
    .list();

for (ProcessDefinition processDefinition : list) {
    System.out.println("流程定义的ID"+processDefinition.getId());
    System.out.println("流程定义的Name"+processDefinition.getName());
    System.out.println("流程定义的Key"+processDefinition.getKey());
    System.out.println("流程定义的version"+processDefinition.getVersion());
    System.out.println("流程部署的ID"+processDefinition.getDeploymentId());
}

流程定义信息查询

5.6 流程的删除

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
//如果该流程定义已经有流程实例启动,删除时会报错
repositoryService.deleteDeployment("30001");
//设置为true则为级联删除定义,即使有实例启动也可以删除
//repositoryService.deleteDeployment("30001",true);

在这里插入图片描述

5.7 流程资源的下载

//1.获取ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
//2.获取RepositoryService对象
RepositoryService repositoryService = engine.getRepositoryService();
//3.得到查询器
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
    .processDefinitionKey("evection")
    .singleResult();
//4.获取流程部署ID
String deploymentId = definition.getDeploymentId();
//5.通过RepositoryService相关方法获取图片信息和BPMN信息
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, definition.getDiagramResourceName());
InputStream bpmInput = repositoryService.getResourceAsStream(deploymentId, definition.getResourceName());
//6.文件的保存
File png =new File("D:/evection.png");
File bpm =new File("D:/evection.bpmn");
OutputStream pngOutput = new FileOutputStream(png);
OutputStream bpmOutput = new FileOutputStream(bpm);

IOUtils.copy(pngInput,pngOutput);
IOUtils.copy(bpmInput,bpmOutput);

pngInput.close();
bpmInput.close();
pngOutput.close();
bpmOutput.close();

5.8 流程历史信息查询

ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = engine.getHistoryService();
//获取actinst表查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
List<HistoricActivityInstance> list = instanceQuery.processDefinitionId("evection:1:30004")
    .orderByHistoricActivityInstanceStartTime().desc()
    .list();

//输出查询结果
for (HistoricActivityInstance instance : list) {
    System.out.println("流程定义ID:"+instance.getProcessDefinitionId());
    System.out.println("活动ID:"+instance.getActivityId());
    System.out.println("任务ID:"+instance.getTaskId());
    System.out.println("活动名称:"+instance.getActivityName());
    System.out.println("活动负责人:"+instance.getAssignee());
    System.out.println("活动类型:"+instance.getActivityType());
    System.out.println("================================================");
}

在这里插入图片描述

6、总结

Activiti工作流学习的要点

1个插件

在IDEA中安装Activiti插件,可以绘制Activiti工作流图

1个引擎

ProcessEngine对象,这是Activiti工作的核心。负责生成流程运行时的各种实例及数据、监控和管理流程的运行。

所有的操作都是从获取引擎开始的,所以一般会把引擎作为全局变量

ProcessEngine processEngine =ProcessEngines.getDefaultProcessEngine();

1个配置文件

activiti.cfg.xml。Activiti核心配置文件,Activiti工作流核心,配置流程引擎创建工具的基本参数和数据库连接池参数

5种数据库表

Activiti的后台是有数据库的支持,所有的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。用途也和服务的API对应。

ACT_RE_*: ‘RE’表示repository。 这个前缀的表包含了流程定义和流程静态资源(图片,规则,等等)。

ACT_RU_*: ‘RU’表示runtime。 这些运行时的表,包含流程实例,任务,变量,异步任务,等运行中的数据。 Activiti只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。 这样运行时表可以一直很小速度很快。

ACT_ID_*: ‘ID’表示identity。 这些表包含身份信息,比如用户,组等等。

ACT_HI_*: ‘HI’表示history。 这些表包含历史数据,比如历史流程实例,变量,任务等等。

ACT_GE_*: 通用数据,用于不同场景下,如存放资源文件。

5项Service

不同的Service类对应不同的功能。

7项基本操作

设计流程图(各种组件,如连线、用户任务、网关)

流程定义增删改查

流程变量增删改查

启动流程定义

任务增删改查

完成任务

历史信息查询

Activiti进阶篇

1、流程实例

1.1 什么是流程实例

1.2 业务管理

1.3 流程实例的挂起和激活

在实际场景中可能由于流程变更需要将当前运行的流程暂停而不是删除,流程暂停后将不能继续执行。

1.3.1全部流程挂起

操作流程的定义为挂起状态,该流程定义下边所有的流程实例全部暂停。该流程定义将不允许启动新的流程实例,同时该流程定义下的所有的流程实例都将全部挂起暂停执行。

挂起后字段的变化

在这里插入图片描述

流程挂起后再操作出现的异常

在这里插入图片描述

1.3.2 单个实例挂起

操作流程实例对象,针对单个流程执行挂起操作,某个流程实例挂起则此流程不再继续执行,当前流程定义的其他流程实例是不受干扰的。完成该流程实例的当前任务会抛异常

在这里插入图片描述

2、个人任务

2.1 分配任务责任人

2.1.1 固定分配

在画图时,填写Assignee项为负责人

在这里插入图片描述

2.1.2 表达式分配

表达式支持解析基础类型,bean、list、array和map,也可以作为条件判断如下:${order.price>100&&order.price<250}

UEL-value

在这里插入图片描述

/**
* 创建一个流程实例
* 给流程定义中的UEL表达式赋值
*/
@Test
public void test02(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    RuntimeService runtimeService = engine.getRuntimeService();
    //设置assignee的值
    Map<String,Object> map =new HashMap<String, Object>();
    map.put("assignee1","张好好");
    map.put("assignee2","赵盼儿");
    map.put("assignee3","宋引章");
    map.put("assignee4","二娘");
    //创建一个流程实例
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("evection-uel",map);
    //输出流程的相关信息
    System.out.println("流程定义的ID:"+processInstance.getProcessDefinitionId());
    System.out.println("流程实例的ID:"+processInstance.getId());
    System.out.println("流程活动的ID:"+processInstance.getActivityId());
}

UEL-method

userBean是Spring容器的一个Bean,表示调用bean的getUserId()方法

UEL-value与UEL-method结合

再比如:
${ldapService.findManagerForEmployee(emp)}

ldapService 是 spring容器的一个bean,findManagerForEmplovee 是该bean的一个方法,emp 是activiti流程变量,emp作为参数传到IdapService.findManagerForEmployee方法中。
其它
表达式支持解析基础类型、 bean、list、array和map,也可作为条件判断。如下:
${order.price > 100 && order.price < 250}

2.1.3 监听器分配

可以使用监听器完成很多Activiti的业务流程

Event选项选择,编写

create:任务创建后触发

assignment:任务分配后触发

Delete:任务完成后触发

All:所有事件都触发

2.2 查询任务

查询任务负责人的待办任务
关联businessKey

在activiti 实际应用时。查询待办任务可能要显示出业务系统的一些相关信息。

比如:查询待审批出差任务列表需要将出差单的日期、出差天数等信息显示出来。出差天数等信息在业务系统中存在,而并没有在activiti 数据库中存在,所以是无法通过 activiti的 api查询到出差天数等信息。

实现:
在查询待办任务时,通过 businessKey (业务标识)关联查询业务系统的出差单表,查询出出差天数等信息。

2.3 办理任务

注意:在实际应用中,完成任务前需要校验任务的负责人是否具有该任务的办理权限。

/**
*完成任务,判断当前用户是否有权限
*/
@Test
public void completTaskO {
//任务id
string taskId = "15005";
//任务负责人
String assingee ="张三";
//获取processEngine
ProcessEngine processEngine = processEngines.getDefaultprocessEngine();
//创建Taskservice
Taskservice taskservice = processEngine.getTaskService(;
//完成任务前,需要校验该负责人可以完成当前任务
//校验方法:
//根据任务id和任务负责人查询当前任务,如果查到该用户有权限,就完成
Task task = taskservice.createTaskQuery o
.taskIdltaskId)
.taskAssignee(assingee).singleResult();
if(task != nu11){
	taskService.complete(taskId);System. out .print1n("完成任务");
}

3、流程变量

3.1 什么是流程变量

流程变是在activiti 中是一个非常重要的角色,流程运转有时需要靠流程变量,业务系统和activiti结合时少不了流程变量,流程变量就是activiti在管理工作流时根据管理需要而设置的变量。
比如:在出差申请流程流转时如果出差天数大于3天则由总经理审核,否则由人事直接审核,出差天数就可以设置为流程变量,在流程流转时使用。
注意:虽然流程变量中可以存储业务数据可以通过activiti的api查询流程变量从而实现查询业务数据,但是不建议这样使用,因为业务数据查询由业务系统负责,activiti设置流程变量是为了流程执行需要而创建。

3.2 流程变量类型

如果将pojo存储到流程变量中,必须实现序列化接口serializable,为了防止由于新增字段无法反序列化。需要生成serialVersionuID。

3.3 流程变量作业域

流程变量的作用域可以是一个流程实例(processInstance),或一个任务(task),或一个执行实例(execution)

3.3.1 globa变量

流程变量的默认作用域是流程实例。当一个流程变量的作用域为流程实例时,可以称为global变量

注意:
如: Global变量: userld(变量名) 、 zhangsan(变量值)
global变量中变量名不允许重复,设置相同名称的变量,后设置的值会覆盖前设置的变量值。

3.3.2 local变量

任务和执行实例仅仅是针对一个任务和一个执行实例范围,范围没有流程实例大,称为 local变量。
Local变量由于在不同的任务或不同的执行实例中,作用域互不影响,变量名可以相同没有影响。Local变量名也可以和global变量名相同,没有影响。

3.4 流程变量的使用方法

3.4.1 在属性上使用UEL表达式

可以在assignee处设置UEL表达式,表达式的值为任务的负责人,比如: ${(assignee},assignee就是一个流程变量名称。Activiti获取UEL表达式的值,即流程变量assignee的值,将assignee的值作为任务的负责人进行任务分配

3.4.2 在连线上使用UEL表达式

3.5 流程变量使用

3.5.1 需求
3.5.2 流程定义

先通过UEL-value来设置负责人

3.5.3 使用Global变量

pojo创建

流程的部署

设置流程变量

启动时设置流程变量(变量的作用域是整个流程实例)

/**
*启动流程实例,设置流程变量
*/
@Test
public void testo2(){
processEngine engine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeservice = engine.getRuntimeService();
//流程定义key
string key m "evection-variable";
//创建变量集合
Map<String,object> variables = new HashMapc>();
//创建出差对象POJ0
Evection evection = new Evection();
//设置出差天数
evection.setNum(4d);
//定义流程变量到集合中
variables. put ("evection" , evection);
//设置assignee的取值
variables.put ("assigneeo","张三1");
variables. put ("assignee1","李四1");
variables. put ("assignee2","王五1");
variables. put ("assignee3","赵财务1");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key,variables);
//输出信息
System.out . print1n("获取流程实例名称: "+processInstance.getName());
System.out.print1n("流程定义ID: " + processInstance.getProcessDefinitionId());
}

通过startProcessInstanceByKey方法设置流程变量的作用域是一个流程实例,流程变量使用Map存储,同一个流程实例map中的key相同,后者会覆盖前者

任务办理时设置流程变量(作用域是整个流程实例)
在完成任务时设置流程变量,该流程变量只有在该任务完成后其它结点才可使用该变量,如果设置的流程变量的key在流程实例中已存在相同的名字则后设置的变量替换前边设置的变量。
这里需要在创建出差单任务完成时设置流程变量

/**
*完成任务,判断当前用户是否有权限
*/
@Test
public void completTask(){
//任务id
string key = "myEvection2";
//任务负责人
string assingee =“张三";
//获取processEngine
processEngine processEngine = processEngines.getDefaultProcessEngine() ;
//创建TaskService
TaskService taskservice = processEngine.getTaskservice(;
//创建变量集合
Map<String, object> map = new HashMap<>;
//创建出差pojo对象
Evection evection = new Evection( ;
//设置出差天数
evection. setNum(2d);定义流程变量
map.put ("" evection" , evection) ;
l/
完成任务前,需要校验该负责人可以完成当前任务
//校验方法:
//根据任务id和任务负责人查询当前任务,如果查到该用户有权限,就完成
Task task = taskservice.createTaskQueryO
.processDefinitionKey(key)
.taskAssignee(assingee).singleResult(;
if(task != nu11){
//完成任务是,设置流程变量的值
taskservice.complete(task. gtId( ,map);System.out.print1n(""任务执行完成"");
}

4、组任务

4.1 需求

在流程定义中在任务结点的 assignee固定设置任务负责人,在流程定义时将参与者固定设置在.bpmn文件中,如果临时任务负责人变更则需要修改流程定义,系统可扩展性差。

针对这种情况可以给任务设置多个候选人。可以从候选人中选择参与者来完成任务。

4.2 设置任务候选人

在流程图的任务节点的配置中设置candidate-user(候选人),多个候选人之间用逗号分开。

<userTask activiti:candidateUsers="yy,jx" activiti:exclusive="true" id="_4" name="部门主管审批"/>

4.3 组任务

4.3.1 组任务办理流程

查询组任务

指定候选人,查询该候选人当前的待办任务,候选人不能立即办理任务

拾取任务

该组任务的所有候选人都能拾取

将候选人的组任务变成个人任务。原来的候选人就变成了该任务的负责人。

如果拾取后不想办理该任务,需要将已经拾取的个人任务归还到组里面,将个人任务变成组任务

查询个人任务

查询方式同个人任务部分,根据assignee查询用户负责的个人任务

办理个人任务

4.3.2 查询组任务

根据候选人查询组任务

/**
     * 查询组任务
     */
@Test
public void test03(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = engine.getTaskService();
    //候选人
    String candidateUser ="yy";
    String key ="evection-group";
    List<Task> list = taskService.createTaskQuery()
        .processDefinitionKey(key)
        .taskCandidateUser(candidateUser)
        .list();

    for (Task task : list) {
        System.out.println(task.getId());
        System.out.println(task.getName());
        System.out.println(task.getProcessInstanceId());
    }
}
4.3.3 拾取组任务

候选人员拾取组任务后该任务变为自己的个人任务

/**
     * 拾取任务
     */
@Test
public void test04(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = engine.getTaskService();
    String taskID ="87502";
    //候选人
    String user="yy";
    Task task = taskService.createTaskQuery()
        .taskId(taskID)
        .taskCandidateUser(user)
        .singleResult();
    if (task!=null){
        taskService.claim(taskID,user);
    }
    System.out.println("拾取成功");
}
4.3.4 查询个人待办任务
4.3.5 任务处理
/**
     * 流程任务处理
     */
@Test
public void test5(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = engine.getTaskService();
    Task task = taskService.createTaskQuery()
        .processDefinitionKey("evection-group")
        .taskAssignee("zhangsan")
        .singleResult();
    //完成任务
    taskService.complete(task.getId());
}
4.3.6 任务归还
/**
* 任务归还
*/
@Test
public void test07(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = engine.getTaskService();
    String taskID ="87502";
    String user="jx";
    Task task = taskService.createTaskQuery()
        .taskId(taskID)
        .taskAssignee(user)
        .singleResult();
    if (task!=null){
        //如果设置为null,任务没有负责人,则归还任务
        taskService.setAssignee(taskID,null);
    }
    System.out.println("任务归还成功");
}
4.3.7 任务交接

任务负责人将任务交给其他负责人来处理

/**
* 任务交接
*/
@Test
public void test06(){
    ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = engine.getTaskService();
    String taskID ="87502";
    String user="yy";
    Task task = taskService.createTaskQuery()
        .taskId(taskID)
        .taskAssignee(user)
        .singleResult();
    if (task!=null){
        //设置该任务新的负责人
        taskService.setAssignee(taskID,"jx");
    }
    System.out.println("任务交接成功");
}

5、网关

网关用来控制流程的流向

5.1 排他网关ExclusiveGateway

5.1.1 什么是排他网关

排他网关,用来在流程中实现决策。当流程执行到这个网关,所有分支都会判断条件是否为true,如果为true则执行该分支

注意:排他网关只会选择一个为true的分支执行。如果有两个分支条件都为true,排他网关会选择id值较小的一条分支去执行。

为什玄要用排他网关?
不用排他网关也可以实现分支,如:在连线的condition条件上设置分支条件。在连线设置condition条件的缺点:如果条件都不满足,流程就结束了(是异常结束)。如果使用排他网关决定分支的走向,如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVHVXjT3-1663994413815)(photo/activiti/排他网关图.png)]

5.1.2 流程定义
5.1.3 测试

排他异常

在这里插入图片描述

5.2 并行网关Parallelgateway

5.2.1 什么是并行网关

并行网关允许将流程分成多条分支,也可以把多条分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:

l fork分支:
并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。

l join汇聚:
所有到达并行网关,在此等待的进入分支,直到所有进入顺序流的分支都到达以后,流程就会通过汇聚网关。
注意,如果同一个并行网关有多个进入和多个外出顺序流,它就同时具有分支和汇聚功能。这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是,并行网关不会解析条件。即使顺序流中定义了条件,也会被忽略。例子:

5.3 包含网关InclusiveGateway

5.3.1 什么是包含网关

包含网关可以看做是排他网关和并行网关的结合体。
和排他网关一样,你可以在外出顺序流上定义条件,包含网关会解析它们。但是主要的区别是包含网关可以选择多于─条顺序流,这和并行网关一样。
包含网关的功能是基于进入和外出顺序流的:

  • 分支:
    所有外出顺序流的条件都会被解析,结果为true的顺序流会以并行方式继续执行,会为每个顺序流创建一个分支。

  • 汇聚:
    所有并行分支到达包含网关,会进入等待状态,直到每个包含流程token的进入顺序流的分支都到达。这是与并行网关的最大不同。换句话说,包含网关只会等待被选中执行了的进入顺序流。在汇聚之后,流程会穿过包含网关继续执行。

5.3.2流程定义:

出差申请大于等于3天需要由项目经理审批,小于3天由技术经理审批,出差申请必须经过人事经理审批。

5.4 事件网关EventGateway

事件网关允许根据事件判断流向。网关的每个外出顺序流都要连接到一个中间捕获事件。当流程到达一个基于事件网关,网关会进入等待状态:会暂停执行。与此同时,[会为每个外出顺序流创建相对的事件订阅。

事件网关的外出顺序流和普通顺序流不同,这些顺序流不会真的"执行”,相反它们让流程引擎去决定执行到事件网关的流程需要订阅哪些事件。要考虑以下条件:
1.事件网关必须有两条或以上外出顺序流;

⒉.事件网关后,只能使用intermediateCatchEvent类型(activiti不支持基于事件网关后连接ReceiveTask)3.连接到事件网关的中间捕获事件必须只有一个入口顺序流。

3.连接到事件网关的中间捕获事件必须只有一个入口顺序流。

5.4.1 流程定义

在这里插入图片描述

Activiti整合篇

1、Spring整合

导入相关依赖

添加整合的配置文件

<?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:tx="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!--数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName"value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti-spring?nullCatalogMeansCurrent=true&amp;characterEncoding=utf-8&amp;serverTimezone=Hongkong"/>
        <property name="username" value="root"/>
        <property name="password" value="XuHuan172."/>
        <property name="maxActive" value="3"/>
        <property name="maxIdle" value="1"/>
    </bean>
    <!--工作流引擎配置bean-->
    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <!--        数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--        使用Spring事务管理器-->
        <property name="transactionManager" ref="transactionManager"/>
        <!--        数据库策略-->
        <property name="databaseSchemaUpdate" value="drop-create"/>
    </bean>
    <!--    流程引擎-->
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
    </bean>
    <!--    资源服务service-->
    <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService"/>
    <!--    流程运行service-->
    <bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService"/>
    <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService"/>
    <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/>
    <!--事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--传播行为-->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="find*" propagation="SUPPORTs" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTs" read-only="true" />
        </tx:attributes>
    </tx:advice>
</beans>

创建测试类测试

2、SpringBoot整合

导入相关依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring-boot-starter -->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring-boot-starter</artifactId>
        <version>7.1.0.M6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
        <version>2.7.2</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.27</version>
    </dependency>

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
</dependencies>
修改配置文件
# application.properties
# 配置Spring的数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/activiti-springboot?nullCatalogMeansCurrent=true&characterEncoding=utf-8&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=XuHuan172.

# activiti的配置
# 1.false:默认值。activiti在启动时,对比数据库表中保存的版本如果没有或者版本不匹配,将抛出异常
# 2.true:activiti会对数据库中所有表进行更新操作。如果表不存在则自动创建
# 3.create_drop:在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
# 4.drop-create:在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
spring.activiti.database-schema-update=true

# 检查历史表是否存在,Activiti7中默认是没有开启数据库历史记录的,启动数据库历史记录
spring.activiti.db-history-used=true
# 记录历史等级,可配置的历史级别有none,acrivity,audit,full
# none:不保存任何的历史数据,因此在流程执行过程中,这是最高效的
# activity:级别高于none,保存流程实例与流程行为,其他数据不保存
# audit:除activity级别会保存数据外,还会保存全部的流程任务及其属性、audit为history的默认值
# full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等
spring.activiti.history-level=full

# 校验流程文件,默认校验resources下的processes文件夹里的流程文件
spring.activiti.check-process-definitions=true
# application.yaml
project:
  manifest:
    file:
      path: classpath:/default-project.json
整合SpringSecurity

因为Activiti和SpringBoot整合后,默认情况下,集成了SpringSecurity安全框架,需要准备SpringSecurity的相关配置信息,添加一个SpringSecurity的工具类

SecurityUtil

package com.yy.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * @author YY
 */
@Component
public class SecurityUtil {

    private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    @Autowired
    @Qualifier("myUserDetailsService")
    private UserDetailsService userDetailsService;

    public void logInAs(String username) {

        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
        }
        logger.info("> Logged in as: " + username);
        SecurityContextHolder.setContext(
            new SecurityContextImpl(
                new Authentication() {
                    @Override
                    public Collection<? extends GrantedAuthority> getAuthorities() {
                        return user.getAuthorities();
                    }

                    @Override
                    public Object getCredentials() {
                        return user.getPassword();
                    }

                    @Override
                    public Object getDetails() {
                        return user;
                    }

                    @Override
                    public Object getPrincipal() {
                        return user;
                    }

                    @Override
                    public boolean isAuthenticated() {
                        return true;
                    }

                    @Override
                    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {

                    }

                    @Override
                    public String getName() {
                        return user.getUsername();
                    }
                }));
        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    }
}

SpringSecurityConfiguration

package com.yy.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;


/**
 * @author YY
 */
@Configuration
public class SpringSecurityConfiguration {

    Logger logger= LoggerFactory.getLogger(SpringSecurityConfiguration.class);
    @Bean
    public UserDetailsService myUserDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        //这里添加用户,后面处理流程时用到的任务负责人,需要添加再这里
        String[][] userGroupsAndRoles ={
            {"jack","password","ROLE_ACTIVITI_USER","GROUP_activitiTeam"},
            {"rose","password","ROLE_ACTIVITI_USER","GROUP_activitiTeam"},
            {"tom","password","ROLE_ACTIVITI_USER","GROUP_activitiTeam"},
            {"other","password","ROLE_ACTIVITI_USER","GROUP_otherTeam"},
            {"system","password","ROLE_ACTIVITI_USER"},
            {"admin","password","ROLE_ACTIVITI_ADMIN"},
        };
        for (String[] user : userGroupsAndRoles) {
            List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
            logger.info("> Registering new user:"+user[0]+"with the following Authorities["+authoritiesStrings+"]");
            inMemoryUserDetailsManager.createUser(new User(user[0],passwordEncoder().encode(user[1]),
                                                           authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
        }
        return inMemoryUserDetailsManager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
创建bpmn文件

创建一个简单的bpmn文件,并设置任务的用户组,CandidateGroups,内容要与SpringSecurity的配置文件中配置的用户组的名称要保持一致,可以填写activitiTeam或者otherTeam。当不确定到底由谁来负责当前的任务的时候,只要是Groups内的用户都可以拾取这个任务

Activiti7中可以自动部署流程,前提是在resources目录下,创建一个新目录processes放置bpmn文件

package com.yy;

import com.yy.utils.SecurityUtil;
import org.activiti.api.process.model.ProcessDefinition;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.runtime.TaskRuntime;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ActivitiSpringbootApplicationTests {

    @Autowired
    private ProcessRuntime processRuntime;

    @Autowired
    private TaskRuntime taskRuntime;

    @Autowired
    private SecurityUtil securityUtil;
    @Test
    void contextLoads() {
        System.out.println(taskRuntime);
    }

    /**
     * 查询流程的定义
     */
    @Test
    public void test02(){
        securityUtil.logInAs("system");
        Page<ProcessDefinition> processDefinitionPage = processRuntime.processDefinitions(Pageable.of(0,10));
        System.out.println("可用的流程定义数量:"+processDefinitionPage.getTotalItems());

        for (ProcessDefinition processDefinition : processDefinitionPage.getContent()) {
            System.out.println("流程定义:"+processDefinition);
        }
    }

    /**
     * 启动流程实例
     */
    @Test
    public void test04(){
        securityUtil.logInAs("system");
        ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder.start().withProcessDefinitionKey("evection-springboot").build());

        System.out.println("流程实例id:"+processInstance.getId());
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值