引擎 API 是与 Flowable 交互的最常见方式。主要起点是 ProcessEngine,它可以通过多种方式创建,如配置部分所述。从 ProcessEngine,您可以获得包含工作流/BPM 方法的各种服务。ProcessEngine 和 services 对象是线程安全的,因此您可以为整个服务器保留对其中一个的引用。
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//处理启动流程定义的新流程实例
RuntimeService runtimeService = processEngine.getRuntimeService();
//提供用于管理和操作部署和流程定义的操作
RepositoryService repositoryService = processEngine.getRepositoryService();
//对流程任务进行管理,例如任务提醒、任务完成和创建任务等。
TaskService taskService = processEngine.getTaskService();
//提供对流程引擎进行管理和维护的服务。
ManagementService managementService = processEngine.getManagementService();
//支持组合用户的管理(创建、更新、删除、查询等操作)
IdentityService identityService = processEngine.getIdentityService();
//存储所有的历史流程数据
HistoryService historyService = processEngine.getHistoryService();
//可选服务,该服务引入了启动表单和任务表单的概念
FormService formService = processEngine.getFormService();
//用于改变流程定义的一部分,无需重新部署
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
常用类:
ProcessEngines–流程引擎管理类
ProcessEngine–流程引擎类
ProcessEngineImpl–流程引擎实现类
ProcessEngineConfiguration–流程引擎配置类
ProcessEngineInfo–流程引擎信息类
ProcessEngines
//key:流程引擎的名称 value:流程引擎的实例对象 Map<String, ProcessEngine> processEngines = new HashMap<>(); //key:流程引擎的名称 value:流程引擎信息类的实例对象 Map<String, EngineInfo> processEngineInfosByName = new HashMap<>(); //key:流程引擎资源的名称 value:流程引擎信息类的实例对象 Map<String, EngineInfo> processEngineInfosByResourceUrl = new HashMap<>(); //存储流程引擎信息类实例对象 List<EngineInfo> processEngineInfos = new ArrayList<>();
初始化时类加载器默认加载:根路径下的 flowable.cfg.xml
flowable和activiti的对比
1、flowable已经支持所有的历史数据使用mongdb存储,activiti没有。
2、flowable支持事务子流程,activiti没有。
3、flowable支持多实例加签、减签,activiti没有。
4、flowable支持httpTask等新的类型节点,activiti没有。
5、flowable支持在流程中动态添加任务节点,activiti没有。
6、flowable支持历史任务数据通过消息中间件发送,activiti没有。
7、flowable支持java11,activiti没有。
8、flowable支持动态脚本,,activiti没有。
9、flowable支持条件表达式中自定义juel函数,activiti没有。
10、flowable支持cmmn规范,activiti没有。
11、flowable修复了dmn规范设计器,activit用的dmn设计器还是旧的框架,bug太多。
12、flowable屏蔽了pvm,activiti6也屏蔽了pvm(因为6版本官方提供了加签功能,发现pvm设计的过于臃肿,索性直接移除,这样加签实现起来更简洁、事实确实如此,如果需要获取节点、连线等信息可以使用bpmnmodel替代)。
13、flowable与activiti提供了新的事务监听器。activiti5版本只有事件监听器、任务监听器、执行监听器。
14、flowable对activiti的代码大量的进行了重构。
15、activiti以及flowable支持的数据库有h2、hsql、mysql、oracle、postgres、mssql、db2。其他数据库不支持的。使用国产数据库的可能有点失望了,需要修改源码了。
16、flowable支持jms、rabbitmq、mongodb方式处理历史数据,activiti没有。
支持的数据库:AbstractEngineConfiguration
/** * **************************************************************************************************** * 1.RepositoryService * 仓库服务类,定义bpmn文件和png图片 * 流程图片的生产方式 * (1)设计流程图的时候产生 * (2)通过API的方式根据流程文件生成 * 引擎配置类构造代码ProcessEngineConfigurationImpl * 注意:流程引擎类(ProcessEngineImpl)通过流程引擎配置类(ProcessEngineConfigurationImpl)来获取服务对象的 * **************************************************************************************************** * RepositoryService * (可以派生的类:DeploymentBuilder、ProcessDefinitionQuery、NativeProcessDefinitionQuery、DeploymentQuery) * (1)DeploymentBuilder:用来定义流程部署相关的参数。 * (2)ProcessDefinitionQuery:用来构造查询流程定义相关的参数。 * (3)NativeProcessDefinitionQuery:用来构造本地SQL查询流程定义相关的参数。 * (4)NativeProcessDefinitionQuery:用来构造查询部署对象相关的参数。 * ***************************************************************************************************** * */
/**
* ****************************************************************************************************
* 1.RepositoryService
* 仓库服务类,定义bpmn文件和png图片
* 流程图片的生产方式
* (1)设计流程图的时候产生
* (2)通过API的方式根据流程文件生成
* 引擎配置类构造代码ProcessEngineConfigurationImpl
* 注意:流程引擎类(ProcessEngineImpl)通过流程引擎配置类(ProcessEngineConfigurationImpl)来获取服务对象的
* ****************************************************************************************************
* RepositoryService
* (可以派生的类:DeploymentBuilder、ProcessDefinitionQuery、NativeProcessDefinitionQuery、DeploymentQuery)
* (1)DeploymentBuilder:用来定义流程部署相关的参数。
* (2)ProcessDefinitionQuery:用来构造查询流程定义相关的参数。
* (3)NativeProcessDefinitionQuery:用来构造本地SQL查询流程定义相关的参数。
* (4)NativeProcessDefinitionQuery:用来构造查询部署对象相关的参数。
* *****************************************************************************************************
* */
public void repositoryService(){
//获取流程仓库引擎的服务类
RepositoryService repositoryService= processEngine.getRepositoryService();
//用来定义流程部署相关的参数
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
//用来构造查询流程定义相关的参数
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
//用来构造本地SQL查询流程定义相关的参数
NativeProcessDefinitionQuery nativeProcessDefinitionQuery =
repositoryService.createNativeProcessDefinitionQuery();
//用来构造查询部署对象相关的参数
DeploymentQuery deploymentQuery = repositoryService.createDeploymentQuery();
}
//用来定义流程部署相关的参数
DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
deploymentBuilder
.category("学习flowable测试分类")
.name("自定义资源部署的名字")
.tenantId("设置租户ID")
.addClasspathResource("bpmn文件路径(在根路径下直接写文件全名)")
.deploy();
*************************************************************************************************************
/**
* 文本方式部署
* 资源的名称必须是"bpmn20.xml", "bpmn"这两个结尾的才能部
* 署到流程定义表中(act_re_procdef),不然只有部署表中才有,
* 定义表中没有相应的数据
* */
@Test
public void testDeployByText(){
String text= IoUtil.readFileAsString("days.xml");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("文本方式部署资源")
.category("测试文本部署分类")
.key("设置key值")
.tenantId("租户的ID")
.addString("days.bpmn", text)//
.deploy();
System.out.println("获取部署对象的id="+deploy.getId());
}
*************************************************************************************************************
/**
* 输入流的方式部署
* */
@Test
public void testDeploymentByInputStream(){
//InputStream resourceAsStream = GetProcessEngine.class.getClassLoader().getResourceAsStream("days.xml");
InputStream resource =getClass().getClassLoader().getResourceAsStream("days.xml");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("测试输入流的方式部署")
.category("测试输入流方式部署分类")
.key("key值")
.tenantId("租户的ID")
.addInputStream("inputStream.bpmn", resource)
.deploy();
System.out.println("输入流的方式部署获取部署对象的id="+deploy.getId());
}
*************************************************************************************************************
/**
* 压缩流的方式部署资源
* */
@Test
public void testDeploymentByZipInputStream(){
InputStream resourceAsStream = GetProcessEngine.class.getClassLoader().getResourceAsStream("resources.zip");
//InputStream resource =getClass().getClassLoader().getResourceAsStream("resources.zip");
ZipInputStream zipInputStream = new ZipInputStream(resourceAsStream);
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("测试压缩流的方式部署")
.category("测试压缩流方式部署分类")
.key("压缩key值")
.tenantId("租户的ID压缩")
.addZipInputStream(zipInputStream)
.deploy();
System.out.println("输入流的方式部署获取部署对象的id="+deploy.getId());
}
*************************************************************************************************************
/**
* 字节的方式部署资源
* */
@Test
public void testDeploymentByBytes(){
InputStream resourceAsStream = GetProcessEngine.class.getClassLoader().getResourceAsStream("days.xml");
//InputStream resource =getClass().getClassLoader().getResourceAsStream("days.xml");
byte[] bytes =IoUtil.readInputStream(resourceAsStream,"Bytes InputStream");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("测试字节流的方式部署")
.category("测试字节流方式部署分类")
.key("字节key值")
.tenantId("字节租户的ID")
.addBytes("happy.bpmn",bytes)
.deploy();
System.out.println("字节的方式部署获取部署对象的id="+deploy.getId());
}
*************************************************************************************************************
1.通过输入流的方式部署
DeploymentBuilder addInputStream(String resourceName, InputStream inputStream);
2.同过根路径(classpath)的方式部署
DeploymentBuilder addClasspathResource(String resource);
3.通过文本的方式部署
DeploymentBuilder addString(String resourceName, String text);
4.通过字节放是部署
DeploymentBuilder addBytes(String resourceName, byte[] bytes);
5.通过压缩流的方式部署(一次可以部署多个文件)
DeploymentBuilder addZipInputStream(ZipInputStream zipInputStream);
6.通过模型的方式部署
DeploymentBuilder addBpmnModel(String resourceName, BpmnModel bpmnModel);
文本的形式部署资源的名称必须是bpmn20.xml或者bpmn结束的
act_re_deployment(部署对象表)
存放流程定义的信息(如名字、类别、租户ID等信息),每部署一次多一条记录。
act_re_procdef(流程定义表)
存放流程定义的属性信息,没部署一个新的流程就会新增一条记录,当流程定义的key相同的话,没部署一次版本号加一(对应数据库字段 “REV_ ” 默认版本号从1开始)。
act_ge_bytearray(资源文件表)
以二进制的形式存储流程定义中部署的bpmn文件和png图片(注:如果只是指定了bpmn文件没有png图片,flowable会在部署解析bpmn文件时生成流程图保存在该表中)。
/**
* 查询流程定义信息
* */
@Test
public void createProcessDefinitionQuery(){
List<ProcessDefinition> processDefinitions = processEngine.getRepositoryService()
.createProcessDefinitionQuery()
//.latestVersion()//根据最后一个版本查询
//.deploymentId("部署的ID")//根据部署的ID查询
//.processDefinitionCategory("类别")//根据类别查询
.list();
for (ProcessDefinition processDefinition : processDefinitions) {
System.out.println("获取流程定义的ID:"+processDefinition.getId());
System.out.println("获取流程定义的名字:"+processDefinition.getName());
System.out.println("获取租户ID:"+processDefinition.getTenantId());
System.out.println("获取key:"+processDefinition.getKey());
System.out.println("获取流程定义的类别:"+processDefinition.getCategory());
}
}
/**
* 流程定义的删除
* */
@Test
public void deleteDeployment(){
//根据部署ID删除
processEngine.getRepositoryService().deleteDeployment("40001");
//根据部署ID级联删除(会删除当前流程定义下的所有流程实例)
processEngine.getRepositoryService().deleteDeployment("30001",true);
}
/**
* 获取流程定义的资源文件(图片)
* */
@Test
public void getDeploymentResource() throws IOException {
String deploymentId="32501";
List<String> names = processEngine
.getRepositoryService()
.getDeploymentResourceNames(deploymentId);
String imageName=null;
for (String name : names) {
if (name.indexOf(".png")>0){
imageName=name;
}
}
if (imageName!=null){
File file = new File("F:\\自己1\\MyCompany\\flowable"+imageName);
InputStream resourceAsStream = processEngine.getRepositoryService()
.getResourceAsStream(deploymentId, imageName);
FileUtils.copyInputStreamToFile(resourceAsStream,file);
}
}
/**
* 自定义SQL本地查询
* */
@Test
public void definitionSQLQuery(){
//根据部署ID删除
List<Deployment> deployments = processEngine.getRepositoryService()
.createNativeDeploymentQuery()
.sql("select*from act_re_deployment")
.list();
for (Deployment deployment : deployments) {
System.out.println("部署ID="+deployment.getId());
System.out.println("部署名称="+deployment.getName());
}
}
1.flowable.cfg.xml加如这条属性
<property name="idBlockSize" value="1000">property>
**************************************************************************************************
2.UUID的生成策略(flowable.cfg.xml加如这条属性)
<property name="idGenerator" ref="strongUuidGenerator">property>
创建一个bean
<bean id="strongUuidGenerator" class="org.flowable.common.engine.impl.persistence.StrongUuidGenerator">bean>
***************************************************************************************************
3.自定义主键的生成策略
创建自定义类
/**
* @author lb
* @date 2021-06-18 9:18
* @description 自定义ID生成器
*/
public class strongUUIDGenerator implements IdGenerator {
@Override
public String getNextId() {
String id="luobo"+UUID.randomUUID().toString();
return id;
}
}
将该类通过bean注入到xml文件中
<bean id="strongUuidGenerator" class="com.lb.config.strongUUIDGenerator">bean>
在flowable的配置bean中引入
<property name="idGenerator" ref="strongUuidGenerator">property>
启动流程实例的方式
1.通过给定的key值启动 ProcessInstance startProcessInstanceByKey(String processDefinitionKey) 2.通过流程定义的ID启动 ProcessInstance startProcessInstanceById(String processDefinitionId); 3.通过流程定义的ID和租户的ID启动 ProcessInstance startProcessInstanceByKeyAndTenantId(String processDefinitionKey, String tenantId); 4.根据message启动 ProcessInstance startProcessInstanceByMessage(String messageName); 5.根据message和租户ID启动 ProcessInstance startProcessInstanceByMessageAndTenantId(String messageName, String tenantId);
一个流程中,执行对象可以有多个,但是执行流程实例至于一个
启动一个实例–>执行实例–>更新实例节点–>实例结束
操作的数据库表为act_ru_execution,当处于用户节点时,会在表act_ru_task中插入一条数据。
执行实例走到任务节点的时候,会暂时处于等待状态,只有当该节点的任务完成后,才能继续流程的运转。
/**
* 执行流程实例
* */
@Test
public void startProcessInstance(){
ProcessInstance processInstance = processEngine
.getRuntimeService()
.startProcessInstanceById("days:8:luobob524e831-0711-4c66-8c88-8e46b557734e");
System.out.println("流程实例ID="+processInstance.getId());
System.out.println("流程实例名字="+processInstance.getName());
}
一个task节点(act_ru_task)和Execution(act_ru_execution)是一对一的关系
/**
*查询任务
* */
@Test
public void queryProcessInstance(){
List<Task> tasks = processEngine
.getTaskService()
.createTaskQuery()
.list();
// .processDefinitionId("luobofa67b3ff-5459-4682-882a-12902ed608a5");
for (Task task : tasks) {
System.out.println("任务名字:"+task.getName());
System.out.println("任务ID="+task.getId());
System.out.println("任务分类:"+task.getCategory());
}
}
任务完成后,该节点的任务会被删除,插入下个节点的任务信息,同时将删除的任务节点信息插入到历史记录表中去。(在同一个事物中操作)。
当所有的任务节点都完成表act_ru_task中就没有数据了。
/**
* 完成任务
*
* */
@Test
public void completeTaskOneByOne(){
String name="经理审批";
List<Task> tasks = processEngine.getTaskService().createTaskQuery().taskName(name).list();
LinkedHashMap<String, Object> variables = new LinkedHashMap<>();
variables.put("message","同意");
if (!CollectionUtils.isEmpty(tasks)){
for (Task task : tasks) {
System.out.println("获取的任务节点ID="+task.getId());
processEngine.getTaskService().complete(task.getId(),variables);
}
}
}
1.在流程的执行过程中,创建的历程实例ID在整个过程中都不会改变,当流程结束后,流程实例将会在正正在执行的执行对象表中(act_ru_execution)被删除。
2.由于一个流程实例ID对应一个实例,所以使用singleResult()执行查询返回唯一的结果,如果返回的结果数量大于1,会抛出异常。
3.判断指定的ID实例是否存在,如果为空就会代表执行流程结束,在执行表中被删除,存到历史数据表中去。
/**
* 查询流程执行的状态(判断流程执行是否结束)
* */
@Test
public void queryProcessInstanceStatus(){
ProcessInstance processInstance =
processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId("luobo1df98a85-df89-4c0f-bdfd-276448b4732e")
.singleResult();
if (processInstance==null){
System.out.println("流程实例执行完毕!");
}else {
System.out.println("流程实例还在执行中!");
}
}
/**
* 查询执行实例
* */
@Test
public void queryExecuteInstance(){
List<Execution> executions= processEngine.getRuntimeService()
.createExecutionQuery()
.list();
if(!CollectionUtils.isEmpty(executions)){
for (Execution execution : executions) {
System.out.println("执行实例的ID="+execution.getId());
System.out.println("执行实例的名字="+execution.getName());
}
}else {
System.out.println("执行实例不存在!");
}
}
历史节点活动数据存储再act_hi_actinst表中
/**
* 历史流程查询
*/
@Test
public void queryHistoryProcess() {
HistoricProcessInstance history = processEngine.getHistoryService()
.createHistoricProcessInstanceQuery()
.processInstanceId("luobo1df98a85-df89-4c0f-bdfd-276448b4732e")
.singleResult();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
System.out.println("流程定义ID"+history.getProcessDefinitionId());
System.out.println("开始时间:"+simpleDateFormat.format(history.getStartTime()));
System.out.println("结束时间:"+simpleDateFormat.format(history.getEndTime()));
}
历史任务表的数据和运行任务表的数据一起插入数据库的,并且在一个事物里面执行的。
如果表act_hi_taskinst中的END_TIME_(结束时间)字段有值,那说明任务已经完成了,没有值说明任务还在执行。
/**
* 查询所有的历史任务
*/
@Test
public void queryHistoryTaskAll() {
List<HistoricTaskInstance> taskInstanceList = processEngine.getHistoryService()
.createHistoricTaskInstanceQuery()
.list();
for (HistoricTaskInstance historicTaskInstance : taskInstanceList) {
System.out.println("历史任务的ID="+historicTaskInstance.getId());
}
}
说明:
initiator跟任务的发起者配合使用才会有效果,否则没有意义。
* 获取表单的属性
* */
@Test
public void getProperties(){
StartFormData startFormData = processEngine.getFormService().getStartFormData("formService:1:99004");
System.out.println(startFormData);
System.out.println("获取流程定义"+startFormData.getProcessDefinition());
List<FormProperty> formProperties = startFormData.getFormProperties();
for (FormProperty formProperty : formProperties) {
System.out.println("获取id:"+formProperty.getId());
System.out.println("获取名字:"+formProperty.getName());
System.out.println("获取类型:"+formProperty.getType());
System.out.println("获取值:"+formProperty.getValue());
}
}
/**
* 流程任务的发起人
*/
@Test
public void identityPeople() {
//(方式一)设置流程的发起人
String authenticatedUserId="luobo";
processEngine.getIdentityService().setAuthenticatedUserId(authenticatedUserId);
//(方式二)
//Authentication.setAuthenticatedUserId(authenticatedUserId);
//启动流程实例
ProcessInstance processInstance = processEngine
.getRuntimeService()
.startProcessInstanceById("days:8:luobob524e831-0711-4c66-8c88-8e46b557734e");
System.out.println("流程实例ID=" + processInstance.getId());
System.out.println("流程实例名字=" + processInstance.getName());
}
删除后历史表中的数据还是存在的
/**
* 流程实例的删除
* */
@Test
public void deleteProcessInstance() {
String processInstanceId="luoboab5875a3-576a-4503-8b9e-c983503d84a0";
String deleteReason="测试通过RuntimeService删除流程实例";
processEngine.getRuntimeService().deleteProcessInstance(processInstanceId,deleteReason);
}
级联删除:删除后历史表中的数据不存在了
/**
* 获取流程运行的活动节点
* */
@Test
public void gainActivityPoint() {
String executionId="luobo819bd398-9985-4a9b-aefc-3c5e3c49ffba";
List<String> activeActivityIds = processEngine.getRuntimeService().getActiveActivityIds(executionId);
for (String activeActivityId : activeActivityIds) {
System.out.println(activeActivityId);
}
}
//结果:manager
流程实例挂起后流程实例下面的所有执行实例都会被挂起
/**
* 查看流程定义是否挂起
* */
@Test
public void processDefinitionSuspended() {
String processDefinitionId="days:1:27504";
boolean suspended =
processEngine.getRepositoryService().isProcessDefinitionSuspended(processDefinitionId);
System.out.println(suspended);//false:没有挂起 true:挂起
}
/**
* 使流程定义挂起
*(SUSPENSION_STATE_)状态为2被挂起
*(SUSPENSION_STATE_) 状态为1没有被挂起
* 流程被挂起后是不能够启动流程实例的(启动会报错)
* */
@Test
public void processDefinitionSuspended() {
String processDefinitionId="days:1:27504";
//根据指定日期挂起
//processEngine.getRepositoryService().suspendProcessDefinitionById(String processDefinitionId, boolean suspendProcessInstances, Date suspensionDate);
processEngine.getRepositoryService().suspendProcessDefinitionById(processDefinitionId);
}
/**
* 解挂,激活流程定义
* */
@Test
public void releaseProcessDefinitionSuspended() {
String processDefinitionId="days:1:27504";
processEngine.getRepositoryService().activateProcessDefinitionById(processDefinitionId);
}
开始节点时每个流程实例的起点,开始节点只能有一个,不能有多个,有多个开始节点部署会报错。
在一个部署文件中结束节点可以存在多个,当流程实例运行到结束节点的时候,则表示当前的执行实例结束了,执行实例结束那么流程实力也就结束了。
flowable中有事件、类型、网关节点。
事件节点:开始节点事件、结束节点事件。
活动类型:人工任务节点、服务任务节点、用户任务节点等。
活动节点大致分为:
等待节点—实例走到这个节点的时候,会停留在这里,需要我们手动处理去完成当前的节点,这样流程实例才会往下走。
非等待节点—实例走到该节点,会直接完成这个节点的任务,然后实例继续往下走。
接收任务会等待消息的到达,当流程到达结束任务时,流程的状态会保存到数据库中。
任务创建后流程进入等待状态,直到引擎接收了一个特定的消息,这会触发流程穿过接收任务继续执行。
/**
* 部署资源
* */
@Test
public void deployProcess(){
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService
.createDeployment()
.name("超市营业额报备(接收任务)")
.category("money")
.tenantId("myCompany")
.key("今天收益多少")
.addClasspathResource("money.png")
.addClasspathResource("money.bpmn")
.deploy();
System.out.println("部署流程名字:" + deployment.getName());
System.out.println("部署流程的id:" + deployment.getId());
}
/**
* 执行流程实例
*/
@Test
public void startProcessInstance() {
ProcessInstance processInstance = processEngine
.getRuntimeService()
.startProcessInstanceById("receive:2:51004");
System.out.println("流程实例ID=" + processInstance.getId());
System.out.println("流程实例名字=" + processInstance.getName());
}
/**
* 查询流程执行实例
* sql:Preparing: select distinct RES.* , P.KEY_ as ProcessDefinitionKey, P.ID_ as ProcessDefinitionId
* from ACT_RU_EXECUTION RES inner join ACT_RE_PROCDEF P on RES.PROC_DEF_ID_ = P.ID_
* WHERE RES.ACT_ID_ = ? and RES.IS_ACTIVE_ = ? order by RES.ID_ asc
* */
@Test
public void queryExecutionInstance(){
//活动ID,表act_ru_execution中的ACT_ID_字段
String activityId="count";
Execution execution = processEngine.getRuntimeService()
.createExecutionQuery()
.activityId(activityId)
.singleResult();
System.out.println("执行实例的名字"+execution.getName());
System.out.println("执行实例的id"+execution.getId());
}
/**
* 完成接收任务
* 触发执行实例继续往下节点走(将外部触发器发送到在给定执行中等待的活动实例)
* 触发需要的参数是执行实例的ID(表act_tu_execution),不是流程实例的ID
* */
@Test
public void trigger(){
//活动ID,表act_ru_execution中的ACT_ID_字段
String executionId="52002";
processEngine.getRuntimeService().trigger(executionId);
}
主要分为个人任务和任务组
个人任务:指定某个确定的办理者,这个任务就是私有任务,即个人任务。
任务组:没法将这个任务指定个某个具体的人,但是可以分配到多个人活着一到多个小组中去,让小组中的成员来办理。
部署资源
/**
* 部署个人任务
* */
@Test
public void deploy(){
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("个人任务")
.category("个人任务分配")
.key("personTask")
.tenantId("person")
.addClasspathResource("personaltask.bpmn")
.addClasspathResource("personaltask.png")
.deploy();
System.out.println("获取部署对象的id=" + deploy.getId());
System.out.println("获取key" + deploy.getKey());
}
执行任务
/**
* 执行任务
* */
@Test
public void startProcessInstance(){
ProcessInstance task = processEngine.getRuntimeService().startProcessInstanceById("task:1:55004");
System.out.println("流程实例ID=" + task.getId());
System.out.println("流程实例名字=" + task.getName());
}
查询某个人需要处理的任务
/**
* 查询某个人需要处理的任务
*select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = ? order by RES.ID_ asc
* */
@Test
public void queryTaskByPeople(){
String userName="lb";
List<Task> tasks = processEngine.getTaskService()
.createTaskQuery()
.taskAssignee(userName)
.list();
for (Task task : tasks) {
System.out.println("任务名称:"+task.getName());
System.out.println("任务的ID:"+task.getId());
}
}
部署资源
/**
* 输入流的方式部署
* */
@Test
public void testDeploymentByInputStream(){
//InputStream resourceAsStream = GetProcessEngine.class.getClassLoader().getResourceAsStream("days.xml");
InputStream resource =getClass().getClassLoader().getResourceAsStream("personaltask2.xml");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("动态分配任务处理人")
.category("任务处理")
.key("flowable")
.tenantId("租户的ID")
.addInputStream("myTask.bpmn", resource)
.deploy();
System.out.println("输入流的方式部署获取部署对象的id="+deploy.getId());
}
执行任务,设置流程变量,指定任务的执行人
/**
* 执行任务
* 因为在部署流程的xml文件中设置了任务的处理人变量userId,如果此处不设置执行流程会出出错
* 错误信息:org.flowable.common.engine.api.FlowableException: Unknown property used in expression: #{userId}
* 所以在此处要引入流程变量
* */
@Test
public void startProcessInstanceById(){
LinkedHashMap<String, Object> variables = new LinkedHashMap<>();
variables.put("userId","lb");
ProcessInstance task = processEngine.getRuntimeService().startProcessInstanceById("task:1:58004",variables);
System.out.println("流程实例ID=" + task.getId());
System.out.println("流程实例名字=" + task.getName());
}
修改任务的处理人
/**
* 认领任务(可以修改任务的执行人,并且修改一次数据库表(act_ru_task)中的版本号(REV_)加一)
* */
@Test
public void setAssigine(){
String id="60007";
String userId="lb1";
processEngine.getTaskService().setAssignee(id,userId);
}
在绘制流程图的时候可以设置 Candidate users
例: 设置为 (你,我 ,他)
注意:逗号要是英文的
部署资源
xml
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/testm1624169623439" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1624169623439" name="" targetNamespace="http://www.activiti.org/testm1624169623439" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="task" isClosed="false" isExecutable="true" name="personaltask" processType="None">
<startEvent id="startTask" name="StartEvent"/>
<userTask activiti:candidateUsers="我,你,他" activiti:exclusive="true" id="_3" name="审批"/>
<endEvent id="endTask" name="EndEvent"/>
<sequenceFlow id="_5" sourceRef="startTask" targetRef="_3"/>
<sequenceFlow id="_6" sourceRef="_3" targetRef="endTask"/>
process>
definitions>
/**
* 输入流的方式部署
* */
@Test
public void testDeploymentByInputStream(){
//InputStream resourceAsStream = GetProcessEngine.class.getClassLoader().getResourceAsStream("days.xml");
InputStream resource =getClass().getClassLoader().getResourceAsStream("personaltask3.xml");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("组任务测试")
.category("组任务")
.key("flowable")
.tenantId("租户的ID")
.addInputStream("groupTask.bpmn", resource)
.deploy();
System.out.println("输入流的方式部署获取部署对象的id="+deploy.getId());
}
执行任务
/**
* 执行任务
* */
@Test
public void startProcessInstanceById(){
ProcessInstance task = processEngine.getRuntimeService()
.startProcessInstanceById("task:2:63004");
System.out.println("流程实例ID=" + task.getId());
System.out.println("流程实例名字=" + task.getName());
}
查询组任务
/**
* 查询组任务
* sql:select distinct RES.* from
* ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null
* and
* exists
* (select LINK.ID_ from
* ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate'
* and
* LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? ) )
* order by RES.ID_ asc
*
* */
@Test
public void queryGroupTaskByPeopleName(){
String userName="我";
List<Task> tasks = processEngine.getTaskService()
.createTaskQuery()
.taskCandidateUser(userName)
.list();
for (Task task : tasks) {
System.out.println("任务名称:"+task.getName());
System.out.println("任务的ID:"+task.getId());
}
}
查询组任务的处理人
/**
* 查询组任务的处理人
*sql:select * from ACT_RU_IDENTITYLINK where TASK_ID_ = ?
* */
@Test
public void queryTaskDealPeople(){
String taskId="64006";
List<IdentityLink> taskPeople = processEngine.getTaskService().getIdentityLinksForTask(taskId);
for (IdentityLink taskPerson : taskPeople) {
System.out.println(taskPerson.getProcessDefinitionId());
System.out.println(taskPerson.getGroupId());
System.out.println(taskPerson.getUserId());
}
}
查询组任务的历史处理人
/**
* 查询组任务的历史处理人
*sql: select * from ACT_HI_IDENTITYLINK where TASK_ID_ = ?
* */
@Test
public void queryHistoryTaskDealPeople(){
String taskId="64006";
List<HistoricIdentityLink> historicIdentityLinks=
processEngine.getHistoryService().getHistoricIdentityLinksForTask(taskId);
for (HistoricIdentityLink historicIdentityLink : historicIdentityLinks) {
//System.out.println(historicIdentityLink.getProcessDefinitionId());
System.out.println(historicIdentityLink.getGroupId());
System.out.println(historicIdentityLink.getUserId ());
}
}
组任务认领
/**
* 认领任务(可以修改任务的执行人,并且修改一次数据库表(act_ru_task)中的版本号(REV_)加一)
* sql:update ACT_RU_ACTINST SET REV_ = ?, PROC_DEF_ID_ = ?, ASSIGNEE_ = ? where ID_ = ? and REV_ = ?
* sql: update ACT_HI_TASKINST SET REV_ = ?, ASSIGNEE_ = ?, CLAIM_TIME_ = ?, LAST_UPDATED_TIME_ = ? where ID_ = ? and REV_ = ?
* */
@Test
public void setAssignee(){
String id="64006";
String userId="你";
processEngine.getTaskService().claim(id,userId);
}
在绘制流程图的时候可以设置 Candidate users
例: #{userIds}
注意:添加流程变量的时候要用英文的逗号隔开字符串的处理人 variables.put(“userIds”,“小火,小炎,小焱,小燚”);
部署流程
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/testm1624169623439" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1624169623439" name="" targetNamespace="http://www.activiti.org/testm1624169623439" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="task" isClosed="false" isExecutable="true" name="personaltask" processType="None">
<startEvent id="startTask" name="StartEvent"/>
<userTask activiti:candidateUsers="#{userIds}" activiti:exclusive="true" id="_3" name="审批"/>
<endEvent id="endTask" name="EndEvent"/>
<sequenceFlow id="_5" sourceRef="startTask" targetRef="_3"/>
<sequenceFlow id="_6" sourceRef="_3" targetRef="endTask"/>
process>
definitions>
/**
* 输入流的方式部署
* */
@Test
public void testDeploymentByInputStream4(){
//InputStream resourceAsStream = GetProcessEngine.class.getClassLoader().getResourceAsStream("days.xml");
InputStream resource =getClass().getClassLoader().getResourceAsStream("personaltask4.xml");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("组任务测试4")
.category("组任务4")
.key("flowable4")
.tenantId("租户的ID4")
.addInputStream("groupTask4.bpmn", resource)
.deploy();
System.out.println("输入流的方式部署获取部署对象的id="+deploy.getId());
}
执行流程
/**
* 执行任务
*
*因为在部署流程的xml文件中设置了任务的处理人变量userId,如果此处不设置执行流程会出出错
*错误信息:org.flowable.common.engine.api.FlowableException: Unknown property used in expression: #{userIds}
*所以在此处要引入变量
* */
@Test
public void startProcessInstanceById4(){
LinkedHashMap<String, Object> variables = new LinkedHashMap<>();
variables.put("userIds","小火,小炎,小焱,小燚");
ProcessInstance task = processEngine.getRuntimeService()
.startProcessInstanceById("task:1:66004",variables);
System.out.println("流程实例ID=" + task.getId());
System.out.println("流程实例名字=" + task.getName());
}
查询任务组
/**
* 查询组任务
* */
@Test
public void queryGroupTaskByPeopleName4(){
String userName="小火";
List<Task> tasks = processEngine.getTaskService()
.createTaskQuery()
.taskCandidateUser(userName)
.list();
for (Task task : tasks) {
System.out.println("任务名称:"+task.getName());
System.out.println("任务的ID:"+task.getId());
}
}
查询组任务的处理人、查询组任务的历史处理人和组任务认领和上面的指定候选人类似。
监听器实现TaskListener接口重写notify方法。
接口:
public class GroupTaskListener implements TaskListener {
@Override
public void notify(DelegateTask delegateTask) {
System.out.println("*************************************调用监听接口!*********************************");
//添加候选人
delegateTask.addCandidateUser("小燕子");
delegateTask.addCandidateUser("小鱼儿");
delegateTask.addCandidateUser("小蜂子");
}
}
xml:
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/testm1624169623439"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
targetNamespace="http://www.activiti.org/testm1624169623439" xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="task" isClosed="false" isExecutable="true" name="personaltask" processType="None">
<startEvent id="startTask" name="StartEvent"/>
<userTask activiti:exclusive="true" id="_3" name="审批">
<extensionElements>
<flowable:taskListener event="create" class="com.lb.config.GroupTaskListener">
flowable:taskListener>
extensionElements>
userTask>
<endEvent id="endTask" name="EndEvent"/>
<sequenceFlow id="_5" sourceRef="startTask" targetRef="_3"/>
<sequenceFlow id="_6" sourceRef="_3" targetRef="endTask"/>
process>
definitions>
/**
* 输入流的方式部署
* */
@Test
public void testDeploymentByInputStream5(){
//InputStream resourceAsStream = GetProcessEngine.class.getClassLoader().getResourceAsStream("days.xml");
InputStream resource =getClass().getClassLoader().getResourceAsStream("personaltask5.xml");
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("组任务测试5")
.category("组任务5")
.key("flowable5")
.tenantId("租户的ID5")
.addInputStream("groupTask5.bpmn", resource)
.deploy();
System.out.println("输入流的方式部署获取部署对象的id="+deploy.getId());
}
@Test
public void delete5(){
processEngine.getRepositoryService().deleteDeployment("63001");
}
/**
* 执行任务
*
*因为在部署流程的xml文件中设置了任务的处理人变量userId,如果此处不设置执行流程会出出错
*错误信息:org.flowable.common.engine.api.FlowableException: Unknown property used in expression: #{userIds}
*所以在此处要引入变量
* */
@Test
public void startProcessInstanceById5(){
ProcessInstance task = processEngine.getRuntimeService()
.startProcessInstanceById("task:1:68004");
System.out.println("流程实例ID=" + task.getId());
System.out.println("流程实例名字=" + task.getName());
}
/**
* 查询组任务
* sql:select distinct RES.* from
* ACT_RU_TASK RES WHERE RES.ASSIGNEE_ is null
* and
* exists
* (select LINK.ID_ from
* ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate'
* and
* LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? ) )
* order by RES.ID_ asc
*
* */
@Test
public void queryGroupTaskByPeopleName5(){
String userName="小燕子";
List<Task> tasks = processEngine.getTaskService()
.createTaskQuery()
.taskCandidateUser(userName)
.list();
for (Task task : tasks) {
System.out.println("任务名称:"+task.getName());
System.out.println("任务的ID:"+task.getId());
}
}
/**
* 查询组任务的处理人
*sql:select * from ACT_RU_IDENTITYLINK where TASK_ID_ = ?
* */
@Test
public void queryTaskDealPeople5(){
String taskId="69006";
List<IdentityLink> taskPeople = processEngine.getTaskService().getIdentityLinksForTask(taskId);
for (IdentityLink taskPerson : taskPeople) {
System.out.println(taskPerson.getProcessDefinitionId());
System.out.println(taskPerson.getGroupId());
System.out.println(taskPerson.getUserId());
}
}
/**
* 查询组任务的历史处理人
*sql: select * from ACT_HI_IDENTITYLINK where TASK_ID_ = ?
* */
@Test
public void queryHistoryTaskDealPeople5(){
String taskId="64006";
List<HistoricIdentityLink> historicIdentityLinks=
processEngine.getHistoryService().getHistoricIdentityLinksForTask(taskId);
for (HistoricIdentityLink historicIdentityLink : historicIdentityLinks) {
//System.out.println(historicIdentityLink.getProcessDefinitionId());
System.out.println(historicIdentityLink.getGroupId());
System.out.println(historicIdentityLink.getUserId());
}
}
/**
* 认领任务(可以修改任务的执行人,并且修改一次数据库表(act_ru_task)中的版本号(REV_)加一)
* sql:update ACT_RU_ACTINST SET REV_ = ?, PROC_DEF_ID_ = ?, ASSIGNEE_ = ? where ID_ = ? and REV_ = ?
* sql: update ACT_HI_TASKINST SET REV_ = ?, ASSIGNEE_ = ?, CLAIM_TIME_ = ?, LAST_UPDATED_TIME_ = ? where ID_ = ? and REV_ = ?
* */
@Test
public void setAssignee5(){
String id="69006";
String userId="小蜂子";
processEngine.getTaskService().claim(id,userId);
}
@Test
public void completeTask(){
processEngine.getTaskService().complete("69006");
System.out.println("任务完成!");
}
在认领任务后可以回退
//某人认领任务后想回退到上一个步骤,就可以将认领人设置为空
/**
* 个人任务分配给组任务
* */
@Test
public void setAssigneeBack(){
String taskId="69006";
processEngine.getTaskService().setAssignee(taskId,null);
}
在执行过程中突然想添加一个候选人
/**
* 添加任务的候选人
* */
@Test
public void setAssigneeByUserId(){
String taskId="69006";
String userId="小虫子";
processEngine.getTaskService().addCandidateUser(taskId,userId);
}
在执行过程中删除候选人
/**
* 删除任务的候选人
* */
@Test
public void deleteAssigneeByUserId(){
String taskId="69006";
String userId="小虫子";
processEngine.getTaskService().deleteCandidateUser(taskId,userId);
}
组任务分配给个人:processEngine.getTaskService().claim(taskId,userId);
个人任务分配给组任务:processEngine.getTaskService().setAssignee(taskId,null);
组任务和个人任务存放办理人对应的表:
1.act_ru_identitylink 存放任务的办理人,包括各人任务和组任务,表示正在执行的任务。
2.act_hi_identitylink 存放任务的办理人,包括各人任务和组任务,表示历史任务。
3.如果是个人任务表(act_ru_identitylink,act_hi_identitylink)对应的TYPE_字段时particpant(参与者)
4.如果是个人任务表(act_ru_identitylink,act_hi_identitylink)对应的TYPE_字段时 candidate(候选人) 和 particpant(参与者)
网关的分类:排他网关、并行网关、兼容网关
网关主要是满足一些特殊的业务,比如排他网关,可以让流程实例出现分支,当有多个条件满足的时候走一个执行分支。
并行网关有分流和聚合的功能。
兼容网关可以当做并行网使用,也可以当做排他网关使用。
连线task1的条件: ${0<=money&&money<1000}
连线task2的条件: ${1000<=money&&money<10000}
连线task3的条件: ${10000<=money}
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1624258853673" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="gateway_test" isClosed="false" isExecutable="true" name="排他网关测试" processType="None">
<startEvent id="_2" name="StartEvent"/>
<userTask activiti:exclusive="true" id="one" name="任务一"/>
<exclusiveGateway gatewayDirection="Unspecified" id="_4" name="ExclusiveGateway"/>
<userTask activiti:exclusive="true" id="two" name="任务二"/>
<userTask activiti:exclusive="true" id="three" name="任务三"/>
<sequenceFlow id="_7" sourceRef="_2" targetRef="_4"/>
<sequenceFlow id="_8" name="task1" sourceRef="_4" targetRef="one">
<conditionExpression xsi:type="tFormalExpression">
conditionExpression>
sequenceFlow>
<sequenceFlow id="_9" name="task2" sourceRef="_4" targetRef="two">
<conditionExpression xsi:type="tFormalExpression">
conditionExpression>
sequenceFlow>
<sequenceFlow id="_10" name="task3" sourceRef="_4" targetRef="three">
<conditionExpression xsi:type="tFormalExpression">
conditionExpression>
sequenceFlow>
<endEvent id="_11" name="EndEvent"/>
<endEvent id="_12" name="EndEvent"/>
<endEvent id="_13" name="EndEvent"/>
<sequenceFlow id="_14" sourceRef="one" targetRef="_11"/>
<sequenceFlow id="_15" sourceRef="two" targetRef="_12"/>
<sequenceFlow id="_16" sourceRef="three" targetRef="_13"/>
process>
definitions>
private ProcessEngine processEngine= ProcessEngines.getDefaultProcessEngine();
/**
* 部署流程定义
* */
@Test
public void deployProcessInstance(){
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("排他网关测试")
.category("网关")
.tenantId("luobo")
.addClasspathResource("gateway.bpmn")
.deploy();
System.out.println("部署ID="+deploy.getId());
System.out.println("部署名称="+deploy.getName());
}
根据传入变量的值,流程会走到不同的流程分支。
/**
* 执行流程
* */
@Test
public void startProcessInstance(){
LinkedHashMap<String, Object> variables = new LinkedHashMap<>();
variables.put("money","10000000000");
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceById("gateway_test:1:79004",variables);
}
/**
* 完成任务
* */
@Test
public void completeTask(){
processEngine.getTaskService().complete("75009");
}
说明:该流程中当任务一完成后,不会继续往下执行,只有等待任务二完成后才会一起往下个节点执行,任务一二完成后就进行汇总,到达任务一二汇总节点。
/**
* 部署流程定义
* */
@Test
public void deployProcessInstance(){
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("并行网关测试")
.category("网关")
.tenantId("yaner")
.addClasspathResource("parallelGateway.bpmn")
.deploy();
System.out.println("部署ID="+deploy.getId());
System.out.println("部署名称="+deploy.getName());
}
/**
* 执行流程
* */
@Test
public void startProcessInstance(){
//运行成后会在act_ru_task表中创建两个实例
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceById("parallelGateway:1:83004");
}
/**
* 完成任务
* */
@Test
public void completeTask(){
//根据在表act_ru_task中的两个实例ID来完成任务(要两个同时完成才会走向下一个节点,一个完成另一个处于等待状态)
processEngine.getTaskService().complete("84012");
processEngine.getTaskService().complete("84012");
}
并行网关使用:
1.兼容网关既可以当做排他网关使用,也可以当做并行网关使用。
2.兼容网关连线上面的条件是可以生效的。
3.兼容网关通常情况下是成对出现的(因为他有可能转化为并行网关)。
4.如果有多个顺序流满足条件,则会当做并行网关使用。
5.如果只有单个顺序流满足条件,则会当做排他网关使用。
流程变量在整个工作流中扮演很重要的作用。
流程变量的作用于范围只对应一个流程实例,各个流程实例的流程变量之间互不影响。
流程实例结束后流程变量保存到历史数据表(act_hi_varinst)中。
流程变量设置
1.启动流程实例的时候。
2.完成任务的时候
3.手工去设置变量
RuntimeService对象可以设置流程变量和获取流程变量、TaskService可以设置和获取流程变量。
启动流程实例的时候可以设置流程变量。
/**
* 部署流程定义
* */
@Test
public void deployProcessInstance(){
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("流程变量")
.category("变量")
.tenantId("熊熊")
.addClasspathResource("variables.bpmn")
.deploy();
System.out.println("部署ID="+deploy.getId());
System.out.println("部署名称="+deploy.getName());
}
/**
* 执行流程
* */
@Test
public void startProcessInstance(){
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceById("variable:1:87004");
}
/**
* 查询任务
* setVariableLocal(task.getId(),"请加天数",8);
* 这种方式设置变量变act_ru_variable/act_hi_variable表中的TASK_ID_有值
* */
@Test
public void queryTaskAndSetVariables(){
Task task = processEngine.getTaskService()
.createTaskQuery()
.processDefinitionId("variable:1:87004")
.singleResult();
processEngine.getTaskService().setVariable(task.getId(),"请假人员","略略");
processEngine.getTaskService().setVariableLocal(task.getId(),"请假天数",8);
LinkedHashMap<String, Object> variables = new LinkedHashMap<>();
variables.put("请假日期",new Date());
variables.put("请假原因","人数短暂及时行乐!");
processEngine.getTaskService().setVariables(task.getId(),variables);
System.out.println("获取执行任务的id="+task.getId());
}
/**
* 完成任务设置变量(完成人)
* */
@Test
public void completeTask(){
Task task = processEngine.getTaskService()
.createTaskQuery()
.processDefinitionId("variable:1:87004")
.singleResult();
LinkedHashMap<String, Object> variables = new LinkedHashMap<>();
variables.put("完成人","luobo");
processEngine.getTaskService().complete(task.getId(),variables);
System.out.println("获取执行任务的id="+task.getId());
}
- 流程变量的作用于就是流程实例,所以无论在那个阶段设置就行了。
- 基本类型设置流程变量,在taskService中使用ID,定义流程变量的名称,设置流程变量的值。
- 如果是对象,则需要该对象实现序列化接口才能保存到数据库中。
- 设置流程变量的时候会向act_ru_variable/act_hi_variable表中添加数据。
/**
* 获取变量
* */
@Test
public void getVariables(){
Task task = processEngine.getTaskService()
.createTaskQuery()
.processDefinitionId("variable:1:87004")
.singleResult();
//获取所有的变量
Map<String, Object> variables = processEngine.getTaskService().getVariables(task.getId());
for (Object variable: variables.values()) {
System.out.println(variable);
}
//获取某个变量
Object name = processEngine.getTaskService().getVariable(task.getId(), "完成人");
System.out.println(name);
}
@Data
public class User implements Serializable {
private static final Integer serialVersion=12345;
private String name;
private Integer age;
}
/**
* 设置一个对象作为变量
* */
@Test
public void setBeanVariables(){
Task task = processEngine.getTaskService()
.createTaskQuery()
.processDefinitionId("variable:1:87004")
.singleResult();
LinkedHashMap<String, Object> variables = new LinkedHashMap<>();
User user = new User();
user.setAge(23);
user.setName("陌陌");
variables.put("user",user);
processEngine.getRuntimeService().setVariables(task.getExecutionId(),variables);
}
/**
* 获取变量(对象)
* */
@Test
public void getBeanVariables(){
Task task = processEngine.getTaskService()
.createTaskQuery()
.processDefinitionId("variable:1:87004")
.singleResult();
Object user = processEngine.getRuntimeService().getVariable(task.getExecutionId(), "user");
if (user instanceof User){
System.out.println(user);
}
}
/**
* 获取历史变量(act_hi_varinst)
*/
@Test
public void getHistoryVariables() {
List<HistoricVariableInstance> historicVariables = processEngine.getHistoryService()
.createHistoricVariableInstanceQuery()
.list();
if (!CollectionUtils.isEmpty(historicVariables)) {
for (HistoricVariableInstance historicVariable : historicVariables) {
System.out.println("历史变量:" + historicVariable);
}
}
}
setVariable(全局的):设置流程变量的时候,流程变量名称相同的时候,后一次的值替换前一次的值
setVariableLocal(局部的):设置流程变量的时候,针对当前活动的节点设置流程变量,如果一个流程中存在2个活动节点,对每个活动节点都设置流程变量,即使流程变量的名称相同,后一次的版本的值也不会替换前一次版本的值,它会使用不同的任务ID作为标识,存放2个流程变量值。
使用setVariableLocal说明了流生变量绑定了当前的任务,当流程继续执行时,下一个节点的任务是获取不到这个流程变量的(因为正在执行的流程变量中没有这个数据),所有查询正在执行的人任务不能查询到我们需要的数据,此时需要查询历史流程变量表。
临时变量是不会保存到数据库中的,可以在启动流程的时候设置流程变量,也可以在完成任务的时候设置临时变量。
/**
* 设置临时变量
*/
@Test
public void setTransientVariables() {
Map<String, Object> variables = new HashMap<>();
variables.put("aaa", "测试临时变量");
variables.put("bbb", "测试临时变量是否会存到数据库");
ProcessInstanceBuilder builder =
processEngine.getRuntimeService().createProcessInstanceBuilder();
ProcessInstance start = builder.processDefinitionId("variable:1:87004")
.name("哈哈哈")
.transientVariables(variables)
.start();
System.out.println(start);
}
/**
* 完成任务时设置流程临时变量
*/
@Test
public void completeTask() {
Map<String, Object> variables = new HashMap<>();
variables.put("ccc", "完成任务时测试临时变量");
variables.put("ddd", "完成任务时测试临时变量是否会存到数据库");
processEngine.getTaskService().complete("92006", null, variables);
}
flowable的查询历史信息类,在一个流程执行完后,该对象为我们提供查询历史信息。
实现类HistoryServiceImpl
/**
* 部署流程定义
*/
@Test
public void deployProcessInstance() {
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("历史信息测试")
.category("history")
.tenantId("luobo")
.addClasspathResource("history.bpmn")
.deploy();
System.out.println("部署ID=" + deploy.getId());
System.out.println("部署名称=" + deploy.getName());
}
/**
* 执行流程
*/
@Test
public void startProcessInstance() {
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceById("history:1:93004");
}
/**
* 查询历史流程实例表(昨天启动成功)
* select distinct RES.* , DEF.KEY_ as PROC_DEF_KEY_, DEF.NAME_ as PROC_DEF_NAME_, DEF.VERSION_ as
* PROC_DEF_VERSION_, DEF.DEPLOYMENT_ID_ as DEPLOYMENT_ID_
* from ACT_HI_PROCINST RES left outer join ACT_RE_PROCDEF DEFon RES.PROC_DEF_ID_ = DEF.ID_
* WHERE RES.END_TIME_ is not NULL order by RES.ID_ asc
*/
@Test
public void queryHistoryProcessInstance() {
List<HistoricProcessInstance> historicProcessInstances = processEngine.getHistoryService()
.createHistoricProcessInstanceQuery()
.finished()
.list();
for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
System.out.println("结束时间:" + historicProcessInstance.getEndTime());
}
}
/**
* 查询历史活动(也就是所有节点的历史信息)
* select RES.* from ACT_HI_ACTINST RES order by RES.ID_ asc
*/
@Test
public void queryHistoryActivity() {
List<HistoricActivityInstance> historicActivityInstances = processEngine
.getHistoryService()
.createHistoricActivityInstanceQuery()
.list();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
System.out.println("************************");
System.out.println(historicActivityInstance.getActivityId());
System.out.println(historicActivityInstance.getActivityName());
}
}
/**
* 查询历史实例信息
* select distinct RES.* from ACT_HI_TASKINST RES order by RES.ID_ asc
*/
@Test
public void queryHistoryInstance() {
List<HistoricTaskInstance> historicActivityInstances = processEngine
.getHistoryService()
.createHistoricTaskInstanceQuery()
.list();
for (HistoricTaskInstance historicActivityInstance : historicActivityInstances) {
System.out.println("************************");
System.out.println("流程定义的ID"+historicActivityInstance.getProcessDefinitionId());
System.out.println("结束时间"+historicActivityInstance.getEndTime());
}
}
/**
* 查询历史流程变量
* select RES.* from ACT_HI_VARINST RES order by RES.ID_ asc
* */
@Test
public void queryHistoryVariables() {
List<HistoricVariableInstance> historicVariableInstances = processEngine
.getHistoryService()
.createHistoricVariableInstanceQuery()
.list();
for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {
System.out.println("************************");
System.out.println("流程定义的实例ID"+historicVariableInstance.getProcessInstanceId());
System.out.println("变量的名称"+historicVariableInstance.getVariableName());
System.out.println("变量的值"+historicVariableInstance.getValue());
}
}
/**
* 历史权限表查询
* select * from ACT_HI_TASKINST where ID_ = ?
* select * from ACT_HI_IDENTITYLINK where TASK_ID_ = ?
* */
@Test
public void queryHistoricIdentityLinksForTask() {
List<HistoricIdentityLink> historicIdentityLinks = processEngine
.getHistoryService()
.getHistoricIdentityLinksForTask("94006");
for (HistoricIdentityLink historicIdentityLink : historicIdentityLinks) {
System.out.println("************************");
System.out.println("流程定义的ID"+historicIdentityLink.getScopeDefinitionId());
}
}
/**
* 自定义SQL查询历史信息
* */
@Test
public void sql() {
List<HistoricActivityInstance> list = processEngine
.getHistoryService()
.createNativeHistoricActivityInstanceQuery()
.sql("select res.*from act_hi_actinst res order by res.ID_ asc")
.list();
for (HistoricActivityInstance historicActivityInstance : list) {
System.out.println(historicActivityInstance.getActivityName());
System.out.println(historicActivityInstance.getProcessDefinitionId());
}
}
1.none: 所有的历史归档数据不会插入到数据库中,该级别对于流程实例运转的性能最高,因为不涉及到历史表相关的操作。
2.activity: 归档所有的流程实例和活动实例,不归档流程细节(如历史任务节点)。
3.audit: 缺省级别,归档所有的流程实例、活动实例以及提交的表单属性,所有与用户进行交互的数据都可以进行跟踪和统计。
4.full 历史数据挂挡的最高级别,该级别记录所有的历史数据。
默认配置为:audit
public void initHistoryLevel() {
if (historyLevel == null) {
historyLevel = HistoryLevel.getHistoryLevelForKey(getHistory());
}
}
//getHistory():
public String getHistory() {
return history;
}
protected String history = HistoryLevel.AUDIT.getKey();
在配置文件中(flowable.cfg.xml)可以自定义
<property name="historyLevel" value="NONE">property>
挂起:suspendProcessDefinitionBykey()
流程定义挂起,指定过期时间
激活:activateProcessDefinitionById()
判断流程定义是否挂起:isProcessDefinitionSuspend
如果设置了指定时间内启动流程定义,那么部署流程后,默认是挂起状态,act_re_procdef表的SUSPENSION_START_为2表示挂起
/**
* 部署流程定义
*/
@Test
public void deployProcessInstance() {
Date date = new Date(new Date().getTime()+3*1000);
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("定时器测试")
.category("activateAndSuspend")
.tenantId("xiongxiong")
.addClasspathResource("history.bpmn")
.activateProcessDefinitionsOn(date)
.deploy();
System.out.println("部署ID=" + deploy.getId());
System.out.println("部署名称=" + deploy.getName());
}
如果想让定时器无法执行,我们可以在flowable.cfg.xml文件设置开关属性。
<property name="asyncExecutorActivate" value="true">property>
选择定时挂起流程定义的时候,也可以选择是否挂起流程实例以及制定挂起的时间。
act_ru_timer_job表中会插入一条数据。
定时器运行之后,流程定义会被挂起,流程实例根据是否挂起参数决定是否挂起实例。
流程定义被挂起后就不能够启动流程实例了,如果在流程实例乖巧之前已经有流程实例在运行,那么当流程定义被挂起后这些流程实例下的所有活动都会被挂起,暂停运行。
/**
* 获取版本(是5版本还是6版本)
*/
@Test
public void testVersion() {
Boolean processDefinition = processEngine.getRepositoryService().isFlowable5ProcessDefinition(
"history:1:93004");//false表示6版本
System.out.println(processDefinition);
}
/**
* 判断流程定义是否被挂起
*/
@Test
public void isSuspend() {
Boolean processDefinition = processEngine.getRepositoryService().isProcessDefinitionSuspended(
"history:2:96004");//true代表被挂起,false代表没有被挂起
System.out.println("是否被挂起"+processDefinition);
}
如果将实例挂起或者激活,那么首先会向白鸥act_ru_timer_job表中插入数据。
定时器在执行的时候,会将act_ru_timer_job中的数据删除,然后添加到定时作业任务表中(act_ru_job)
/**
* 定时器在执行的时候,会将act_ru_timer_job中的数据删除,然后添加到定时作业任务表中(act_ru_job)
*/
@Test
public void managerService() {
String jobId="5512";
processEngine.getManagementService().moveTimerToExecutableJob(jobId);
}
/**
* 设置重置次数
* */
@Test
public void set() {
String jobId="1564";
processEngine.getManagementService().setJobRetries(jobId,10);
}
/**
* 执行作业表移到死信作业表
* 死信作业表(act_ru_deadletter_job)数据不会被执行,因为相关的任务在这个表中说明已经不需要在执行了。
* */
@Test
public void moveJobToDeadLetterJob() {
String jobId="5512";
processEngine.getManagementService().moveJobToDeadLetterJob(jobId);
}
/**
* 将act_ru_deadletter_job中的数据移动到act_ru_job中,变成可执行的任务。
* */
@Test
public void moveDeadLetterJobToExecutableJob() {
String jobId="5512";
processEngine.getManagementService().moveDeadLetterJobToExecutableJob(jobId,10);
}
表单使用的三种方式
- 动态表单:只能定义表单中字段的一些配置信息,如字段的可读、可写、必须等信息,不能定义完成表单的页面。
- 外置表单:只能定义表单的key,关于key的内容自己去维护。
- 内置表单:内置表单定义以及渲染引擎
表单实现类:FormService–>实现类FormServiceImpl
表单定义支持的节点:开始节点、任务节点、其他节点都不支持。
xml定义:
<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:flowable="http://flowable.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.flowable.org/processdef">
<process id="formService" name="form" isExecutable="true">
<startEvent id="startEvent1">
<extensionElements>
<flowable:formProperty id="start_date" name="开始时间" type="date" expression="yyyy-MM-dd HH:mm"
required="true">flowable:formProperty>
<flowable:formProperty id="end_date" name="结束时间" type="date" expression="yyyy-MM-dd HH:mm"
datePattern="MM-dd-yyyy hh:mm" required="true">flowable:formProperty>
<flowable:formProperty id="reason" name="请假的原因" type="string" required="true">flowable:formProperty>
<flowable:formProperty id="days" name="请假天数" type="long" required="true">flowable:formProperty>
extensionElements>
startEvent>
<userTask id="sid-9D9E120A-CFC5-4BB4-89A6-88D246598273" name="请假申请哦">
<extensionElements>
<flowable:formProperty id="start_date" name="开始时间" type="date" expression="yyyy-MM-dd HH:mm"
readable="true">flowable:formProperty>
<flowable:formProperty id="end_date" name="结束时间" type="date" expression="yyyy-MM-dd HH:mm"
datePattern="MM-dd-yyyy hh:mm" readable="true">flowable:formProperty>
<flowable:formProperty id="reason" name="请假的原因" type="string" required="true">flowable:formProperty>
<flowable:formProperty id="days" name="请假天数" type="long" required="true">flowable:formProperty>
extensionElements>
userTask>
<sequenceFlow id="sid-2B6B3030-A341-4210-9E2F-1C4F026CDDE4" sourceRef="startEvent1"
targetRef="sid-9D9E120A-CFC5-4BB4-89A6-88D246598273">sequenceFlow>
<userTask id="sid-E3D9B3D4-45C3-4149-A830-72AC83B25539" name="老大审批">userTask>
<sequenceFlow id="sid-51A2D0E0-54E2-4FCD-8A55-A147BEF16819" sourceRef="sid-9D9E120A-CFC5-4BB4-89A6-88D246598273"
targetRef="sid-E3D9B3D4-45C3-4149-A830-72AC83B25539">sequenceFlow>
<endEvent id="sid-5C58CE2B-1428-4901-B028-8617A96FAFCF">endEvent>
<sequenceFlow id="sid-CC68B1E5-9466-4988-941B-1CE659FB72C5" sourceRef="sid-E3D9B3D4-45C3-4149-A830-72AC83B25539"
targetRef="sid-5C58CE2B-1428-4901-B028-8617A96FAFCF">sequenceFlow>
process>
definitions>
部署
/**
* 部署流程定义
*/
@Test
public void deployProcessInstance() {
Deployment deploy = processEngine.getRepositoryService()
.createDeployment()
.name("表单测试")
.category("form")
.tenantId("formId")
.addClasspathResource("form.bpmn")
.deploy();
System.out.println("部署ID=" + deploy.getId());
System.out.println("部署名称=" + deploy.getName());
}
获取表单属性
/**
* 获取表单的属性
* */
@Test
public void getProperties(){
StartFormData startFormData = processEngine.getFormService().getStartFormData("formService:1:99004");
System.out.println(startFormData);
System.out.println("获取流程定义"+startFormData.getProcessDefinition());
List<FormProperty> formProperties = startFormData.getFormProperties();
for (FormProperty formProperty : formProperties) {
System.out.println("获取id:"+formProperty.getId());
System.out.println("获取名字:"+formProperty.getName());
System.out.println("获取类型:"+formProperty.getType());
System.out.println("获取值:"+formProperty.getValue());
}
}