工作流概述
在一个公司中,每一项业务的开始和结束,都可以理解为一个工作流,例如,公司的费用报销的基本流程如下:
如图所示的工作流:员工先提出费用报销申请,提交该申请给部门领导,部门领导审批后,再提交给财务部门审批,审批完成后,通知提出申请的员工可以报销,即报销流程结束。整个步骤按照正常工作方式一步步完成,这就是一个简单而又完整的工作流工作流可以理解为从开始节点发起流程,然后经过其中多个节点,完成动作,最后到结束节点的整个过程
工作流系统
一个软件系统中如果具有工作流系统,我们就把它称为工作流系统。一个系统中的工作流的功能是对系统业务流程进行自动化管理。一个软件系统的核心根本上还是业务流程,工作流只是协助业务流程的管理,即使没有工作流业务一样能照常展开,只不过使用工作流可以更好地管理业务流程,提高系统的扩展性
工作流的具体应用有:
- 关键业务类:订单、报价处理、合同审核、供应链管理等等
- 行政管理类:出差申请、请假申请、日报周报等等
- 人事管理类:员工培训安排、变动处理等等
- 财务相关类:收付款处理、报销处理、预算申请等等
- 客户服务类:客户信息管理、客户投诉、请求处理、售后服务等等
工作流引擎
在没有工作流引擎之前,为了实现流程控制,通常的做法是采用状态字段的值来跟踪流程的变化。例如设立一个字段,初始值为 0,经过某些流程后变成 1,变成 2,最后根据这个值来判断状态,给出相应的处理
很明显,这样一来工作的流程会和业务高度耦合,当我们的流程发生变更时,所编写的代码也必须做出调整。如果有一样工具能帮助我们管理工作流,并做到当业务流程变化之后,程序不需要跟着发生变化,那么我们的业务系统的适应能力将会有大幅提升
Activit7 是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言 BPMN2.0 进行定义,业务流程将按照预定义的流程执行。系统的流程由 activit 管理,从而减少业务系统由于流程变化而导致的工作量,提高系统健壮性
官方网址:https://www.activiti.org/
BPM
BPM(Business Process Management)即业务流程管理,是一种规范化的构造端到端的业务流程,以持续提高组织业务效率
BPM 软件就是根据企业中业务环境的变化,推进人与人之间、人与系统之间以及系统与系统之间的整理及调整的经营方法与解决方案的 IT 工具。使用 BPM 软件对企业内部及外部的业务流程的整个生命周期进行建模、自动化、管理监控和优化,可以降低企业成本,提高利润
BPMN(Business Process Model AndNotation)即业务流程模型和符号,是一套标准的业务流程建模符号,使用 BPMN 提供的符号可以创建业务流程。Activit 就是使用 BPMN 进行流程建模、流程执行管理的
BPMN2.0 是业务流程建模符号 2.0 的缩写,它由 Business Process Management Initiative 这个非营利协会创建并不断发展。BPMN2.0 是使用一些符号来明确业务流程设计流程图的一套符号规范,能增进业务建模时的沟通效率。目前 BPMN2.0 是最新的版本,它用于在 BPM 上下文中进行布局和可视化的沟通
BPMN2.0 的基本符号主要包含:
-
事件 Event
-
活动 Activity
活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程;其次,你还可以为活动指定不同的类型。常见活动如下:
-
网关 GateWay
网关用来处理决策,有几种常用网关需要了解:
-
排它网关
只有一条路径会被选择。流程执行到该网关时,按照输出流的顺序逐个计算,当条件的计算结果为 true 时,继续执行当前网关的输出流;如果多条线路计算结果都是 true,则会执行第一个值为 true 的线路。如果所有网关计算结果没有 true,则引擎会抛出异常。排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流
-
并行网关
所有路径会被同时选择
-
拆分:并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路
-
合并:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行
-
-
包容网关
可以同时执行多条线路,也可以在网关上设置条件
- 拆分:计算每条线路上的表达式,当表达式计算结果为 true 时,创建一个并行线路并继续执行
- 合并:所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行
-
事件网关
专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态
-
-
流向 Flow
流是连接两个流程节点的连线,常见的流向包含以下几种:
Activit 部署流程
Activiti 是一个工作流引擎,业务系统通过访问 Activiti 所提供的接口,就可以很方便的操作流程的相关数据,把工作流环境与业务系统环境集成在一起
首先使用 Activiti 流程建模工具(Activity-Designer)来通过 BPMN2.0 符号来定义业务流程,生成一个 .bpmn 文件。.bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程
得到 .bpmn 文件后,使用 Activiti 提供的 Api 把流程定义内容存储起来。接着启动一个流程实例(ProcessInstance),表示一次业务流程开始运行
既然系统的业务流程已经交给 Activiti 管理,那么通过 Activiti 就可以查看当前流程执行到哪一步,当前用户需要办理什么任务。这些操作由 activiti 帮我们管理,而不需要开发人员自己编写 sql 语句查询
用户查询到待办任务后,就可以开始办理某个任务了。如果这个任务办理完成后,还需要其它用户继续办理,比如采购单创建后要交由部门经理审核,那么这个过程也是由 Activiti 帮我们完成了,总之流程可以一直走下去,直到没有下一个任务结点,那么这个流程实例也就完成了
Activit 使用
1. 安装数据库
需要注意的是,Activiti 运行必须要有数据库的支持,支持的数据库有:h2、mysql、oracle、postgres、mssql、db2
2. Activit 流程定义工具插件安装
安装插件想必不用我多说,搜索 actiBPM 插件,它就是 Activit Designer 的 IDEA 版本,点击 Install 安装即可。但由于笔者使用的是 IDEA 2020.3 版本没有找到这个插件,猜测是不再支持了,只好退而求其次,选择了一款名为 Activiti BPMN Visualizer 的插件
3. 创建一个 maven 工程并添加相关依赖
首先需要在 Java 工程中ProcessEngine 所需要的依赖,包括:
- activiti 的相关包
- 其他工具包,如 mybatis、alf4j、log4j 等
- activiti 依赖的 spring 包
- mysql 数据库驱动
- 第三方数据连接池 dbcp
- 单元测试 Junit
<properties>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
<activiti.version>7.0.0.Beta1</activiti.version>
</properties>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 模型处理 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn json数据转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 布局 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- activiti 云支持 -->
<dependency>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!-- mybatis -->
<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>
<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>
</dependencies>
4. log4j 日志配置
既然使用了 log4j,那就把日志也做个配置,在 resource 目录下创建一个 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=f:\act\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
5. 编写 activit 配置文件
我们使用 activit 提供的默认方式来创建 mysql 表,默认方式要求在 resources 目录下创建 activit.cfg.xml 文件,路径和文件名都不能修改。默认方式中 activiti.cfg.xml 里面 bean 的名字要叫 processEngineConfiguration,不可以修改
<?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">
<!-- 默认 id 对应的值为 processEngineConfiguration -->
<!-- processEngine Activiti 的流程引擎 -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 配置数据库相关信息 -->
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activit?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&useAffectedRows=true"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="123"/>
<!-- activiti 数据库表处理策略,true 为如果数据库中已存在相应的表,则直接使用,否则创建 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
也可以使用连接池来提高性能
<?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">
<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/activit?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true&useAffectedRows=true"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
<property name="maxActive" value="3"/>
<property name="maxIdle" value="1"/>
</bean>
<!-- 默认 id 对应的值为 processEngineConfiguration -->
<!-- processEngine Activiti 的流程引擎 -->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 引入上面配置好的链接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- activiti 数据库表处理策略,true 为如果数据库中已存在相应的表,则直接使用,否则创建 -->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
6. 编写 Java 类生成表
创建一个测试类,调用 activiti 的工具类,生成 acitivti 需要的数据库表。直接使用 activiti 提供的工具类 ProcessEngines,会默认读取 classpath 下的 activiti.cfg.xml 文件,读取其中的数据库配置,创建 ProcessEngine,在创建 ProcessEngine 时会自动创建表。
public class testCreate {
@Test
public void testCreateTable() {
// 读取 activiti.cfg.xml 配置文件,创建 ProcessEngine 的同时会创建表
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
}
这种方式默认读取 resource 目录下的 activiti.cfg.xml 配置文件,我们也可以自定义配置文件,并指定路径进行读取
// 先构建ProcessEngineConfiguration
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 通过 ProcessEngineConfiguration 创建 ProcessEngine,此时会创建数据库
ProcessEngine processEngine = configuration.buildProcessEngine();
在测试程序执行过程中,idea 的控制台会输出日志,说明程序正在创建数据表。执行完成后我们查看数据库, 创建了 25 张表,结果如下:
至此·,我们就完成了 activiti 运行需要的数据库和表的创建
Activiti 表结构
Activiti 的运行支持必须要有这 25 张表的支持,主要是在业务流程运行过程中,记录参与流程的用户主体,用户组信息,以及流程的定义,流程执行时的信息,和流程的历史信息等等
1. 表的命名规则和作用
观察创建的表,我们发现 Activiti 的表都以 act_ 开头,紧接着是表示表的用途的两个字母标识,也和 Activiti 所提供的服务的 API 对应:
- ACT_RE:RE 表示 repository,这个前缀的表包含了流程定义和流程静态资源 (图片、规则、等等)
- ACT_RU:RU 表示 runtime,这些表运行时,会包含流程实例、任务、变量、异步任务等流程业务进行中的数据。Activiti 只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这样表就可以一直保持很小的体积,并且速度很快
- ACT_HI:HI 表示 history,这些表包含一些历史数据,比如历史流程实例、变量、任务等等
- ACT_GE:GE 表示 general,通用数据
2. Activiti 数据表介绍
表分类 | 表名 | 解释 |
---|---|---|
一般数据 | ||
[ACT_GE_BYTEARRAY] | 通用的流程定义和流程资源 | |
[ACT_GE_PROPERTY] | 系统相关属性 | |
流程历史记录 | ||
[ACT_HI_ACTINST] | 历史的流程实例 | |
[ACT_HI_ATTACHMENT] | 历史的流程附件 | |
[ACT_HI_COMMENT] | 历史的说明性信息 | |
[ACT_HI_DETAIL] | 历史的流程运行中的细节信息 | |
[ACT_HI_IDENTITYLINK] | 历史的流程运行过程中用户关系 | |
[ACT_HI_PROCINST] | 历史的流程实例 | |
[ACT_HI_TASKINST] | 历史的任务实例 | |
[ACT_HI_VARINST] | 历史的流程运行中的变量信息 | |
流程定义表 | ||
[ACT_RE_DEPLOYMENT] | 部署单元信息 | |
[ACT_RE_MODEL] | 模型信息 | |
[ACT_RE_PROCDEF] | 已部署的流程定义 | |
运行实例表 | ||
[ACT_RU_EVENT_SUBSCR] | 运行时事件 | |
[ACT_RU_EXECUTION] | 运行时流程执行实例 | |
[ACT_RU_IDENTITYLINK] | 运行时用户关系信息,存储任务节点与参与者的相关信息 | |
[ACT_RU_JOB] | 运行时作业 | |
[ACT_RU_TASK] | 运行时任务 | |
[ACT_RU_VARIABLE] | 运行时变量 |
Activiti 体系架构
完成了 Activiti 数据库表的生成,我们就可以在 Java 代码中调用 Activiti 的工具类,下面来了解 Activiti 的类关系
1. 类关系图
在新版本中,IdentityService、FormService 这两个 Serivce 都已经删除了,所以后面我们对于这两个 Service 也不讲解了,但老版本中还是有这两个 Service,有需要可以自行了解一下
2. Service 服务接口
Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用这些接口就可以操作服务对应的表
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
简单介绍一下各个 Service 的实现类:
-
RepositoryService
Activiti 的资源管理类,该服务负责部署流程定义,管理流程资源。在使用 Activiti 时,一开始需要先完成流程部署,即将使用建模工具设计的业务流程图通过 RepositoryService 进行部署
-
RuntimeService
Activiti 的流程运行管理类,用于开始一个新的流程实例,获取关于流程执行的相关信息。流程定义用于确定一个流程中的结构和各个节点间行为,而流程实例则是对应的流程定义的一个执行,可以理解为 Java 中类和对象的关系
-
TaskService
Activiti 的任务管理类,用于处理业务运行中的各种任务,例如查询分给用户或组的任务、创建新的任务、分配任务、确定和完成一个任务
-
HistoryService
Activiti 的历史管理类,可以查询历史信息。执行流程时,引擎会保存很多数据,比如流程实例启动时间、任务的参与者、完成任务的时间、每个流程实例的执行路径等等。这个服务主要通过查询功能来获得这些数据
-
ManagementService
Activiti 的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于 Activiti 系统的日常维护