Flowable流程引擎


一、流程引擎API和服务

引擎 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

二、.RepositoryService服务

/*** ***************************************************************************************************** 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();}

1.资源部署的多种方式

 //用来定义流程部署相关的参数DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();deploymentBuilder.category("学习flowable测试分类").name("自定义资源部署的名字").tenantId("设置租户ID").addClasspathResource("bpmn文件路径(在根路径下直接写文件全名)").deploy();
*************************************************************************************************************
/*** 文本方式部署* 资源的名称必须是"bpmn20.xml", "bpmn"这两个结尾的才能部* 署到流程定义表中(act_re_procdef),不然只有部署表中才有,* 定义表中没有相应的数据* */@Testpublic 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());}
*************************************************************************************************************/*** 输入流的方式部署* */@Testpublic 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());}
*************************************************************************************************************/*** 压缩流的方式部署资源* */@Testpublic 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());}
*************************************************************************************************************/*** 字节的方式部署资源* */@Testpublic 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结束的

2.部署完成后对应表的变化

  • act_re_deployment(部署对象表)
    存放流程定义的信息(如名字、类别、租户ID等信息),每部署一次多一条记录。

  • act_re_procdef(流程定义表)
    存放流程定义的属性信息,没部署一个新的流程就会新增一条记录,当流程定义的key相同的话,没部署一次版本号加一(对应数据库字段 “REV_ ” 默认版本号从1开始)。

  • act_ge_bytearray(资源文件表)

    以二进制的形式存储流程定义中部署的bpmn文件和png图片(注:如果只是指定了bpmn文件没有png图片,flowable会在部署解析bpmn文件时生成流程图保存在该表中)。

3.查询流程定义

 /*** 查询流程定义信息* */@Testpublic 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());}}
  • 流程定义和流程部署相关的Service都是RepositoryService
  • 创建流程定义查询对象,可以再ProcessDefinitionQuery上设置查询相关的参数
  • key值用来区分不同的流程定义的
  • 流程的定义id的生成规则:(流程定义的key:流程定义的版本:随机生成的数字)/({processDefinitionKey}:{processDefinitionVersion}:{generated-id}) 如:days:1:27504
  • 部署表(act_re_deployment)的ID按照一定规律变化(参考act_ge_property中的next.dbid的value值)

4.删除流程定义

  /*** 流程定义的删除* */@Testpublic void deleteDeployment(){//根据部署ID删除processEngine.getRepositoryService().deleteDeployment("40001");//根据部署ID级联删除(会删除当前流程定义下的所有流程实例)processEngine.getRepositoryService().deleteDeployment("30001",true);}

5.获取流程定义的文档资源

 /*** 获取流程定义的资源文件(图片)* */@Testpublic 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);}}
  • deploymentId为act_re_deployment表的ID。
  • resourceName(imageName)为act_ge_bytearray表中的NAME_的值。
  • getRepositoryService().getDeploymentResourceNames(deploymentId)方法获取指定部署下的文件名称。
  • getRepositoryService().getResourceAsStream(deploymentId, imageName)根据部署的ID和资源文件的名称可以获取指定部署下的文件的输入流。

6.自定义SQL进行本地查询

 /*** 自定义SQL本地查询* */@Testpublic 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());}}

7.自定义主键的生成策略

1.flowable.cfg.xml加如这条属性
<!--设置ID生成,每次增加1000:部署表(act_re_deployment)的ID按照一定规律变化(参考act_ge_property中的next.dbid的value值(默认每次增加2500))--><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 {@Overridepublic 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>

三、RuntimeService服务

启动流程实例的方式

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);

一个流程中,执行对象可以有多个,但是执行流程实例至于一个

启动一个实例–>执行实例–>更新实例节点–>实例结束

1.流程实例执行

操作的数据库表为act_ru_execution,当处于用户节点时,会在表act_ru_task中插入一条数据。
执行实例走到任务节点的时候,会暂时处于等待状态,只有当该节点的任务完成后,才能继续流程的运转。

   /*** 执行流程实例* */@Testpublic 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());}

2.查询的任务

一个task节点(act_ru_task)和Execution(act_ru_execution)是一对一的关系

    /***查询任务* */@Testpublic 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());}}

3.完成任务

任务完成后,该节点的任务会被删除,插入下个节点的任务信息,同时将删除的任务节点信息插入到历史记录表中去。(在同一个事物中操作)。

当所有的任务节点都完成表act_ru_task中就没有数据了。

 /*** 完成任务** */@Testpublic 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);}}}

4.查询流程的状态(判断结束还是在执行)

1.在流程的执行过程中,创建的历程实例ID在整个过程中都不会改变,当流程结束后,流程实例将会在正正在执行的执行对象表中(act_ru_execution)被删除。
2.由于一个流程实例ID对应一个实例,所以使用singleResult()执行查询返回唯一的结果,如果返回的结果数量大于1,会抛出异常。
3.判断指定的ID实例是否存在,如果为空就会代表执行流程结束,在执行表中被删除,存到历史数据表中去。

    /*** 查询流程执行的状态(判断流程执行是否结束)* */@Testpublic void queryProcessInstanceStatus(){ProcessInstance processInstance =processEngine.getRuntimeService().createProcessInstanceQuery().processInstanceId("luobo1df98a85-df89-4c0f-bdfd-276448b4732e").singleResult();if (processInstance==null){System.out.println("流程实例执行完毕!");}else {System.out.println("流程实例还在执行中!");}}

5.查询执行实例(act_ru_execution)

 /*** 查询执行实例* */@Testpublic 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("执行实例不存在!");}}

6.历史流程实例数据查询(act_hi_procinst)

历史节点活动数据存储再act_hi_actinst表中

 /*** 历史流程查询*/@Testpublic 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()));}

7.历史任务查询(act_hi_taskinst)

历史任务表的数据和运行任务表的数据一起插入数据库的,并且在一个事物里面执行的。
如果表act_hi_taskinst中的END_TIME_(结束时间)字段有值,那说明任务已经完成了,没有值说明任务还在执行。

    /*** 查询所有的历史任务*/@Testpublic void queryHistoryTaskAll() {List<HistoricTaskInstance> taskInstanceList = processEngine.getHistoryService().createHistoricTaskInstanceQuery().list();for (HistoricTaskInstance historicTaskInstance : taskInstanceList) {System.out.println("历史任务的ID="+historicTaskInstance.getId());}}

说明:

  • act_ru_execution 正在执行的信息
  • act_ru_task 当前节点正在执行的 任务信息
  • act_hi_procinst 已经执行完的历史流程实例信息
  • act_hi_actinst 存放历史所有完成的活动
  • act_hi_taskinst 已经执行完的历史任务信息

8.流程任务的发起者

initiator跟任务的发起者配合使用才会有效果,否则没有意义。

     * 获取表单的属性* */@Testpublic 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());}
}/*** 流程任务的发起人*/@Testpublic 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());}

9.流程实例删除

删除后历史表中的数据还是存在的

    /*** 流程实例的删除* */@Testpublic void deleteProcessInstance() {String processInstanceId="luoboab5875a3-576a-4503-8b9e-c983503d84a0";String deleteReason="测试通过RuntimeService删除流程实例";processEngine.getRuntimeService().deleteProcessInstance(processInstanceId,deleteReason);}

级联删除:删除后历史表中的数据不存在了


10.获取流程运行的活动节点

    /*** 获取流程运行的活动节点* */@Testpublic 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

11.流程定义的挂起和激活

流程实例挂起后流程实例下面的所有执行实例都会被挂起

    /*** 查看流程定义是否挂起* */@Testpublic 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没有被挂起* 流程被挂起后是不能够启动流程实例的(启动会报错)* */@Testpublic void processDefinitionSuspended() {String processDefinitionId="days:1:27504";//根据指定日期挂起//processEngine.getRepositoryService().suspendProcessDefinitionById(String processDefinitionId, boolean suspendProcessInstances, Date suspensionDate);processEngine.getRepositoryService().suspendProcessDefinitionById(processDefinitionId);}
    /*** 解挂,激活流程定义* */@Testpublic void releaseProcessDefinitionSuspended() {String processDefinitionId="days:1:27504";processEngine.getRepositoryService().activateProcessDefinitionById(processDefinitionId);}

四、节点

1.开始节点

开始节点时每个流程实例的起点,开始节点只能有一个,不能有多个,有多个开始节点部署会报错。

2.结束节点

在一个部署文件中结束节点可以存在多个,当流程实例运行到结束节点的时候,则表示当前的执行实例结束了,执行实例结束那么流程实力也就结束了。

flowable中有事件、类型、网关节点。
事件节点:开始节点事件、结束节点事件。
活动类型:人工任务节点、服务任务节点、用户任务节点等。
活动节点大致分为:
等待节点—实例走到这个节点的时候,会停留在这里,需要我们手动处理去完成当前的节点,这样流程实例才会往下走。
非等待节点—实例走到该节点,会直接完成这个节点的任务,然后实例继续往下走。

3.接收任务节点

接收任务会等待消息的到达,当流程到达结束任务时,流程的状态会保存到数据库中。

任务创建后流程进入等待状态,直到引擎接收了一个特定的消息,这会触发流程穿过接收任务继续执行。

 /*** 部署资源* */@Testpublic 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());}
/*** 执行流程实例*/@Testpublic 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* */@Testpublic 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* */@Testpublic void trigger(){//活动ID,表act_ru_execution中的ACT_ID_字段String executionId="52002";processEngine.getRuntimeService().trigger(executionId);}

五、TaskService服务(用户任务)

主要分为个人任务和任务组
个人任务:指定某个确定的办理者,这个任务就是私有任务,即个人任务。
任务组:没法将这个任务指定个某个具体的人,但是可以分配到多个人活着一到多个小组中去,让小组中的成员来办理。

个人任务

1.测试个人任务:在画流程图的时候指定Assignee为具体的人

部署资源

 /*** 部署个人任务* */@Testpublic 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());}

执行任务

    /*** 执行任务* */@Testpublic 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* */@Testpublic 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());}}
2.测试动态分配任务处理人:在画流程图的时候指定Assignee为变量(#{userId})

部署资源

 /*** 输入流的方式部署* */@Testpublic 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}* 所以在此处要引入流程变量* */@Testpublic 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);
}

3.组任务

1.指定候选人

在绘制流程图的时候可以设置 Candidate users
例: 设置为 (你,我 ,他)
注意:逗号要是英文的

部署资源
xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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>
 /*** 输入流的方式部署* */@Testpublic 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());}

执行任务

 /*** 执行任务* */@Testpublic 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** */@Testpublic 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_ = ?* */@Testpublic 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_ = ?* */@Testpublic void setAssignee(){String id="64006";String userId="你";processEngine.getTaskService().claim(id,userId);}
2.通过流程变量添加组成员

在绘制流程图的时候可以设置 Candidate users
例: #{userIds}
注意:添加流程变量的时候要用英文的逗号隔开字符串的处理人 variables.put(“userIds”,“小火,小炎,小焱,小燚”);

部署流程

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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>
 /*** 输入流的方式部署* */@Testpublic 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}*所以在此处要引入变量* */@Testpublic 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());}

查询任务组

    /*** 查询组任务* */@Testpublic 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());}}

查询组任务的处理人、查询组任务的历史处理人和组任务认领和上面的指定候选人类似。

3.通过监听器实现添加组成员

监听器实现TaskListener接口重写notify方法。

接口:

public class GroupTaskListener  implements TaskListener {@Overridepublic void notify(DelegateTask delegateTask) {System.out.println("*************************************调用监听接口!*********************************");//添加候选人delegateTask.addCandidateUser("小燕子");delegateTask.addCandidateUser("小鱼儿");delegateTask.addCandidateUser("小蜂子");}
}

xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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>
/*** 输入流的方式部署* */@Testpublic 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());}@Testpublic void delete5(){processEngine.getRepositoryService().deleteDeployment("63001");}/*** 执行任务**因为在部署流程的xml文件中设置了任务的处理人变量userId,如果此处不设置执行流程会出出错*错误信息:org.flowable.common.engine.api.FlowableException: Unknown property used in expression: #{userIds}*所以在此处要引入变量* */@Testpublic 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** */@Testpublic 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_ = ?* */@Testpublic 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_ = ?* */@Testpublic 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_ = ?* */@Testpublic void setAssignee5(){String id="69006";String userId="小蜂子";processEngine.getTaskService().claim(id,userId);}@Testpublic void completeTask(){processEngine.getTaskService().complete("69006");System.out.println("任务完成!");}

在认领任务后可以回退

    //某人认领任务后想回退到上一个步骤,就可以将认领人设置为空/*** 个人任务分配给组任务* */@Testpublic void setAssigneeBack(){String taskId="69006";processEngine.getTaskService().setAssignee(taskId,null);}

在执行过程中突然想添加一个候选人

    /*** 添加任务的候选人* */@Testpublic void setAssigneeByUserId(){String taskId="69006";String userId="小虫子";processEngine.getTaskService().addCandidateUser(taskId,userId);}

在执行过程中删除候选人

    /*** 删除任务的候选人* */@Testpublic void deleteAssigneeByUserId(){String taskId="69006";String userId="小虫子";processEngine.getTaskService().deleteCandidateUser(taskId,userId);}
  • 通过实现TaskListener接口,可以添加组任务的办理人,也就是delegate.addCandidateUser(用户的id)。
  • processEngine.getTaskService().claim(taskId,userId)可以将组任务分配给某个指定的人,即认领任务。
  • 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(参与者)


六、网关

网关的分类:排他网关、并行网关、兼容网关

网关主要是满足一些特殊的业务,比如排他网关,可以让流程实例出现分支,当有多个条件满足的时候走一个执行分支。

并行网关有分流和聚合的功能。

兼容网关可以当做并行网使用,也可以当做排他网关使用。

1.排他网关

1.绘制流程图

连线task1的条件: ${0<=money&&money<1000}

连线task2的条件: ${1000<=money&&money<10000}

连线task3的条件: ${10000<=money}

2.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<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"><![CDATA[${0<=money&&money<1000}]]></conditionExpression></sequenceFlow><sequenceFlow id="_9" name="task2" sourceRef="_4" targetRef="two"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${1000<=money&&money<10000}]]></conditionExpression></sequenceFlow><sequenceFlow id="_10" name="task3" sourceRef="_4" targetRef="three"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${10000<=money}]]></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>
3.部署流程
 private ProcessEngine processEngine= ProcessEngines.getDefaultProcessEngine();/*** 部署流程定义* */@Testpublic 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());}
4.执行流程

根据传入变量的值,流程会走到不同的流程分支。

/*** 执行流程* */@Testpublic void startProcessInstance(){LinkedHashMap<String, Object> variables = new LinkedHashMap<>();variables.put("money","10000000000");ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById("gateway_test:1:79004",variables);}/*** 完成任务* */@Testpublic void completeTask(){processEngine.getTaskService().complete("75009");}

2.并行网关

1.绘制流程图

说明:该流程中当任务一完成后,不会继续往下执行,只有等待任务二完成后才会一起往下个节点执行,任务一二完成后就进行汇总,到达任务一二汇总节点。

2.流程部署
 /*** 部署流程定义* */@Testpublic 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());}
3.执行流程
 /*** 执行流程* */@Testpublic void startProcessInstance(){//运行成后会在act_ru_task表中创建两个实例ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById("parallelGateway:1:83004");}/*** 完成任务* */@Testpublic void completeTask(){//根据在表act_ru_task中的两个实例ID来完成任务(要两个同时完成才会走向下一个节点,一个完成另一个处于等待状态)processEngine.getTaskService().complete("84012");processEngine.getTaskService().complete("84012");}

并行网关使用:

  • 并行网关通常是承兑出现的,第一个并行网关代表分流,第二个网关代表聚合。
  • 如果只有分流,没有聚合,那么整个流程是无法结束的。
  • 并行网关上面的条件是不会生效的。

3.兼容网关

1.兼容网关既可以当做排他网关使用,也可以当做并行网关使用。

2.兼容网关连线上面的条件是可以生效的。

3.兼容网关通常情况下是成对出现的(因为他有可能转化为并行网关)。

4.如果有多个顺序流满足条件,则会当做并行网关使用。

5.如果只有单个顺序流满足条件,则会当做排他网关使用。


七、流程变量

流程变量在整个工作流中扮演很重要的作用。
流程变量的作用于范围只对应一个流程实例,各个流程实例的流程变量之间互不影响。
流程实例结束后流程变量保存到历史数据表(act_hi_varinst)中。

流程变量设置

1.启动流程实例的时候。

2.完成任务的时候

3.手工去设置变量

RuntimeService对象可以设置流程变量和获取流程变量、TaskService可以设置和获取流程变量。
启动流程实例的时候可以设置流程变量。

1.绘制流程图

2.部署资源并执行流程

     /*** 部署流程定义* */@Testpublic 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());}/*** 执行流程* */@Testpublic void startProcessInstance(){ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById("variable:1:87004");}

3.设置流程变量

      /*** 查询任务* setVariableLocal(task.getId(),"请加天数",8);* 这种方式设置变量变act_ru_variable/act_hi_variable表中的TASK_ID_有值* */@Testpublic 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());}

4.完成任务

 /*** 完成任务设置变量(完成人)* */@Testpublic 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());}

5.设置流程变量说明

  • 流程变量的作用于就是流程实例,所以无论在那个阶段设置就行了。
  • 基本类型设置流程变量,在taskService中使用ID,定义流程变量的名称,设置流程变量的值。
  • 如果是对象,则需要该对象实现序列化接口才能保存到数据库中。
  • 设置流程变量的时候会向act_ru_variable/act_hi_variable表中添加数据。

6.获取流程变量

    /*** 获取变量* */@Testpublic 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);}

7.设置一个bean对象作为变量

1.User对象
@Data
public class User  implements Serializable {private static final Integer serialVersion=12345;private String name;private Integer age;}
2.设置变量
 /*** 设置一个对象作为变量* */@Testpublic 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);}
3.获取对象
 /*** 获取变量(对象)* */@Testpublic 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);}}

8.历史变量的查询

 /*** 获取历史变量(act_hi_varinst)*/@Testpublic 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说明了流生变量绑定了当前的任务,当流程继续执行时,下一个节点的任务是获取不到这个流程变量的(因为正在执行的流程变量中没有这个数据),所有查询正在执行的人任务不能查询到我们需要的数据,此时需要查询历史流程变量表。

9.临时变量

临时变量是不会保存到数据库中的,可以在启动流程的时候设置流程变量,也可以在完成任务的时候设置临时变量。

1.启动流程时设置流程变量
 /*** 设置临时变量*/@Testpublic 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);}
2.完成时设置临时变量
 /*** 完成任务时设置流程临时变量*/@Testpublic void completeTask() {Map<String, Object> variables = new HashMap<>();variables.put("ccc", "完成任务时测试临时变量");variables.put("ddd", "完成任务时测试临时变量是否会存到数据库");processEngine.getTaskService().complete("92006", null, variables);}

八、HistoryService服务

flowable的查询历史信息类,在一个流程执行完后,该对象为我们提供查询历史信息。

实现类HistoryServiceImpl

1.绘制流程图

2.部署资源并执行流程

/*** 部署流程定义*/@Testpublic 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());}/*** 执行流程*/@Testpublic void startProcessInstance() {ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById("history:1:93004");}

3.查看历史流程实例

 /*** 查询历史流程实例表(昨天启动成功)* 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*/@Testpublic void queryHistoryProcessInstance() {List<HistoricProcessInstance> historicProcessInstances = processEngine.getHistoryService().createHistoricProcessInstanceQuery().finished().list();for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {System.out.println("结束时间:" + historicProcessInstance.getEndTime());}}

4.查看所有历史活动

 /*** 查询历史活动(也就是所有节点的历史信息)* select RES.* from ACT_HI_ACTINST RES order by RES.ID_ asc*/@Testpublic 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());}}

5.查看历史实例信息

 /*** 查询历史实例信息* select distinct RES.* from ACT_HI_TASKINST RES order by RES.ID_ asc*/@Testpublic 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());}}

6查看流程历史变量

/*** 查询历史流程变量* select RES.* from ACT_HI_VARINST RES order by RES.ID_ asc* */@Testpublic 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());}}

6.历史权限表查询

 /*** 历史权限表查询* select * from ACT_HI_TASKINST where ID_ = ?* select * from ACT_HI_IDENTITYLINK where TASK_ID_ = ?* */@Testpublic void queryHistoricIdentityLinksForTask() {List<HistoricIdentityLink> historicIdentityLinks = processEngine.getHistoryService().getHistoricIdentityLinksForTask("94006");for (HistoricIdentityLink historicIdentityLink : historicIdentityLinks) {System.out.println("************************");System.out.println("流程定义的ID"+historicIdentityLink.getScopeDefinitionId());}}

7.自定义SQL查询历史信息

    /*** 自定义SQL查询历史信息* */@Testpublic 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());}}

8.历史数据级别的配置

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>

九、定时器

1.流程定义的挂起与激活

挂起:suspendProcessDefinitionBykey()

流程定义挂起,指定过期时间

激活:activateProcessDefinitionById()

判断流程定义是否挂起:isProcessDefinitionSuspend

如果设置了指定时间内启动流程定义,那么部署流程后,默认是挂起状态,act_re_procdef表的SUSPENSION_START_为2表示挂起

    /*** 部署流程定义*/@Testpublic 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表中会插入一条数据。
定时器运行之后,流程定义会被挂起,流程实例根据是否挂起参数决定是否挂起实例。
流程定义被挂起后就不能够启动流程实例了,如果在流程实例乖巧之前已经有流程实例在运行,那么当流程定义被挂起后这些流程实例下的所有活动都会被挂起,暂停运行。

2.判断使用flowable的版本

    /*** 获取版本(是5版本还是6版本)*/@Testpublic void testVersion() {Boolean processDefinition = processEngine.getRepositoryService().isFlowable5ProcessDefinition("history:1:93004");//false表示6版本System.out.println(processDefinition);}

3.判断流程定义是否被挂起

/*** 判断流程定义是否被挂起*/@Testpublic 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)

4.手动操作定时器(ManagerService服务)

    /*** 定时器在执行的时候,会将act_ru_timer_job中的数据删除,然后添加到定时作业任务表中(act_ru_job)*/@Testpublic void managerService() {String jobId="5512";processEngine.getManagementService().moveTimerToExecutableJob(jobId);}
    /*** 设置重置次数* */@Testpublic void set() {String jobId="1564";processEngine.getManagementService().setJobRetries(jobId,10);}
    /*** 执行作业表移到死信作业表* 死信作业表(act_ru_deadletter_job)数据不会被执行,因为相关的任务在这个表中说明已经不需要在执行了。* */@Testpublic void moveJobToDeadLetterJob() {String jobId="5512";processEngine.getManagementService().moveJobToDeadLetterJob(jobId);}/*** 将act_ru_deadletter_job中的数据移动到act_ru_job中,变成可执行的任务。* */@Testpublic void moveDeadLetterJobToExecutableJob() {String jobId="5512";processEngine.getManagementService().moveDeadLetterJobToExecutableJob(jobId,10);}

十、表单FormService使用

表单使用的三种方式

  • 动态表单:只能定义表单中字段的一些配置信息,如字段的可读、可写、必须等信息,不能定义完成表单的页面。
  • 外置表单:只能定义表单的key,关于key的内容自己去维护。
  • 内置表单:内置表单定义以及渲染引擎

表单实现类:FormService–>实现类FormServiceImpl

表单定义支持的节点:开始节点、任务节点、其他节点都不支持。

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: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>

部署

 /*** 部署流程定义*/@Testpublic 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());}

获取表单属性

    /*** 获取表单的属性* */@Testpublic 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());}
}

flowable流程引擎相关推荐

  1. Flowable 流程引擎系列文章导读

    Flowable 流程引擎系列文章导读 集成篇 入门教程篇 功能篇 其他问题 集成篇 Flowable 快速入门教程:SpringBoot 集成 Flowable + Flowable Modeler ...

  2. 《Flowable流程引擎从零到壹》引入日志框架和部署流程定义

    14天学习训练营导师课程: 邓澎波<Flowable流程引擎-基础篇[2022版]> 邓澎波<Flowable流程引擎-高级篇[2022版]> 学习笔记<Flowable ...

  3. 《Flowable流程引擎从零到壹》Flowable流程引擎介绍和实战项目初始化流程引擎实例

    14天学习训练营导师课程: 邓澎波<Flowable流程引擎-基础篇[2022版]> 邓澎波<Flowable流程引擎-高级篇[2022版]> 学习笔记<Flowable ...

  4. 通过 Flowable-UI 来体验一把 Flowable 流程引擎

    [TOC] 本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究! 本专栏第一篇已发布,尚未看过的小伙伴请移步这里: Flowable 开篇,流程引擎扫盲 在我们使 ...

  5. flowable流程引擎基础概念总结

    flowable数据库说明 ACT_RE_* : 'RE'表示repository(存储).RepositoryService接口操作的表.带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片 ...

  6. Flowable流程引擎和各类表说明

    目录 Flowable 五大引擎 Flowable 数据库表类别 Flowable 常用数据库表说明 通用数据表 流程定义表 历史记录表 用户权限表 运行实例表 其他表 Flowable 五大引擎 引 ...

  7. 流程引擎之Flowable简介

    背景 Flowable 是一个流行的轻量级的采用 Java 开发的业务流程引擎,通过 Flowable 流程引擎,我们可以部署遵循 BPMN2.0 协议的流程定义(一般为XML文件)文件,并能创建流程 ...

  8. flowable 查询完成的流程_flowable流程引擎初体验,完成一个请假流程

    flowable是一个用Java写的轻量级商业流程引擎,用它可以部署BPMN2.0(在工业界被广泛接受的XML标准)流程定义, 并且可以创建流程实例,驱动节点流转,存储相关的历史数据等等.可能更多人先 ...

  9. activiti5、activiti6、activiti7、flowable、camunda7、camunda8流程引擎对比分析和选型参考

    常见的开源工作流引擎有哪些?该如何选择?市场上比较有名的开源流程引擎有osworkflow.jbpm.activiti.flowable.camunda,其中activiti又有activiti5.a ...

最新文章

  1. Javascript基础系列之(三)数据类型 (数值 Number)
  2. html表单验证元素必填,AngularJS表单验证:向用户指示必填字段
  3. Java中使用Jedis操作Redis
  4. HDU - 4027 Can you answer these queries?(线段树)
  5. PostgreSQL Oracle 兼容性之 - rownum
  6. 数据库查询字段为空时,返回0
  7. 【JAVA 第三章 流程控制语句】课后习题 月历打印
  8. android8 压力触控,压力感应触摸屏的原理说明
  9. python登录斗鱼_Python---20行代码爬取斗鱼平台房间数据(下)
  10. 全方面解析软件测试行业发展现状及前景
  11. 小型项目的微服务架构指南
  12. 天黑时间跟经度还是纬度有关_经纬度和时间气候
  13. 小马哥杂牌机高仿机刷机教程---史上最简单的卡刷方法。adb推送模式自动卡刷教程
  14. 时间计算题100道_@所有考生,答应大家的政经10道计算题来了!
  15. java微信开发之--更换背景图片
  16. PyTorch 音频处理教程
  17. 阿里云服务器ECS到底是什么?
  18. MOOC《程序设计入门——C语言》翁恺 第六周编程练习及答案
  19. python实现支持向量机实例_一个简单的案例带你了解支持向量机算法(Python代码)...
  20. dataFrame使用loc根据if条件修改列值

热门文章

  1. Flash Builder 4注册机下载地址
  2. 内核编译图文教程,显卡篇
  3. window下eslipse搭建django遇到的问题以及解决方法积累(持续更新)
  4. HTMLCSS基本样式
  5. WCDMA的R99版本网络结构
  6. 最近在开发一款答题类微信小程序
  7. xml中遍历数组或集合
  8. md5在 node 中如何使用
  9. 2020中国高校信息化发展论坛圆满落幕
  10. 【简谈】可编程逻辑器件发展历史及工艺分类