一、acitiviti7环境
1.1 数据库表结构
表分类 | 表名 | 解释 |
---|---|---|
一般数据 | ||
ACT_GE_BYREARRAY | 存储通用的的流程定义和流程资源 | |
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_DEADLETTER_JOB | 无法执行工作表: 如果一个任务执行了很多次,都无法执行,那么这个任务会写入 | |
ACT_RU_EVENT_SUBSCR | 运行时事件 | |
ACT_RU_EXECUTION | 运行时流程执行实例 | |
ACT_RU_INDETITYLINK | 运行时用户关系信息,存储任务节点与参与者相关信息 | |
ACT_RU_INTEGRATION | 运行时积分表 | |
ACT_RU_JOB | 运行时定时任务作业 | |
ACT_RU_SUSPENDED_JOB | 暂停的工作,流程中有一个定时任务,如果把这个任务停止工作了,这个任务会写入 | |
ACT_RU_TASK | 运行时任务 | |
ACT_RU_TIMER_JOB | 运行时定时器作业表 | |
ACT_RU_VARIABLE | 运行时变量表 | |
其他表 | ||
ACT_EVT_LOG | 事件日志 | |
ACT_PROCDEF_INFO | 流程定义的动态变更信息 |
1.2 命名规则与作用
RE : 表示repository,包含流程定义与流程静态资源(图片、规则等)
RU : 表示runtime,包含流程实例,任务,变量,异步任务等运行中的数据,activiti只在流程实例执行过程中保存这些数据,在流程结束是会删除。表小速度快
HI : 表示history,包含历史数据,比如历史流程实例,变量任务等,activiti默认提供了四种历史级别
- none 不保存任何历史信息,可以提高系统性能
- activity 保存所有的流程实例、任务、活动信息
- audit 保存所有的流程实例、任务、活动、表单属性
- full 最完整的历史记录,除audit级别的信息外还可以保存详细,例如流程变量
GE : 表示general,通用数据
ID : 如果你生成的表结构有的这类表的话,这些表中保存的都是身份信息,如用户和组以及两者之间的关系。如果Activiti被集成在某一系统当中的话,这些表可以不用,可以直接使用现有系统中的用户或组信息。
- ACT_ID_USER 用户信息
- ACT_ID_MEMBERSHIP 用户与分组对应信息
- ACT_ID_INFO 用户组信息拓展表
- ACT_ID_GROUP 用户组信息
1.3 配置文件
二、Service服务接口
Service是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用这些接口可以就是操作服务对应的数据表.可 通过ProcessEngine创建Service
// 方式一:使用classpath下的activiti.cfg.xml中的配置创建processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//方式二:先构建ProcessEngineConfiguration,再创建ProcessEngine
ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
ProcessEngine processEngine = configuration.buildProcessEngine();
// 获取service
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
service名称 | service作用 |
---|---|
RepositoryService | activiti的资源管理类 |
RuntimeService | activiti的流程运行管理类 |
TaskService | activiti的任务管理类 |
HistoryService | activiti的历史管理类 |
ManagerService | activiti的引擎管理类 |
2.1 RepositoryService
- 流程定义部署,是activiti的资源管理类,提供了管理和控制流程发布包和流程定义的操作。
- 查询引擎中的发布包和流程定义。
- 暂停或激活发布包,对应全部和特定流程定义,类似启用禁用
- 获得流程定义的pojo版本, 可以用来通过java解析流程
2.2 RuntimeService
- Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息,我们可以通过runtimeService来开启一个流程实例,一个流程实例对应多个流程节点,比如小王发起一个请假单,就是一个流程实例,对应的经理审核、人力审核就是流程节点
TaskService
- Activiti的任务管理类。可以从这个类中获取任务的信息
2.3 HistoryService
- Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者,
完成任务的时间,每个流程实例的执行路径,等等。 这个服务主要通过查询功能来获得这些数据
2.4 ManagementService
- Activiti的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用, 主要用于
Activiti 系统的日常维护
三、流程符号
3.1 事件 Event
3.2 活动 Activity
活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程; 其次,你还可以为活动指定不同的类型。常见活动如下:
3.3 网关 GateWay
网关用来处理决策
3.3.1 排他网关
- 只有一条路径会被选择,流程执行到该网关时,按照输出流的顺序逐个计算,当计算结果为true时继续执行当前网关输出流
- 若多个路径的计算结构都是true,则会执行第一个值为true的路线,如果没有true,则抛出异常。
- 排他网关需要和条件顺序流结合使用,default 属性指定默认顺序流,当所有的条件不满足时会执行默认顺序流
3.3.2 并行网关
- 所有路径被同时选择
- 拆分,并行执行所有输出顺序流,为每一条顺序流创建一个并行执行线路
- 合并,所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行
3.3.3 包容网关
- 可以同时执行多条线路,也可以在网关上设置条件
- 拆分,计算每条线路上的表达式,当表达式计算结果为true时,创建一个并行线路并继续执行
- 合并,所有从并行网关拆分并执行完成的线路均在此等候,直到所有的线路都执行完成才继续向下执行
3.3.4 事件网关
- 专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态
3.4 流程 Flow
流是连接两个流程节点的连线
3.5 Bpmn建模符号
名称 | 描述 |
---|---|
UserTask | 用户任务,即用户操作的任务 |
ScriptTask | 脚本任务,脚本任务是一个自动化活动。当一个流程执行到达脚本任务时,执行相应的脚本。Activiti脚本任务比较少用,脚本任务通常是用在当前的监听器或者监听服务类都不能知足的情形下面,或者说后期系统维护,忽然在不想改动系统的状况下须要对流程作一些适当的改变。仅仅是几个变量或者仅仅是一个计算公式等等。这个时候可使用脚本任务 |
ServiceTask | 服务任务不同于用户任务(需人工处理),服务任务一般是一段可自动执行的任务而无需人工干预。 |
MailTask | 允许通过自动邮件服务任务来增强业务流程,这些任务将电子邮件发送给一个或多个收件人,多个人以逗号隔开,包括抄送,密件抄送,支持发送HTML内容等。请注意,邮件任务不是 BPMN 2.0 的正式任务规格(因此没有专用图标)。因此,在Activiti中,邮件任务也是serviceTask,只是使用activiti:type="mail"属性来表示此服务任务为邮件任务。 |
ManualTask | 手工任务就是一个自动执行的过程。手动任务几乎不在程序中做什么事情,只是在流程的历史中留下一点痕迹,表明流程是走过某些节点的。而且这个任务是无法用taskservice查询到的。手工任务就是一个直接通过的任务。可以使用它来自动执行一些可以直接通过的任务。 |
ReceiveTask | 接受任务其实和Activiti的手动任务是差不多的,不过手动任务是直接通过,而接受任务则在任务启动到该节点的时候停下来等待信号。当任务接收到信号的时候,该流程就会继续往下执行。 |
BusinessRuleTask | 规则任务可以通过制定一系列的规则来实现流程自动化 |
CallActivityTask | 唤醒(调用)子流程,主流程走到某个节点后唤醒其它子流程,当子流程走完后,主流程继续走。其中子流程可以作为一个独立的流程启动互不影响 |
SubProcess | 内嵌子流程,子流程元素内嵌在主流程元素之内,只能在该流程中使用该子流程,外部是无法访问到的。这种子流程一般作为局部通用逻辑处理,或者因为特定业务需要,使得比较复杂的单个主流程设计清晰直观 |
ParallelGateWay | 它可以将分支(fork,并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。)为多个路径,也可以合并(join,所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关)多个入口路径。并行网关的前提条件:基于出口顺序流和入口顺序流 |
ExclusiveGateway | 排他网关经常使用流程变量决定流程下一步要选择的路径,为流程中的决策建模。当执行到达这个网关时,所有出口顺序流会按照它们定义的顺序进行计算。条件计算为true的顺序流(当没有设置条件时,认为顺序流定义为true)会被选择用于继续流程。用排他网关时,只会选择一条顺序流。当多条顺序 流的条件都计算为true时,其中在XML中定义的第一条(也只有这条)会被选择,用于继续流程。如果没有可选的顺序流,会抛出异常 |
InclusiveGateway | 包容网关就是并行网关(Parallel Gateway)和排他网关(Exclusive Gateway)的组合。可以在出口顺序流上定义条件,包容网关会计算它们。然而主要的区别是,包容网关与并行网关一样,可以选择多于一条(出口)顺序流 |
EventGateway | 事件网关,允许基于事件做选择。网关的每一条出口顺序流,都需要连接至一个捕获中间事件。当流程执行到达基于事件的网关时,网关类似等待状态地动作:执行被暂停。并且,为每一条出口顺序流,创建一个事件订阅。流程的走向完全是由于中间事件的选择。而由哪一个事件来决定流程的走向则是由最先触发的事件来决定的。 |
四、流程的基本操作
4.1 流程部署
通过RepositoryService进行流程部署,将流图文件转换成输入流。
//方式一: 单个文件部署方式
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、使用RepositoryService进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("bpmn/evection.bpmn") // 添加bpmn资源
.addClasspathResource("bpmn/evection.png") // 添加png资源
.name("出差申请流程")
.deploy();
//方式二:压缩包方式部署
// 定义zip输入流
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment().addZipInputStream(zipInputStream).deploy();
//方式三 BPMN文件以流的形式传入进行部署
DeploymentBuilder builder = repositoryService.createDeployment();
builder.name(process.getName());
builder.addInputStream(fileName, inputStream);
Deployment deployment = builder.deploy();
//流程部署列表查询的方法
DeploymentQuery deploymentQuery =repositoryService.createDeploymentQuery();
//可以根据很多条件查询,这是根据部署名称模糊查询
List<Deployment> list = deploymentQuery.deploymentNameLike("%"+name+"%")
在springboot项目中,我们将bpmn文件放在processes文件夹下springboot可以帮我自动部署,不需要我们手动操作,部署一个流程一般会影响三张表
- act_re_deployment 流程定义部署表,每部署一次增加一条记录
- act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
- act_ge_bytearray 流程资源表,存放了我们的BPMN文件
操作数据表
每部署一次流程会操作3张表,最好一次部署一个流程
- act_re_deployment 流程定义部署表,每部署一次增加一条记录,记录流程部署信息
- act_re_procdef 流程定义表,部署每个新的流程定义都会在这张表中增加一条记录,KEY 字段是用来唯一识别不同流程的关键字
- act_ge_bytearray 流程资源表,存储了.bpmn与.png文件
4.2 流程资源下载
将我们的部署的BPMN文件下载到本地
// 1、得到引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、得到查询器:ProcessDefinitionQuery,设置查询条件,得到想要的流程定义
ProcessDefinition processDefinition =
repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("leave")
.singleResult();
// 4、通过流程定义信息,得到部署ID
String deploymentId = processDefinition.getDeploymentId();
// 5、通过repositoryService的方法,实现读取图片信息和bpmn信息
// png图片的流
InputStream pngInput = repositoryService.getResourceAsStream(deploymentId,processDefinition.getDiagramResourceName());
// bpmn文件的流
InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId,processDefinition.getResourceName());
// 6、构造OutputStream流
File file_png = new File("d:/leave-new.png");
File file_bpmn = new File("d:/leave-new.bpmn");
FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
FileOutputStream pngOut = new FileOutputStream(file_png);
// 7、输入流,输出流的转换
IOUtils.copy(pngInput,pngOut);
IOUtils.copy(bpmnInput,bpmnOut);
// 8、关闭流
pngOut.close();
bpmnOut.close();
pngInput.close();
bpmnInput.close();
4.3 流程定义查询
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 得到ProcessDefinitionQuery 对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
// 查询流程定义
List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("leave")
.orderByProcessDefinitionVersion() // orderByProcessDefinitionVersion 按照版本排序
.desc() // desc倒叙
.list(); // list 返回集合
4.4 流程定义删除
删除流程定义后,历史表信息不会被删除
// 流程部署id
String deploymentId = "1";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 通过流程引擎获取repositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
//删除流程定义,如果该流程定义已有流程实例启动则删除时出错
repositoryService.deleteDeployment(deploymentId);
//设置true 级联删除流程定义,即使该流程有流程实例启动也可以删除,设置为false非级别删除方式,如果流程
repositoryService.deleteDeployment(deploymentId, true);
4.5 流程实例启动
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3、根据流程定义Id启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave")
// 4、根据流程定义Id启动流程,其中1001是BusinessKey,代表请假单的ID,可以通过这个ID关联查询请假单的具体信息
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave","1001");
操作数据库表
- act_hi_actinst 流程实例执行历史
- act_hi_identitylink 流程的参与用户历史信息
- act_hi_procinst 流程实例历史信息
- act_hi_taskinst 流程任务历史信息
- act_ru_execution 流程执行信息
- act_ru_identitylink 流程的参与用户信息
4.6 流程实例查询
// 流程定义key
String processDefinitionKey = "leave";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
List<ProcessInstance> list = runtimeService
.createProcessInstanceQuery()
.processDefinitionKey(processDefinitionKey)
.list();
4.7 流程实例的挂起与激活
// 获取processEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 查询流程定义的对象
ProcessInstance processInstance = runtimeService.
createProcessInstanceQuery().
processInstanceId("15001").
singleResult();
// 得到当前流程定义的实例是否都为暂停状态
boolean suspended = processInstance.isSuspended();
// 流程定义id
String processDefinitionId = processInstance.getId();
// 判断是否为暂停
if(suspended){
// 如果是暂停,可以执行激活操作 ,参数:流程定义id
runtimeService.activateProcessInstanceById(processDefinitionId);
System.out.println("流程定义:"+processDefinitionId+",已激活");
}else{
// 如果是激活状态,可以暂停,参数:流程定义id
runtimeService.suspendProcessInstanceById( processDefinitionId);
System.out.println("流程定义:"+processDefinitionId+",已挂起");
}
4.8 流程任务查询
// 任务负责人
String assignee = "zhangsan";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("leave") //流程Key
.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());
}
4.9 流程任务处理
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取taskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务的负责人 查询任务
// 返回一个任务对象
Task task = taskService.createTaskQuery()
.processDefinitionKey("leave") //流程Key
.taskAssignee("zhangsan") //要查询的负责人
.singleResult();
// 完成任务,参数:任务id
taskService.complete(task.getId());
4.10 流程历史信息查询
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取HistoryService
HistoryService historyService = processEngine.getHistoryService();
// 获取 actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
// 查询 actinst表,条件:根据 InstanceId 查询
// instanceQuery.processInstanceId("InstanceId");
// 查询 actinst表,条件:根据 DefinitionId 查询
instanceQuery.processDefinitionId("DefinitionId");
// 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
// 查询所有内容
List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
五、流程实例
5.1 动态分布任务负责人
通过UEL表达式在创建BPMN流程图的时候设置变量,在流程实例启动的时候为变量赋值
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 设置assignee的取值,用户可以在界面上设置流程的执行
Map<String,Object> assigneeMap = new HashMap<>();
assigneeMap.put("assignee0","张三");
assigneeMap.put("assignee1","李经理");
assigneeMap.put("assignee2","王总经理");
assigneeMap.put("assignee3","赵财务");
// 启动流程实例,同时还要设置流程定义的assignee的值
runtimeService.startProcessInstanceByKey("leave",assigneeMap);