学习目标
实现流程的自动化管理,使业务流程变更后代码,不用修改。
Activity7 工作原理
上图是一个请假流程的,在Activity实现的工作机制。流程的每个节点都对应数据库表中的一条数据,该节点走完就从数据库中删除,从而实现流程的推进。从而当业务流程新增或者减少了一个节点时,我们就可以不用修改业务代码,直接修改流程图,实现流程自动化管理。
在idea下载Activiti BPMN visualizer插件
activity表结构生成(java) 方式
1、导入依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<slf4j.version>1.6.6</slf4j.version>
<log4j.version>1.2.12</log4j.version>
</properties>
<dependencies>
<!--activity-->
<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>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<dependency>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>7.0.0.Beta1</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<!--junit-->
<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>
2 创建Activiti配置文件 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:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd" default-lazy-init="false">
<!--数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/activitydb"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--配置Activiti使用的processEngine对象 默认命名为processEngineConfiguration-->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"/>
<!--指定数据库生成策略-->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
3、编写java代码生成表
/**
* 生成Activity的25张表
*/
public class CreateActivityTable {
@Test
public void testGenTable(){
//1、创建ProcessEngineConfiguration对象
ProcessEngineConfiguration configuration = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
//2、创建ProcessEngine对象
ProcessEngine processEngine = configuration.buildProcessEngine();
System.out.println(processEngine);
}
}
执行后25张表成功生成
表名规则
Activiti 使用到的表都是 ACT_ 开头的。表名的第二部分用两个字母表明表的用途。
ACT_GE_ (GE) 表示 general 全局通用数据及设置,各种情况都使用的数据。
ACT_HI_ (HI) 表示 history 历史数据表,包含着程序执行的历史相关数据,如结束的流程实例,变量,任务,等等
ACT_ID_ (ID) 表示 identity 组织机构,用户记录,流程中使用到的用户和组。这些表包含标识的信息,如用户,用户组,等等。
ACT_RE_ (RE) 表示 repository 存储,包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。
ACT_RU_ (RU) 表示 runtime 运行时,运行时的流程变量,用户任务,变量,职责(job)等运行时的数据。Activiti 只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
参考
activiti7数据库表结构参考_x_u_xiang的博客-CSDN博客_activiti数据库表结构
流程定义
holiday.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/processdef">
<process id="holiday" isClosed="false" name="请假流程" isExecutable="true" processType="None">
<endEvent id="EndEvent_1" name="结束"/>
<sequenceFlow sourceRef="StartEvent_1" targetRef="ServiceTask_1" id="StartEvent_1-ServiceTask_1"/>
<sequenceFlow sourceRef="ServiceTask_1" targetRef="ServiceTask_2" id="ServiceTask_1-ServiceTask_2"/>
<sequenceFlow sourceRef="ServiceTask_2" targetRef="ServiceTask_3" id="ServiceTask_2-ServiceTask_3"/>
<sequenceFlow sourceRef="ServiceTask_3" targetRef="EndEvent_1" id="ServiceTask_3-EndEvent_1"/>
<userTask activiti:assignee="zhangsan" id="ServiceTask_1" name="填写请假单" />
<userTask activiti:assignee="lisi" id="ServiceTask_2" name="部门经理审核" />
<userTask activiti:assignee="wangwu" id="ServiceTask_3" name="总经理审核" />
<startEvent id="StartEvent_1" name="开始"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_holiday">
<bpmndi:BPMNPlane bpmnElement="holiday" id="BPMNPlane_holiday">
<bpmndi:BPMNShape bpmnElement="StartEvent_1">
<omgdc:Bounds height="48.0" width="48.0" x="37.0" y="55.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="ServiceTask_1">
<omgdc:Bounds height="48.0" width="120.0" x="167.0" y="55.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="ServiceTask_2">
<omgdc:Bounds height="48.0" width="120.0" x="334.0" y="50.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="ServiceTask_3">
<omgdc:Bounds height="48.0" width="120.0" x="499.0" y="52.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="EndEvent_1">
<omgdc:Bounds height="48.0" width="48.0" x="721.0" y="51.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="StartEvent_1-ServiceTask_1">
<omgdi:waypoint x="61.0" y="79.0"/>
<omgdi:waypoint x="227.0" y="79.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="ServiceTask_1-ServiceTask_2">
<omgdi:waypoint x="227.0" y="79.0"/>
<omgdi:waypoint x="394.0" y="74.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="ServiceTask_2-ServiceTask_3">
<omgdi:waypoint x="394.0" y="74.0"/>
<omgdi:waypoint x="559.0" y="76.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="ServiceTask_3-EndEvent_1">
<omgdi:waypoint x="559.0" y="76.0"/>
<omgdi:waypoint x="745.0" y="75.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
流程部署
/**
* 流程定义的部署
*/
public class ActivitiDeployment {
//流程定义部署
public static void main(String[] args) {
//1 创建ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2 得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
//3 进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("diagram/holiday.bpmn")
.addClasspathResource("diagram/holiday.png")
.name("请假申请流程")
.deploy();
//4 输出部署的一些信息
System.out.println("---------------------------");
System.out.println(deployment.getName());
System.out.println(deployment.getId());
System.out.println("---------------------------");
}
}
此外还可以通过zip包部署,当然zip包含bpmn和png文件才行
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
InputStream is = getClass().getClassLoader().getResourceAsStream("diagram/holiday.zip");
ZipInputStream zipInputStream = new ZipInputStream(is);
Deployment deployment = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("请假申请流程")
.deploy();
执行效果:
同时影响以下三张表
act_re_deployment
act_re_procdef
act_ge_bytearray
流程实例启动
/**
* 启动流程实例
* 前提完成流程定义和部署
*/
public class ActivitiStartInstance {
public static void main(String[] args) {
//1 得到ProcessEngine对象
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//2 得到RunService对象
RuntimeService runtimeService = processEngine.getRuntimeService();
//3 创建流程实例,流程定义的key需要去act_re_procdef表查holiday
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday");
//4 输出实例的相关信息
System.out.println("-----------------");
System.out.println("流程部署id:" + processInstance.getDeploymentId());
System.out.println("流程实例id:" + processInstance.getId());
System.out.println("流程活动id:" + processInstance.getActivityId());
System.out.println("-----------------");
}
}
执行输出
影响的表
act_hi_procinst——流程实例信息
act_hi_identitylink——参与者信息
act_hi_taskinst——任务实例信息
act_hi_actinst——已完成活动信息
act_ru_execution——执行信息
act_ru_identitylink——参与者
act_ru_task——任务
查询当前用户的任务列表
/**
* 查询当前用户的任务列表
*/
public class ActivitiTaskQuery {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 得到TaskService对象
TaskService taskService = processEngine.getTaskService();
// 根据例程定义的key ,负责人assignee来实现当前用户的任务列表查询
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("holiday")
.taskAssignee("zhangsan")
.list();
// 任务列表的展示
for(Task task : taskList){
System.out.println("流程实例ID:" + task.getProcessInstanceId());
System.out.println("任务ID:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
}
执行输出
当前用户任务处理
先查看act_ru_task表中,有哪些任务需要被处理的。
/**
* 实现当前用户任务处理
*/
public class ActivitiCompleteTask {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
//处理任务,结合当前用户任务列表的查询操作,任务ID:12505
taskService.complete("12505");
}
}
代码执行后再看act_ru_task表
可以看到,填写请假单这个流程被处理完后,数据就被删除了,正好对应开始说的工作原理。同时在act_hi_taskinst中也会出现相关的历史任务数据。
流程定义信息查询
public class QueryProcessDefinition {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//设置条件,并查询所有流程定义,查询条件 key=holiday
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("holiday")
.orderByProcessDefinitionVersion()
.desc().list();
for(ProcessDefinition processDefinition : list){
System.out.println("流程定义ID:" + processDefinition.getId());
System.out.println("流程定义名称:" + processDefinition.getName());
System.out.println("流程定义的key:" + processDefinition.getKey());
System.out.println("流程定义的版本:" + processDefinition.getVersion());
}
}
}
流程定义信息删除
/**
* 删除流程定义信息
* 影响的表
* act_ge_bytearray
* act_re_deployment
* act_re_procdef
*/
public class DeleteProcessDefinition {
/**
* 1·当我们正在执行的一套流程,没有完全审批结束的时候,此时删除流程定义会失败
* 2·如果要强制删除,可以使用 repositoryService.deleteDeployment("10001",true);
* 参数true代表级联删除,此时就会先删除没有完成流程节点,最后就可以删除流程定义信息,false的值代表不级联
*/
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
//执行删除,参数是流程部署的id
repositoryService.deleteDeployment("10001");
}
}
Activity实现资源文件的保存
/**
* 需求:
* 1.从Activity的cat_ge_bytearray表中读取两个资源文件
* 2.将两个资源文件保存到路径:D:\mycode\StudyActivity7\information\holiday
* 技术方案
* 1、activity的api来实现
* 2、用jdbc对blob类型,clob类型数据的读取,并保存
* IO流转换,最好commons-io.jar包可以轻松解决IO操作
*/
public class QueryBpmnFile {
public static void main(String[] args) throws IOException {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
// 设置查询条件
processDefinitionQuery.processDefinitionKey("holiday");
// 查询出想要的流程定义
ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
String deploymentId = processDefinition.getDeploymentId();
//第一个参数部署id,第二个参数资源名称
InputStream pngIs = repositoryService.getResourceAsStream(deploymentId,
processDefinition.getDiagramResourceName());
InputStream bpmnIs = repositoryService.getResourceAsStream(deploymentId,
processDefinition.getResourceName());
//构建OutputStream流
FileOutputStream pngOs =
new FileOutputStream("D:\\mycode\\StudyActivity7\\information\\" + processDefinition.getDiagramResourceName());
FileOutputStream bpmnOS =
new FileOutputStream("D:\\mycode\\StudyActivity7\\information\\" + processDefinition.getResourceName());
// 输入流,输出流的转换 commons-io-xx.jar中的方法
IOUtils.copy(pngIs,pngOs);
IOUtils.copy(bpmnIs,bpmnOS);
// 关闭流
pngOs.close();
bpmnOS.close();
pngIs.close();
bpmnIs.close();
}
}
查看流程的历史信息
/**
* 历史数据查看
*/
public class HistoryQuery {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
HistoryService historyService = processEngine.getHistoryService();
//得到HistoricActivityInstanceQuery
HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService.createHistoricActivityInstanceQuery();
//设置流程实例的id
historicActivityInstanceQuery.processInstanceId("12501");
//执行查询
List<HistoricActivityInstance> list = historicActivityInstanceQuery
.orderByHistoricActivityInstanceStartTime().asc().list();
for(HistoricActivityInstance instance : list){
System.out.println(instance.getActivityId());
System.out.println(instance.getActivityName());
System.out.println(instance.getProcessDefinitionId());
System.out.println(instance.getProcessInstanceId());
System.out.println("===================================");
}
}
}
输出结果
业务流程businessKey整合
请假业务的表和Activity7的25张表是通过act_ru_execution表中的BUSINESS_KEY_字段进行关联的。我们可以通过该字段判断对应的请假单执行到哪个流程。
/**
* 启动流程实例,添加进businessKey
* 本质:act_ru_execution表中的businessKey的字段存入业务标识
*/
public class BusinessKeyAdd {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
//启动流程实例,同时还要知道业务标识businessKey,其本身就是请假单的id
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday", "1001");
//输出processInstance相关的属性
System.out.println(processInstance.getBusinessKey());
}
}
执行控制台输出
同时数据库act_ru_execution表多出两行数据
流程的激活和挂起
/**
* 全部流程实例挂起与激活
*/
public class SuspendProcessInstance {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("holiday").singleResult();
//得到当前流程定义实例是否都为挂起状态
boolean suspended = processDefinition.isSuspended();
String processDefinitionId = processDefinition.getId();
if(suspended){
//true 挂起,进行激活
repositoryService.activateProcessDefinitionById(processDefinitionId,true,null);
System.out.println("流程定义:" + processDefinitionId + "激活");
}else{
repositoryService.suspendProcessDefinitionById(processDefinitionId,true,null);
System.out.println("流程定义:" + processDefinitionId + "挂起");
}
}
}
/**
* 单个流程实例的激活和挂起
*/
public class SuspendProcessInstance2 {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId("25001").singleResult();
//得到当前流程定义实例是否都为挂起状态
boolean suspended = processInstance.isSuspended();
String processInstanceId = processInstance.getId();
if(suspended){
//true 挂起,进行激活
runtimeService.activateProcessInstanceById(processInstanceId);
System.out.println("流程定义:" + processInstanceId + "激活");
}else{
runtimeService.suspendProcessInstanceById(processInstanceId);
System.out.println("流程定义:" + processInstanceId + "挂起");
}
}
}
/**
* 单个流程实例的激活和挂起
*/
public class SuspendProcessInstance2 {
public static void main(String[] args) {
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId("25001").singleResult();
//得到当前流程定义实例是否都为挂起状态
boolean suspended = processInstance.isSuspended();
String processInstanceId = processInstance.getId();
if(suspended){
//true 挂起,进行激活
runtimeService.activateProcessInstanceById(processInstanceId);
System.out.println("流程定义:" + processInstanceId + "激活");
}else{
runtimeService.suspendProcessInstanceById(processInstanceId);
System.out.println("流程定义:" + processInstanceId + "挂起");
}
}
}
如果我们要start一个被挂起的流程实例会报错
Cannot start process instance. Process definition 请假流程 (id = holiday:1:22504) is suspended