赞
踩
框架,将业务数据和节点的流程进行分离【特定形式进行关联】,实现节点的自动流转的工作流框架.,帮助我们实现流程自动化。
请假审批:填写申请–>主管部门–>HR审批
报销审批:填写申请–>经理批准–>总经理–>财务批准
虽然不用框架也能实现该业务需求,流程变得复杂,需要改动时,就需要耗费大量的时间成本
Activiti7框架就是这样开源出来的一套框架,可以做到流程与代码分离,帮助我们实现
制定了一套规范,也就是业务流程图
SE环境,基于maven项目
<!--添加依赖--> <properties> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <activiti.version>7.0.0.SR1</activiti.version> </properties> <dependencies> <!-- activiti引擎 --> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-engine</artifactId> <version>${activiti.version}</version> </dependency> <!-- 整合Spring --> <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> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</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> <!-- log end --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> <!--数据库连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.4</version> </dependency> </dependencies>
在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=./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
在resource文件夹中创建名为 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" 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="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql:///activiti"/> <property name="username" value="root"/> <property name="password" value="admin"/> </bean> <!-- 默认id对应的值 为processEngineConfiguration --> <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> <property name="dataSource" ref="dataSource"/> <!-- activiti数据库表处理策略 false(默认值):检查数据库的版本和依赖库的版本,如果不匹配就抛出异常 true:构建流程引擎时,执行检查,如果需要就执行更新。如果表不存在,就创建。 create-drop:构建流程引擎时创建数据库报表,关闭流程引擎时就删除这些表。 drop-create:先删除表再创建表。 create:构建流程引擎时创建数据库表,关闭流程引擎时不删除这些表 --> <property name="databaseSchemaUpdate" value="true"/> </bean> </beans>
Activiti是通过操作数据库中的表单来实现工作流的。不同的环境中,表的数量也是不一样的。
此环境中有25张表。
流程定义—>RepositoryService
act_ge_bytearray:用于存储bpmn的数据
act_ge_property:用于生产主键等,是内部表,我们一般不需要关注
act_re_deployment:部署信息表
act_re_procdef:流程定义表
流程实例—>RunTimeService
执行任务—>taskService
接口 | 作用 |
---|---|
RepositoryService | 进行资源管理,流程定义 |
RunTImeService | 进行流程实例 |
TaskService | 进行任务管理 |
HistoryService | 进行历史管理 |
ManagementService | 进行引擎管理 |
bpmn的id和Name是自定义的
给任务节点设置名字,给上执行人,后续执行人可以使用UEL表达式,监听器来设置,也可以通过候选人方式设置。
@Test
public void test1() {
//1 获取流程引擎对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2 获取仓库Service, RespositoryService
processEngine.getRepositoryService()
.createDeployment()
.addClasspathResource("bpmn/act3.bpmn")
.deploy();
//一个deploye对应一个流程定义
}
@Test
public void test2() {
processEngine.getRuntimeService()
.startProcessInstanceByKey("bpmn图中id");//自定义的流程名称
}
@Test public void test3(){ /* 参数为该任务的id,可以用TaskService提供query方法进行任务的查询 添加评论要在任务提交前执行 添加评论 taskService.addComent(taskId,processInstanceId,messag); 多个对象用list(),唯一用singleResult() */ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); TaskService taskService = processEngine.getTaskService(); taskService .createTaskQuery() .processDefinitionKey("leaveProcess")//查询条件 .taskAssignee("张三")//查询条件 .list().forEach(task -> { taskService.addComment(task.getId(), task.getProcessInstanceId(), "不干了"); taskService.complete(task.getId()); }); }
@Test
public void test10() {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey("leaveProcess")
.list()
.forEach(historicProcessInstance -> {
System.out.println(historicProcessInstance.getId());
});
}
@Test public void testDefinitionQuery(){ //创建ProcessEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //获取仓库服务 RepositoryService repositoryService = processEngine.getRepositoryService(); //获取流程定义集合 List<ProcessDefinition> processDefinitionList = repositoryService .createProcessDefinitionQuery() .processDefinitionKey("leaveProcess") .list(); //遍历集合 for (ProcessDefinition definition:processDefinitionList){ System.out.println("流程定义ID:"+definition.getId()); System.out.println("流程定义名称:"+definition.getName()); System.out.println("流程定义key:"+definition.getKey()); System.out.println("流程定义版本:"+definition.getVersion()); System.out.println("流程部署ID:"+definition.getDeploymentId()); System.out.println("===================="); } }
2.现在我们的流程资源文件已经上传到数据库了,如果其他用户想要查看这些资源文件,可以从数据库中把资源文件下载到本地。
@Test public void testDownloadResource() throws Exception { //创建ProcessEngine对象 ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //获取仓库服务 RepositoryService repositoryService = processEngine.getRepositoryService(); //获取流程定义集合 List<ProcessDefinition> list = repositoryService .createProcessDefinitionQuery() .processDefinitionKey("leaveProcess") .orderByProcessDefinitionVersion()//按照版本排序 .desc()//降序 .list(); //获取最新那个 ProcessDefinition definition =list.get(0); //获取部署ID String deploymentId = definition.getDeploymentId(); //获取bpmn的输入流 InputStream bpmnInput = repositoryService.getResourceAsStream( deploymentId, definition.getResourceName()); //获取png的输入流 InputStream pngInput = repositoryService.getResourceAsStream( deploymentId, definition.getDiagramResourceName()); //设置bpmn输入 FileOutputStream bpmnOutPut = new FileOutputStream("D:/leave.bpmn"); //设置png输入 FileOutputStream pngOutPut = new FileOutputStream("D:/leave.png"); IOUtils.copy(bpmnInput,bpmnOutPut); IOUtils.copy(pngInput,pngOutPut); }
3.根据部署Id删除对应的流程定义
如果流程定义 没有流程实例可以直接删除
如果流程定义 已近有流程实例,可以通过一个参数 true 进行级联删除
@Test
public void test3(){
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
processEngine.getRepositoryService().deleteDeployment("5001",true);
}
框架,将业务数据和节点的流程进行分离【特定形式进行关联】,特定形式指的就是businessKey
businessKey
搞清楚businessKey 作用?
把我们的业务数据和流程绑定起来 , 因为在审核的时候 , 审核人妖看到用户申请信息.
步骤
1 用户在申请的时候填写信息 ,最终把信息保存到业务表中
2 先画出bpmn流程 ,把流程图 部署到activiti当中
3 小陈发起申请, 在activiti 开启流程实例 , 在启动流程时候就会把businessKey 和流程实例绑定起来
4 任务走了部门经理节点 , 这里是一个任务, 我们是可以通过任务拿到流程实例id
5 通过流程实例id拿到流程实例 , 就可以通过流程实例那businessKey
6 根据businessKey 上业务表中取查询数据 , 审核人在根据查询的数据看是否通过
public class Demo { /** * 获取businessKey */ //流程定义 @Test public void test1() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addClasspathResource("bpmn/act3.bpmn") .deploy(); } //流程实例+添加businessKey @Test public void test2() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceByKey("act3", "2887"); } //在任务中获取实例id,通过实例id获取实例对象,通过实例对象获取businessKey @Test public void test3() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); String processInstanceId = processEngine.getTaskService() .createTaskQuery() .singleResult() .getProcessInstanceId(); String businessKey = processEngine .getRuntimeService() .createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult() .getBusinessKey(); System.out.println("businessKey = " + businessKey); } }
三、流程定义/实例挂起/激活
例如公司制度改变过程中的流程, 总经理更换过程中的流程,有100个人的流程, 70个人已经完成,30个人流程正好在总经理更换中,就需要挂起.
【开始节点】–>【A节点】–>【B节点】–>【C节点】–>【结束节点】
【C节点】的业务逻辑需要和外部接口交互,刚好外部接口出问题了,如果剩下的流程都走到【C节点】,执行【C节点】的业务逻辑,那都会报错,我们就可以把流程挂起,等待外部接口可用之后再重新激活流程.
public class Demo { /** * 流程定义的挂起与激活 */ //定义流程 @Test public void test1() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addClasspathResource("bpmn/act4.bpmn") .deploy(); } //流程实例 @Test public void test2() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceByKey("act4"); } //进行流程挂起 @Test public void test3() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); ProcessDefinition processDefinition = processEngine.getRepositoryService(). createProcessDefinitionQuery() .singleResult(); processEngine.getRepositoryService().suspendProcessDefinitionById(processDefinition.getId()); } //查询流程挂起状态 @Test public void test4() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); System.out.println(processEngine.getRepositoryService() .createProcessDefinitionQuery() .singleResult().isSuspended());//true } //可以完成未完成的任务 @Test public void test5() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); Task task = processEngine.getTaskService() .createTaskQuery() .singleResult(); processEngine.getTaskService().complete(task.getId()); } //但不能再流程实例 @Test public void test6() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceByKey("act4"); } //重新激活流程定义+测试+执行新实例任务 @Test public void test7() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); ProcessDefinition processDefinition = repositoryService .createProcessDefinitionQuery() .singleResult(); if (processDefinition.isSuspended()) { System.out.println("to true"); repositoryService.activateProcessDefinitionById(processDefinition.getId()); System.out.println("流程实例"); processEngine.getRuntimeService() .startProcessInstanceByKey("act4"); System.out.println("执行任务"); TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery().taskAssignee("张三").singleResult(); taskService.complete(task.getId()); } } //挂起后不能执行任务和进行流程定义 @Test public void test8() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); RepositoryService repositoryService = processEngine.getRepositoryService(); ProcessDefinition processDefinition = repositoryService .createProcessDefinitionQuery() .singleResult(); repositoryService .suspendProcessDefinitionById(processDefinition.getId(), true, null); } //不能进行流程定义 @Test public void test9(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRuntimeService() .startProcessInstanceByKey("act4"); } //不能执行任务 @Test public void test10(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService().complete("10005"); } //挂起实例 @Test public void test11(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); //把原来的挂起的流程定义激活 RepositoryService repositoryService = processEngine.getRepositoryService(); ProcessDefinition processDefinition = repositoryService .createProcessDefinitionQuery() .singleResult(); //挂起实例 RuntimeService runtimeService = processEngine.getRuntimeService(); runtimeService.suspendProcessInstanceById("10001"); //检测是否挂起,若已近挂起,就重新激活,并执行任务 TaskService taskService = processEngine.getTaskService(); Task task = taskService.createTaskQuery() .taskAssignee("张三") .singleResult(); //获取实例id String processInstanceId = task.getProcessInstanceId(); //判断是否已近挂起 boolean suspended = runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult() .isSuspended(); if (suspended) { System.out.println("======================"); System.out.println("激活"); runtimeService.activateProcessInstanceById(processInstanceId); taskService.complete(task.getId()); } } }
指的就是指定处理任务的人,分配方法有三种
1.固定分配 直接把名字写死,在实际开发过程中,此方法不适应。但在测试时使用比其他的方法都方便。
只要在任务结点中的 Assignee属性中设置值
使用EUL表达式,是一种占位符,在任务结点中的 Assignee属性中设置${value}。需要把处理人添加到流程实例,就要在流程实例时,添加参数 ,以map的方式添加。
public class UEL { /** * 分配负责人 */ //流程定义 @Test public void test1() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getRepositoryService() .createDeployment() .addClasspathResource("bpmn/act5.bpmn") .deploy(); } //实例部署 @Test public void test2() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); Map<String, Object> map = new HashMap<>(); map.put("s1", "张三"); map.put("s2", "lisi"); processEngine.getRuntimeService() .startProcessInstanceByKey("act5", map); } //查询候选人 @Test public void test3() { ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); Task lisi = processEngine.getTaskService() .createTaskQuery() .taskCandidateOrAssigned("lisi").singleResult(); } //领任务 @Test public void test4(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService().claim("17507","lisi"); } //执行任务 @Test public void test5(){ ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); processEngine.getTaskService().complete("17507"); } }
监听器
任务监听器是发生对应的任务相关事件时执行自定义的Java逻辑或表达式。
任务相关事件包括:
自定义一个任务监听器类,然后此类必须实现org.activiti.engine.delegate.TaskListener接口
package cn.wolfcode; import org.activiti.engine.delegate.DelegateTask; import org.activiti.engine.delegate.TaskListener; /** * Created by wolfcode */ public class AssigneeTaskListener implements TaskListener { public void notify(DelegateTask delegateTask) { if(delegateTask.getName().equals("部门经理审批")){ delegateTask.setAssignee("赵六"); }else if(delegateTask.getName().equals("部门经理审批")){ delegateTask.setAssignee("孙七"); } } }
在bpmn文件中配置监听器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uzt3J72y-1653831176864)(E:/CodeWolf/20220215/阶段四/07 Activit/讲义/图片/image-20210603114503828.png)]
在实际开发中,一般也不使用监听器分配方式,太麻烦了。
在流程中,可以通过变量来控制流程的走向,从而增强业务能力。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GXacHjjK-1653831176865)(C:\Users\SKY\AppData\Roaming\Typora\typora-user-images\image-20220529172114148.png)]
3. 使用方法
1. 在流程实例时,添加一个map为参数,Map<String,Object> ,String为变量名,Object为值
4. 特殊情况
1. 当两个情况都满足,在不添加其他操作时,两个任务都会被触发
2. 当两个情况都不满足时,会抛出异常,说找不到出口
1. 在实际运用的过程中,任务的执行人不是固定的,而是从几个人中,拿一个人出来执行便可
2. 在Condidata中,通过添加候选人,可以添加多个,中间使用逗号隔开
3. 在流程实例后,候选人领取任务前,该任务是没有执行人的
4. 候选人执行任务
1. 要使用TaskServicet提供的 claim(String taskId,String userId),进行领取任务
2. 使用 taskSerice.commit(Strinig taskId)执行任务
排他网关
并行网关
包含网关
可以看成是排他网关与并行网关的集合体
需要两个网关,把所有分支都夹起来
使用场景:请假规则
候选人,需要先使用claim进行领取任务,在执行任务
g为变量名,Object为值
4. 特殊情况
1. 当两个情况都满足,在不添加其他操作时,两个任务都会被触发
2. 当两个情况都不满足时,会抛出异常,说找不到出口
1. 在实际运用的过程中,任务的执行人不是固定的,而是从几个人中,拿一个人出来执行便可
2. 在Condidata中,通过添加候选人,可以添加多个,中间使用逗号隔开
3. 在流程实例后,候选人领取任务前,该任务是没有执行人的
4. 候选人执行任务
1. 要使用TaskServicet提供的 claim(String taskId,String userId),进行领取任务
2. 使用 taskSerice.commit(Strinig taskId)执行任务
排他网关
并行网关
包含网关
可以看成是排他网关与并行网关的集合体
需要两个网关,把所有分支都夹起来
使用场景:请假规则
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。