工作流概述
在一个公司中,每一项业务的开始和结束,都可以理解为一个工作流,例如,公司的费用报销的基本流程如下:
如图所示的工作流:员工先提出费用报销申请,提交该申请给部门领导,部门领导审批后,再提交给财务部门审批,审批完成后,通知提出申请的员工可以报销,即报销流程结束。整个步骤按照正常工作方式一步步完成,这就是一个简单而又完整的工作流工作流可以理解为从开始节点发起流程,然后经过其中多个节点,完成动作,最后到结束节点的整个过程
工作流系统
一个软件系统中如果具有工作流系统,我们就把它称为工作流系统。一个系统中的工作流的功能是对系统业务流程进行自动化管理。一个软件系统的核心根本上还是业务流程,工作流只是协助业务流程的管理,即使没有工作流业务一样能照常展开,只不过使用工作流可以更好地管理业务流程,提高系统的扩展性
工作流的具体应用有:
- 关键业务类:订单、报价处理、合同审核、供应链管理等等
- 行政管理类:出差申请、请假申请、日报周报等等
- 人事管理类:员工培训安排、变动处理等等
- 财务相关类:收付款处理、报销处理、预算申请等等
- 客户服务类:客户信息管理、客户投诉、请求处理、售后服务等等
工作流引擎
在没有工作流引擎之前,为了实现流程控制,通常的做法是采用状态字段的值来跟踪流程的变化。例如设立一个字段,初始值为 0,经过某些流程后变成 1,变成 2,最后根据这个值来判断状态,给出相应的处理
很明显,这样一来工作的流程会和业务高度耦合,当我们的流程发生变更时,所编写的代码也必须做出调整。如果有一样工具能帮助我们管理工作流,并做到当业务流程变化之后,程序不需要跟着发生变化,那么我们的业务系统的适应能力将会有大幅提升
Activit7 是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言 BPMN2.0 进行定义,业务流程将按照预定义的流程执行。系统的流程由 activit 管理,从而减少业务系统由于流程变化而导致的工作量,提高系统健壮性
官方网址: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">
<span class="hljs-comment"><!-- 默认 id 对应的值为 processEngineConfiguration --></span>
<span class="hljs-comment"><!-- processEngine Activiti 的流程引擎 --></span>
<span class="hljs-tag"><<span class="hljs-name">bean</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"processEngineConfiguration"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"</span>></span>
<span class="hljs-comment"><!-- 配置数据库相关信息 --></span>
<span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jdbcDriver"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"com.mysql.cj.jdbc.Driver"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jdbcUrl"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"jdbc:mysql://localhost:3306/activit?useSSL=false<span class="hljs-symbol">&amp;</span>serverTimezone=UTC<span class="hljs-symbol">&amp;</span>allowPublicKeyRetrieval=true<span class="hljs-symbol">&amp;</span>useAffectedRows=true"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jdbcUsername"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"root"</span>/></span>
<span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"jdbcPassword"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"123"</span>/></span>
<span class="hljs-comment"><!-- activiti 数据库表处理策略,true 为如果数据库中已存在相应的表,则直接使用,否则创建 --></span>
<span class="hljs-tag"><<span class="hljs-name">property</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"databaseSchemaUpdate"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"true"</span>/></span>
<span class="hljs-tag"></<span class="hljs-name">bean</span>></span>