repository
runtime
identity
history
general
我使用SpringBoot进行集成
spring:
activiti:
#校验、部署流程文件
check-process-definitions: true
#自动更新数据库结构
database-schema-update: true
#流程定义文件存放目录
process-definition-location-prefix: classpath:/processes/
#检查身份信息表是否存在
db-identity-used: true
下面两个是activiti的官方文档说明和api说明
java Doc https://www.activiti.org/javadocs/
activiti
https://www.activiti.org/userguide/
它有这么几个服务
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 提供了处理流程实例不同步骤的结构和行为
RuntimeService runtimeService = processEngine.getRuntimeService();
// 提供运营管理和操作流程实例的服务
RepositoryService repositoryService = processEngine.getRepositoryService();
// 提供有关任务相关功能的服务
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
// 提供对用户和群组的管理
IdentityService identityService = processEngine.getIdentityService();
// 提供activiti引擎收集的历史记录信息服务
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
RepositoryService:提供运营管理和操作流程实例的服务
RuntimeService:提供了处理流程实例不同步骤的结构和行为
TaskService:提供有关任务相关功能的服务
IdentityService:提供对用户和群组的管理
FormService:也可以启动一个流程
HistoryService:提供activiti引擎收集的历史记录信息服务
ManagementService:job任务查询和数据库操作
DynamicBpmnService:无需重新部署就能修改流程定义内容
注册上面的服务为spring bean:
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.spring.SpringProcessEngineConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.io.IOException;
@Configuration
public class ActivitiConfig {
private Logger logger = LoggerFactory.getLogger(ActivitiConfig.class);
@Value("${spring.activiti.database-schema-update}")
private String databaseSchemaUpdate;
@Value("${spring.activiti.db-identity-used}")
private boolean dbIdentityUsed;
@Value("${spring.datasource.url}")
private String dbUrl;
@Bean
public ProcessEngine processEngine(DataSourceTransactionManager transactionManager, DataSource dataSource) throws IOException {
logger.info("==========activiti=======开始==============");
SpringProcessEngineConfiguration configuration = new SpringProcessEngineConfiguration();
/* 自动部署已有的流程文件
作用相当于 (据bpmn文件部署流程repositoryService.createDeployment().addClasspathResource("singleAssignee.bpmn").deploy();)*/
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourceLoader.CLASSPATH_URL_PREFIX + "processes/*.bpmn");
configuration.setTransactionManager(transactionManager);
//设置数据源
configuration.setDataSource(dataSource);
//是否每次都更新数据库
//configuration.setDatabaseSchemaUpdate(databaseSchemaUpdate);
configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE);
//configuration.setAsyncExecutorActivate(false);
configuration.setDeploymentResources(resources);
//设置是否使用activti自带的用户体系
configuration.setDbIdentityUsed(dbIdentityUsed);
return configuration.buildProcessEngine();
}
/**
* 工作流仓储服务
* @param processEngine
* @return
*/
@Bean
public RepositoryService repositoryService(ProcessEngine processEngine) {
return processEngine.getRepositoryService();
}
/**
* 工作流运行服务
* @param processEngine
* @return
*/
@Bean
public RuntimeService runtimeService(ProcessEngine processEngine) {
return processEngine.getRuntimeService();
}
/**
* 工作流任务服务
* @param processEngine
* @return
*/
@Bean
public TaskService taskService(ProcessEngine processEngine) {
return processEngine.getTaskService();
}
/**
* 工作流历史数据服务
* @param processEngine
* @return
*/
@Bean
public HistoryService historyService(ProcessEngine processEngine) {
return processEngine.getHistoryService();
}
/**
* 工作流管理服务
* @param processEngine
* @return
*/
@Bean
public ManagementService managementService(ProcessEngine processEngine) {
return processEngine.getManagementService();
}
/**
* 工作流唯一服务
* @param processEngine
* @return
*/
@Bean
public IdentityService identityService(ProcessEngine processEngine) {
return processEngine.getIdentityService();
}
}
它支持的部署方式有classpath、inputStream、zip/bar格式压缩包.
// 读取bpmn
// 创建构建器
Deployment dep = repositoryService.createDeployment()
// 添加资源
.name("测试流程").addClasspathResource("processes/testProcess.bpmn")
// 执行部署
.deploy();
// 读取xml
Deployment dep = repositoryService.createDeployment()
.name("测试流程").addClasspathResource("processes/testProcess.xml")
.deploy();
String fileName = "processes/testProcess.bpmn";
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName);
// inputstream部署
repositoryService.createDeployment()
.name("processes.zip")
.addInputStream("testProcess.bpmn", inputStream)
.deploy();
// 启动流程
Map<String,Object> map = new HashMap<>();
map.put("user1","ali");
ProcessInstance p1 = runtimeService.startProcessInstanceByKey("myProcess", map);
System.out.println("部署流程-id:"+p1.getId());
System.out.println("部署流程-name:"+p1.getName());
String zipFileName = "processes/processes.zip";
System.out.println("部署包:"+zipFileName);
// ZipInputStream inputStream = new ZipInputStream(new FileInputStream(zipFileName));
// 读取资源路径下
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(zipFileName);
repositoryService.createDeployment()
.name("processes.zip")
.addZipInputStream(new ZipInputStream(inputStream))
.deploy();
// 启动流程
Map<String,Object> map = new HashMap<>();
map.put("userId","aliZip");
ProcessInstance p1 = runtimeService.startProcessInstanceByKey("zipProcess1", map);
ProcessInstance p2 = runtimeService.startProcessInstanceByKey("zipProcess2", map);
System.out.println("部署流程1-id:"+p1.getId());
System.out.println("部署流程1-name:"+p1.getName());
System.out.println("部署流程2-id:"+p2.getId());
System.out.println("部署流程2-name:"+p2.getName());
在部署后会创建对应的流程定义,数据库表里流程定义的key即为流程图(bpmn)里的流程定义ID,并且会分配一个版本,格式为:key:value,它会根据key匹配,相同的版本+1。
启动步骤一般是和部署分开的。
发起流程由runtimeService.startProcessInstanceByKey()
发起,他有四个重载方法,完全能够应对我们的日常开发:
startProcessInstanceById(String processDefinitionId)
startProcessInstanceById(String processDefinitionId, Map<String,Object> variables)
startProcessInstanceById(String processDefinitionId, String businessKey)
startProcessInstanceById(String processDefinitionId, String businessKey, Map<String,Object> variables)
参数说明:
processDefinitionId:流程定义id,流程图中的id,数据库表中的key
variables:要传递到下一环节的变量
businessKey:上下文参数,可以理解位业务关键字,区别业务系统的键
下面以一个简单流程示例说明
<?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/test">
<process id="myProcess" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<userTask id="user_task_1" name="组长审批" activiti:assignee="${user1}"></userTask>
<userTask id="user_task_2" name="领导审批" activiti:assignee="${user2}"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="user_task_1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="user_task_1" targetRef="user_task_2"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="user_task_2" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="345.0" y="20.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="345.0" y="280.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="user_task_1" id="BPMNShape_user_task_1">
<omgdc:Bounds height="55.0" width="105.0" x="310.0" y="70.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="user_task_2" id="BPMNShape_user_task_2">
<omgdc:Bounds height="55.0" width="105.0" x="310.0" y="181.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="362.0" y="55.0"></omgdi:waypoint>
<omgdi:waypoint x="362.0" y="70.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="362.0" y="125.0"></omgdi:waypoint>
<omgdi:waypoint x="362.0" y="181.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="362.0" y="236.0"></omgdi:waypoint>
<omgdi:waypoint x="362.0" y="280.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
部署后的流程,可以创建多个实例,所以只要部署一次就可以了。
如果流程图中有配置assignee,那么要在参数中加上assignee对应的变量键值
在启动时,assignee的参数是param最上层的,而在之后的任务中的assignee参数是在variable这层下面。
{
"processInitKey":"myProcess",
"user1":"sss"
}
public String startProcess(Map<String, Object> param) {
ProcessInstance process = runtimeService.startProcessInstanceByKey(String.valueOf(param.get("processInitKey")), param);
System.out.println("流程启动=============");
System.out.println("processId: "+ process.getId());
return process.getId();
}
以上一节的流程图为例
这里的流程沿用上面的流程
流程比较简单,只有启动、结束和user task,用户任务的表示就和图中红框一样,xml表示:
<userTask id="user_task_1" name="组长审批" activiti:assignee="${user1}"></userTask>
属性:
activiti:assignee :直接分配任务给指定用户
activiti:candidateUsers :指定用户为任务候选人
activiti:candidateGroups : 指定用户组的用户为任务候选人
自定义属性配置,这个我还没有看
还可以配置描述:
<userTask id="theTask" name="Schedule meeting" >
<documentation>
......
</documentation>
</userTask>
form 属性和form key还不知道,需要补充
在视图上配置的话比较简单,常用的是配置assignee和listener,上面的【组长审批】【领导审批】的assignee分别配置了{user1}和{user2},使用符号
TaskQuery、TaskInfoQuery
提供了查询方法,它的获取方式是taskService.createTaskQuery()
,查询方法比较多,这里列举几个
// 根据任务ID查询
T taskId(String taskId);
// 根据任务名称查询
T taskName(String name);
// 根据任务名称列表查询
T taskNameIn(List<String> nameList);
// 模糊任务名称查询
T taskNameLike(String nameLike);
// 根据分配给指定用户查询
T taskAssignee(String assignee);
// 根据分配给指定用户模糊查询
T taskAssigneeLike(String assigneeLike);
// 根据分配给指定用户列表查询
T taskAssigneeIds(List<String> assigneeListIds);
// 委托人委托的任务
T taskOwner(String owner);
// 根据流程实例id查询
T processInstanceId(String processInstanceId);
// 根据流程实例id列表查询
T processInstanceIdIn(List<String> processInstanceIds);
// 根据路程定义key查询
T processDefinitionKey(String processDefinitionKey);
// 创建时间之前
T taskCreatedBefore(Date before);
// 创建时间之后
T taskCreatedAfter(Date after);
这里我只列举两个查询下面测试可能用到的,因为都是一样的写法。
第一种:
根据用户ID查询
/**
* 通过用户名查询该用户的所有任务
* @param userId
* @return
*/
public String queryByUser(String userId) {
List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).list();
List<TaskVo> result = new ArrayList<>();
addTaskVo(result, tasks);
// printTaskInfo(tasks);
return JSON.toJSONString(result);
}
第二种:
根据流程发起人查询,这种方式会查出该流程发起人下的所有任务
/**
* 通过发起者查询该用户发起的所有任务
* @param userId
* @return
*/
public String queryByInitiator(String userId) {
// 先查出流程实例
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().startedBy(userId).list();
List<TaskVo> result = new ArrayList<>();
for (ProcessInstance processInstance : list) {
// 遍历查出任务对象
List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
addTaskVo(result, tasks);
// printTaskInfo(tasks);
}
return JSON.toJSONString(result);
}
走到【组长审批】这一环节后,想要继续往下走,就需要完成当前任务,完成任务需要对应的用户去审批,然而,它本身没有去判断当前用户的功能,所以这个由指定用户审批的功能要自己实现(即在上一个环节你指定了用户,但是在当前环节依然可以由别人去审核完成)
/**
* 根据 taskid 审核任务
*/
public boolean audit(AudiaVo audiaVo) {
boolean result = false;
// 审核需要判断处理人是否有其权限(需要查询用户表和用户组表)
// Task task = taskService.createTaskQuery().taskId(audiaVo.getTaskId()).singleResult();
// if (StringUtils.isEmpty(task.getAssignee())) {
// return result;
// }
//
// // 查询组
// GroupQuery groupQuery = identityService.createGroupQuery().groupMember(audiaVo.getUserId());
// if (groupQuery == null) {
// logger.info("无用户组信息");
// }
// 设置当前处理人
Authentication.setAuthenticatedUserId(audiaVo.getUserId());
// 添加评论
taskService.addComment(audiaVo.getTaskId(), audiaVo.getProcessInstanceId(), audiaVo.getComment());
// 完成任务
taskService.complete(audiaVo.getTaskId(), audiaVo.getVariable());
return true;
}
它完成一个任务,需要受理人、任务ID、下一个任务的参数
,如果需要发表评论则还需要流程实例id
。
{
"taskId": "132520",
"userId": "ali",
"comment": "ok",
"variable": {
"isAgree": 1
}
}
处理人可以委托任务给其他人员,和审核任务一样,他没有自动校验委托人是否有权限
/**
* 委托任务
* @param param
*/
public void owner(Map<String, String> param) {
String taskId = param.get("taskId");
String userId = param.get("userId");
// todo 判断是否有委托权限
taskService.delegateTask(taskId, userId);
}
示例:
<?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/test">
<process id="process2" name="process2" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="审批" activiti:assignee="${userId}"></userTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow>
<serviceTask id="servicetask1" name="不同意" activiti:class="com.lry.delegate.TaskDelegate">
<extensionElements>
<activiti:field name="isAgree">
<activiti:expression><![CDATA[${isAgree}]]></activiti:expression>
</activiti:field>
</extensionElements>
</serviceTask>
<sequenceFlow id="flow3" sourceRef="exclusivegateway1" targetRef="servicetask1">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${isAgree == 0}]]></conditionExpression>
</sequenceFlow>
<serviceTask id="servicetask2" name="同意" activiti:class="com.lry.delegate.TaskDelegate">
<extensionElements>
<activiti:field name="nextParam">
<activiti:expression><![CDATA[${nextParam}]]></activiti:expression>
</activiti:field>
<activiti:field name="isAgree">
<activiti:expression><![CDATA[${isAgree}]]></activiti:expression>
</activiti:field>
</extensionElements>
</serviceTask>
<sequenceFlow id="flow4" sourceRef="exclusivegateway1" targetRef="servicetask2">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${isAgree == 1}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="servicetask2" targetRef="endevent1"></sequenceFlow>
<sequenceFlow id="flow6" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_process2">
<bpmndi:BPMNPlane bpmnElement="process2" id="BPMNPlane_process2">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="120.0" y="150.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="230.0" y="140.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="670.0" y="151.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
<omgdc:Bounds height="40.0" width="40.0" x="395.0" y="148.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask1" id="BPMNShape_servicetask1">
<omgdc:Bounds height="55.0" width="105.0" x="500.0" y="230.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="servicetask2" id="BPMNShape_servicetask2">
<omgdc:Bounds height="55.0" width="105.0" x="490.0" y="60.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="155.0" y="167.0"></omgdi:waypoint>
<omgdi:waypoint x="230.0" y="167.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="335.0" y="167.0"></omgdi:waypoint>
<omgdi:waypoint x="395.0" y="168.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="415.0" y="188.0"></omgdi:waypoint>
<omgdi:waypoint x="552.0" y="230.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="415.0" y="148.0"></omgdi:waypoint>
<omgdi:waypoint x="542.0" y="115.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
<omgdi:waypoint x="542.0" y="115.0"></omgdi:waypoint>
<omgdi:waypoint x="687.0" y="151.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
<omgdi:waypoint x="552.0" y="230.0"></omgdi:waypoint>
<omgdi:waypoint x="687.0" y="186.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
我在【同意】和【不同意】task上绑定了上面的同一个类,并且,添加了两个字段属性,这两个属性的获取方式看下面代码
service task的监听类需要实现JavaDelegate:
public class TaskDelegate implements JavaDelegate{
// 这里的名称和流程图里配置的字段名称一样
// 而传递的参数值是expression的表达式
private Expression nextParam;
private Expression isAgree;
@Override
public void execute(DelegateExecution execution) {
System.out.println("执行 taskDelegate======== ");
String value = (String)nextParam.getValue(execution);
Integer s = (Integer)isAgree.getValue(execution);
// 第一种获取方式
System.out.println("nextParam 参数值:"+value);
System.out.println("isAgree 参数值: " + s);
// 第二种获取方式
System.out.println("2 isAgree " + execution.getVariable("isAgree"));
System.out.println("2 nextParam " + execution.getVariable("nextParam"));
System.out.println("审批同意后 执行相应业务");
}
}
执行完审批任务后,他会根据判断isAgree的参数,然后进入TaskDelegate的execute的方法。
参数:
{
"taskId": "200007",
"userId": "bli",
"comment": "ok",
"variable": {
"isAgree": 1,
"nextParam": "oklsdsdsd"
}
}
常用的应该就是通过用户id查询任务了,这个查询能查询到已经处理完的,和未处理的,包括了所有的任务。
/**
* 通过用户id查询历史任务
* @param userId
* @return
*/
public List<HistoricTaskInstance> history(String userId) {
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().taskAssignee(userId).list();
System.out.println(JSON.toJSONString(list));
return list;
}
// 全局监听器
ExecutionListener
ActivitiEventListener 3.17.1
TaskListener
接口中定义了3个事件名称,用来标识流程的开始、结束和节点连线的。
这三个状态以常亮方式定义在BaseExecutionListener
String EVENTNAME_START = "start";
String EVENTNAME_END = "end";
String EVENTNAME_TAKE = "take";
实现的方法notify
提供了DelegateExecution
对象,它可以操作流程执行对象,具体操作方法可以查看他的API。
void notify(DelegateExecution delegateExecution)
它的使用方式和service task差不多。
实现监听器:
public class CustomExecutionListener implements ExecutionListener {
private static final long serialVersionUID = 1961119667797372483L;
private Expression message;
public Expression getMessage() {
return message;
}
public void setMessage(Expression message) {
this.message = message;
}
@Override
public void notify(DelegateExecution delegateExecution) {
String eventName = delegateExecution.getEventName();
if (BaseExecutionListener.EVENTNAME_START.equals(eventName)) {
System.out.println("-----------ExecutionListener start----------------");
} else if (BaseExecutionListener.EVENTNAME_END.equals(eventName)) {
System.out.println("-----------ExecutionListener end----------------");
} else {
System.out.println("-----------ExecutionListener take----------------");
}
}
}
下面例子使用上面流程图,不同的是添加了监听器
流程图:
<?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/test">
<process id="myProcess" name="My process" isExecutable="true">
<extensionElements>
<activiti:executionListener event="end" class="com.lry.listener.CustomExecutionListener">
<activiti:field name="message">
<activiti:string><![CDATA[流程结束]]></activiti:string>
</activiti:field>
</activiti:executionListener>
<activiti:executionListener event="start" class="com.lry.listener.CustomExecutionListener">
<activiti:field name="message">
<activiti:string><![CDATA[流程启动]]></activiti:string>
</activiti:field>
</activiti:executionListener>
</extensionElements>
<startEvent id="startevent1" name="Start">
<extensionElements>
<activiti:executionListener event="MyExecutionListener " class="com.lry.listener.CustomExecutionListener"></activiti:executionListener>
</extensionElements>
</startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<userTask id="user_task_1" name="组长审批" activiti:assignee="${user1}"></userTask>
<userTask id="user_task_2" name="领导审批" activiti:assignee="${user2}"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="user_task_1">
<extensionElements>
<activiti:executionListener event="take" class="com.lry.listener.CustomExecutionListener">
<activiti:field name="message">
<activiti:string><![CDATA[第一环节]]></activiti:string>
</activiti:field>
</activiti:executionListener>
</extensionElements>
</sequenceFlow>
<sequenceFlow id="flow2" sourceRef="user_task_1" targetRef="user_task_2">
<extensionElements>
<activiti:executionListener event="take" class="com.lry.listener.CustomExecutionListener"></activiti:executionListener>
</extensionElements>
</sequenceFlow>
<sequenceFlow id="flow3" sourceRef="user_task_2" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="345.0" y="20.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="345.0" y="280.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="user_task_1" id="BPMNShape_user_task_1">
<omgdc:Bounds height="55.0" width="105.0" x="310.0" y="70.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="user_task_2" id="BPMNShape_user_task_2">
<omgdc:Bounds height="55.0" width="105.0" x="310.0" y="181.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="362.0" y="55.0"></omgdi:waypoint>
<omgdi:waypoint x="362.0" y="70.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="362.0" y="125.0"></omgdi:waypoint>
<omgdi:waypoint x="362.0" y="181.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="362.0" y="236.0"></omgdi:waypoint>
<omgdi:waypoint x="362.0" y="280.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
操作步骤:
CustomExecutionListener
Expression
类型的属性,还可以新增【fields】,这里配置field作用不是很大,也不好说明,在下一个任务监听器配置时再做说明。执行方法:
public String initProcess1() {
System.out.println("发起流程....");
Deployment dep = repositoryService.createDeployment().name("测试流程")
.addClasspathResource("processes/testProcess.bpmn").deploy();
// 读取xml方式
// Deployment dep = repositoryService.createDeployment().name("测试流程")
// .addClasspathResource("processes/test.xml").deploy();
System.out.println("部署id:" + dep.getId());
System.out.println("部署名称:" + dep.getName());
System.out.println("-----------------");
identityService.setAuthenticatedUserId("startUser");
Map<String, Object> map = new HashMap<>();
map.put("user1", "ali");
ExecutionEntity pi = (ExecutionEntity) runtimeService.startProcessInstanceByKey("myProcess", map);
System.out.println("启动流程成功!");
System.out.println("流程实例id:" + pi.getId());
System.out.println("流程定义id:" + pi.getProcessInstanceId());
System.out.println("流程实例名称:" + pi.getName());
System.out.println("-------------------------------------");
// 查询任务
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
System.out.println("任务ID: " + task.getId());
System.out.println("任务的办理人: " + task.getAssignee());
System.out.println("任务名称: " + task.getName());
System.out.println("任务的创建时间: " + task.getCreateTime());
System.out.println("流程实例ID: " + task.getProcessInstanceId());
System.out.println("发起成功....");
return "success";
}
执行结果:
启动流程的时候会触发start时间,然后触发take事件(连线)然后到下一个环节(节点)
任务监听器,event支持:create、assignment、complete、delete四种类型,
还有一种就是all
,它是上面四种的集合,就是我们在配置是,可以配置这五个值。
这四种类型以常亮方式定义在BaseTaskListener
;
继续上面的例子,我们再新增一个监听器,实现TaskListener
接口。
public class CustomTaskListener implements TaskListener {
private Expression taskFlag;
@Override
public void notify(DelegateTask delegateTask) {
String eventName = delegateTask.getEventName();
System.out.println("任务监听器:" + eventName);
String value = (String)taskFlag.getValue(delegateTask);
System.out.println("taskFlag:" + value);
// 获取变量:user2的值
String user2 = String.valueOf(delegateTask.getVariable("user2"));
System.out.println("user2: " + user2);
}
}
event支持:create、assignment、complete、delete、all五个值
然后发起部署请求,并完成任务;
方法参数:{ “taskId”:“207518”, “userId”:“ali”, “comment”:“ok”, “variable”: { “user2”: “bli” } }
修改event为all(需要重新部署),支持所有事件类型,可以看出assignment是在create事件之前的
在很多时候,是在任务监听器中调用我们自己的dao层去查询操作数据库,比如下面代码获取的user2是某个用户id,然后通过spring工具获取dao的bean,然后通过这个bean根据id查询是否有这个用户或是
// 获取变量:user2的值
String user2 = String.valueOf(delegateTask.getVariable("user2"));
使用监听器测试的流程图;
public InputStream tracePhoto(String proInstanceId) {
// 通过历史表查询历史流程
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(proInstanceId).singleResult();
if (historicProcessInstance == null) {
return null;
}
// 通过历史流程里的定义id查询bpmn
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 获取激活的任务节点
List<Task> list = taskService.createTaskQuery().processInstanceId(proInstanceId).list();
List<String> activeActivityIds = new ArrayList<>();
for (Task task : list) {
activeActivityIds.add(task.getTaskDefinitionKey());
}
// 这里设置字体避免乱码
return processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator().generateDiagram(bpmnModel, "png", activeActivityIds, new ArrayList<String>(), "宋体", "宋体", "宋体", null, 1.0);
}
这里需要注意的是要设置字体为宋体,网上朋友说因为不存在arial字体,所以生成的图片是乱码的,我尝试断点看了下,在生成图片的类中,默认是arial。
activiti支持uel-value 、uel-method两种方式,uel-method用的不多,不做说明;
对于uel-value的也可以这样:${day > 3}
uel表达式设置变量有以下4个注意点:
简单改造上面流程如下,从组长审批分两个支路,前面有一个类似的使用service task实现,这里是通过变量来进行分流的。
<?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/test">
<process id="uelKey" name="testUel" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="level1" name="组长审批" activiti:assignee="${user1}"></userTask>
<userTask id="level2" name="领导审批" activiti:assignee="${user2}"></userTask>
<userTask id="level3" name="经理审批" activiti:assignee="${user3}"></userTask>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="level1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="level1" targetRef="level2">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${day <= 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow3" sourceRef="level1" targetRef="level3">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${day > 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" sourceRef="level2" targetRef="endevent1"></sequenceFlow>
<sequenceFlow id="flow5" sourceRef="level3" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_uelKey">
<bpmndi:BPMNPlane bpmnElement="uelKey" id="BPMNPlane_uelKey">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="1160.0" y="240.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="level1" id="BPMNShape_level1">
<omgdc:Bounds height="55.0" width="105.0" x="1125.0" y="350.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="level2" id="BPMNShape_level2">
<omgdc:Bounds height="55.0" width="105.0" x="1040.0" y="500.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="level3" id="BPMNShape_level3">
<omgdc:Bounds height="55.0" width="105.0" x="1220.0" y="500.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="1160.0" y="640.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="1177.0" y="275.0"></omgdi:waypoint>
<omgdi:waypoint x="1177.0" y="350.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="1177.0" y="405.0"></omgdi:waypoint>
<omgdi:waypoint x="1092.0" y="500.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="1177.0" y="405.0"></omgdi:waypoint>
<omgdi:waypoint x="1272.0" y="500.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="1092.0" y="555.0"></omgdi:waypoint>
<omgdi:waypoint x="1177.0" y="640.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
<omgdi:waypoint x="1272.0" y="555.0"></omgdi:waypoint>
<omgdi:waypoint x="1177.0" y="640.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
分别选中组长审批分出的线,设置其变量条件
当传入如下的参数day<=3,就会进入领导审批,反之进入经理审批