# Flowable **Repository Path**: springbootPlus/flowable_demo ## Basic Information - **Project Name**: Flowable - **Description**: Flowable 演示 Demo, 用来熟悉 Flowable 工作流 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 68 - **Created**: 2019-01-19 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、Flowable 介绍 ​ [参考](https://blog.csdn.net/puhaiyang/article/details/79845248) [官网](https://flowable.org) ​ Flowable是一个用java语言写的轻量级工作流引擎,与Activiti框架相比开发方便快速,易与Spring Boot等各种框架快速整合。如果项目中需要快速实现一些工作流的相关功能那么用此框架是一个不错的选择。 ​ Flowable支持3种不同流程引擎:BPMN用于流程管理,CMMN用于案例管理,DMN用于决策规则 ## 1、BPMN2.0规范说明 ​ [参考学习](https://blog.csdn.net/iwillbeaceo/article/details/74394920) ## 2、数据库说明 ![数据表](QQ截图20180925104800.png) `act_cmmn_`: 表示的是CMMN流程引擎(cmmn)数据 `act_dmn_`: 表示的是DMN流程引擎(dmn)数据 `act_evt_log`表示事件日志 `act_fo_`: 表示的是表单引擎(form)数据 `act_ge_`: 表示的是通用数据(general),用于各种用例 `act_hi_`: 表示这些表包含历史数据(history),例如过去的流程实例、变量、任务等等 `act_id_`: 表示存储的是与用户身份(identity)相关的数据 `act_prodef_info`表示的是流程定义的信息。 `act_re_`: 表示的是流程定义(repository)相关数据的存储库。具有此前缀的表包含静态信息,例如流程定义和流程资源(图像、规则等) `act_ru_`: 表示的是流程实例运行时(runtime)时的数据,这些是包含流程实例、用户任务、变量、作业等运行时数据的运行时表。Flowable仅在流程实例执行期间存储运行时数据,并在流程实例结束时删除记录。这使运行时表保持小而快。 ​ 租户也就是**TENANT_ID_(tenantId)**。该值主要用于记录启动的流程实例归属于哪个系统,比如a,b,c三个系统都有一个请假流程并且数据存储在同一个数据库,这个时候就应该考虑如何区分这三个流程了。 ## 3、流程基本步骤 ### 3.1 流程部署 一共六种方式部署流程文档 ``` DeploymentBuilder addInputStream(String resourceName, InputStreaminputStream); DeploymentBuilder addClasspathResource(String resource); DeploymentBuilder addString(String resourceName, String text); DeploymentBuilder addBytes(String resourceName, byte[] bytes); DeploymentBuilder addZipInputStream(ZipInputStream zipInputStream); DeploymentBuilder addBpmnModel(String resourceName, BpmnModel bpmnModel); ``` #### 3.1.1 addInputStream 方式 @Test public void addInputStreamTest() throws IOException { // 定义的文件信息的流读取 InputStream inputStream = DeploymentBuilderTest.class.getClassLoader() .getResource("com/shareniu/shareniu_flowable6/ch2/leave.bpmn") .openStream(); // 流程定义的分类 String category = "shareniu_addInputStream"; // 构造DeploymentBuilder对象 DeploymentBuilder deploymentBuilder = repositoryService .createDeployment().category(category) .addInputStream(resourceName, inputStream); // 部署 Deployment deploy = deploymentBuilder.deploy(); System.out.println(deploy); } #### 3.1.2 addClasspathResource 方式 public void addClasspathResource() throws InterruptedException { ProcessEngineConfigurationImpl processEngineConfiguration = Context .getProcessEngineConfiguration(); System.out.println(processEngineConfiguration); String resource = "com/shareniu/chapter3/common.bpmn"; // 流程定义的分类 String category = "shareniu_addClasspathResource"; // 构造DeploymentBuilder对象 Date date = new Date(); DeploymentBuilder deploymentBuilder = repositoryService .createDeployment() // .activateProcessDefinitionsOn(date) .addClasspathResource(resource).category(category); // 部署 processEngineConfiguration = Context.getProcessEngineConfiguration(); System.out.println(processEngineConfiguration); Deployment deploy = deploymentBuilder.deploy(); System.out.println(deploy); } #### 3.1.3 addString 方式 @Test public void addString() { String resource = "shareniu_addString.bpmn"; // 读取文件获取文件中定义的xml信息 String text = readTxtFile("E:/activitilearing/activiti/src/main/java/com/shareniu/chapter3/common.bpmn"); // 构造DeploymentBuilder对象 DeploymentBuilder deploymentBuilder = repositoryService .createDeployment().addString(resource, text); // 部署 Deployment deploy = deploymentBuilder.deploy(); } #### 3.1.4 addZip 方式 @Test public void zip() { InputStream inputStream = DeploymentBuilderTest.class.getClassLoader() .getResourceAsStream("1.zip"); ZipInputStream zipInputStream = new ZipInputStream(inputStream); Deployment deploy = repositoryService.createDeployment() .addZipInputStream(zipInputStream).deploy(); identityService.setAuthenticatedUserId("shareniu"); } #### 3.1.5 addByte 方式 该方式可以直接将字符串转化为byte数组进行部署即可。 #### 3.1.6 BpmnModel 方式 SequenceFlow flow1 = new SequenceFlow(); flow1.setId("flow1"); flow1.setName("开始节点->任务节点1"); flow1.setSourceRef("start1"); flow1.setTargetRef("userTask1"); // 任务节点1->任务节点2 SequenceFlow flow2 = new SequenceFlow(); flow2.setId("flow2"); flow2.setName("任务节点1->任务节点2"); flow2.setSourceRef("userTask1"); flow2.setTargetRef("userTask2"); // 任务节点1->任务节点2 SequenceFlow flow3 = new SequenceFlow(); flow3.setId("flow3"); flow3.setName("任务节点2->结束节点"); flow3.setSourceRef("userTask2"); flow3.setTargetRef("endEvent"); String resource = "shareniu_addBpmnModel"; // 声明BpmnModel对象 BpmnModel bpmnModel = new BpmnModel(); // 声明Process对象 一个BpmnModel可以包含多个Process对象 Process process = new Process(); process.setId("process1"); // 开始节点的封装 StartEvent start = new StartEvent(); start.setName("开始节点"); start.setId("start1"); start.setOutgoingFlows(Arrays.asList(flow1)); // 任务节点1 UserTask userTask1 = new UserTask(); userTask1.setName("任务节点1"); userTask1.setId("userTask1"); userTask1.setIncomingFlows(Arrays.asList(flow1)); userTask1.setOutgoingFlows(Arrays.asList(flow2)); // 任务节点2 UserTask userTask2 = new UserTask(); userTask2.setName("任务节点2"); userTask2.setId("userTask2"); userTask2.setIncomingFlows(Arrays.asList(flow2)); userTask2.setOutgoingFlows(Arrays.asList(flow3)); // 结束节点 EndEvent endEvent = new EndEvent(); endEvent.setName("结束节点"); endEvent.setId("endEvent"); endEvent.setIncomingFlows(Arrays.asList(flow3)); // 将所有的FlowElement添加到process中 process.addFlowElement(start); process.addFlowElement(flow1); process.addFlowElement(userTask1); process.addFlowElement(flow2); process.addFlowElement(userTask2); process.addFlowElement(flow3); process.addFlowElement(endEvent); bpmnModel.addProcess(process); ### 3.2 发起流程 repositoryService: 主要用于管理流程部署的数据 runtimeService: 主要用于管理流程在运行时产生的数据(流程参数,事件,流程实例,以及执行流)以及对正在运行的流程进行操作的API #### 3.2.1 基础使用 String processDefinitionId="myProcess:1:9527";//部署的流程ID runtimeService.startProcessInstanceById(processDefinitionId);//发起流程 #### 3.2.2 设置流程发起人 Authentication.setAuthenticatedUserId("Foolish"); String processDefinitionId="myProcess:1:9527"; runtimeService.startProcessInstanceById(processDefinitionId); Authentication.setAuthenticatedUserId(null); 首先,调用Authentication.setAuthenticatedUserId方法即可, 注意一点:这个方法最终使用一个ThreadLocal类型的变量进行存储,也就是与当前的线程绑定,所以流程实例启动完毕之后,需要设置为null,防止多线程的时候出问题。 上述的代码执行完毕之后,act_hi_procinst表中中的START_USER_ID_字段值已经变为“Foolish”了, ![发起人](QQ截图20180926145201.png) ### 3.3 任务查询 #### 3.3.1 我的任务 - [流程实例] public GritResult instanceList(String userId) { ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery(); if(!StringUtils.isEmpty(userId)){ query.startedBy(userId); } List processInstanceList = query.list(); } #### 3.3.1 待我处理 public GritResult taskList(String userId, String group) { TaskQuery query = taskService.createTaskQuery(); if(!StringUtils.isEmpty(group)){ query.taskCandidateGroup("managers"); }else if(!StringUtils.isEmpty(userId)){ query.taskCandidateOrAssigned("userId"); } List tasks = query.orderByTaskCreateTime().desc().list(); } #### 3.3.2 已完成 [历史流程实例查询参考](https://www.jianshu.com/p/518d61b5c338) ``` public GritResult historicTaskList(String userId) { HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery(); if(!StringUtils.isEmpty(userId)){ query.involvedUser(userId); } query.finished(); List hpis = query.orderByProcessInstanceEndTime().desc().list(); List list = new ArrayList<>(); for (HistoricProcessInstance hpi : hpis) { HistoricProcessInstance_ ft = new HistoricProcessInstance_(hpi); System.out.println(ft.toString()); list.add(ft); } GritResult gr = new GritResult("success",list.size(),list); return gr; } ``` ### 3.4 流程审批 这里只做简单的案例, 通过设置流程中的参数**outcome** 控制流程后面的走向 /** * 批准 * * @param taskId 任务ID */ public String apply(String taskId) { Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); if (task == null) { throw new RuntimeException("流程不存在"); } //通过审核 HashMap map = new HashMap<>(); map.put("outcome", "通过"); taskService.complete(taskId, map); return "processed ok!"; } /** * 拒绝 */ public String reject(String taskId) { HashMap map = new HashMap<>(); map.put("outcome", "驳回"); taskService.complete(taskId, map); return "reject"; }