flowable工作流学习笔记

不同版本使用方式不一样,案例使用两个版本6.5.0及6.6.0,学习中6.5.0 版本是独立框架(服务单独部署)使用的, 6.6.0与springboot集成,

6.5.0版本如下:

下载flowable:

https://github.com/flowable/flowable-engine/releases

选择6.5.0版本

在这里插入图片描述

解压:
在这里插入图片描述
database中为所需的表,新建flowable数据库,导入mysql即可:

在这里插入图片描述
将war文件夹中的5个jar包复制到tomcat的webapps文件中
在这里插入图片描述
flowable-admin:后台管理
flow-idm:用户组权限管理
flow-modeler:流程定义管理
flowable-rest:流程引擎对外提供的API接口
flowable-task:用户任务管理

启动tomcat
在这里插入图片描述
修改每个项目的配置文件,将数据库配置修改为自己的地址:
在这里插入图片描述
其他均是flowable-default文件。

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/flowable?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Hongkong&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=XXXXX

每个项目的lib中需要加入mysql驱动包。

在这里插入图片描述

重新启动tomcat后登录:

 http://127.0.0.1:8080/flowable-idm   用户名 admin 密码 test http://127.0.0.1:8080/flowable-admin   用户名 admin 密码 testhttp://127.0.0.1:8080/flowable-modeler   用户名 admin 密码 test 

需要先登录idm系统后再登录其他系统,否则会登录失败。

框架中引入

  <!-- 工作流flowable架包 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.5.0</version></dependency>

6.6.0版本如下:

集成flowable模块
在这里插入图片描述

6.6.0版本集成到框架中即可:引入flowable-ui及modeler即可

 <!-- 工作流flowable架包 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.6.0</version></dependency><!--流程引擎的引用,单例唯一,可以通过它获得所有api的服务对象--><dependency><groupId>org.flowable</groupId><artifactId>flowable-engine</artifactId><version>6.6.0</version></dependency><dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-common</artifactId><version>6.6.0</version></dependency><!-- modeler绘制流程图 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-ui-modeler</artifactId><version>6.6.0</version></dependency><!-- idm依赖提供身份认证 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-ui-idm</artifactId><version>6.6.0</version></dependency>

application配置文件:

server:port: 8085
spring:application:name: flowabledatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/flowable?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT&nullCatalogMeansCurrent=trueusername: rootpassword: root
flowable:#关闭定时任务JOBasync-executor-activate: false#将databaseSchemaUpdate设置为true。当flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。database-schema-update: trueactivity-font-name: '楷体'
#flowable打印sql
logging:level:org.flowable.engine.impl.persistence.entity.*: debugorg.flowable.task.service.impl.persistence.entity.*: debug

完整pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springcloud</artifactId><groupId>com.example</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>flowable-master</artifactId><properties><mybatis-plus.version>3.3.2</mybatis-plus.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.3.3.RELEASE</version><scope>test</scope></dependency><!-- 工作流flowable架包 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter</artifactId><version>6.6.0</version></dependency><!--流程引擎的引用,单例唯一,可以通过它获得所有api的服务对象--><dependency><groupId>org.flowable</groupId><artifactId>flowable-engine</artifactId><version>6.6.0</version></dependency><!--flowable-ui--><dependency><groupId>org.flowable</groupId><artifactId>flowable-ui-common</artifactId><version>6.6.0</version></dependency><!-- modeler绘制流程图 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-ui-modeler</artifactId><version>6.6.0</version></dependency><!-- idm依赖提供身份认证 --><dependency><groupId>org.flowable</groupId><artifactId>flowable-spring-boot-starter-ui-idm</artifactId><version>6.6.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.2</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.3.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- jpa --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- thymeleaf模块引擎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>
</project>

启动后访问localhost:8085

登录后画流程图:
成果图:
在这里插入图片描述
每个节点详情(主键id、参数等信息自定义即可):
新建模型:key自定义即可,用于标识流程。

在这里插入图片描述

开始:
在这里插入图片描述

申请:
在这里插入图片描述
在这里插入图片描述

申请流程:
在这里插入图片描述

主管审批:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
排他网关:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
老板审批:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

保存后导出:
在这里插入图片描述
导出BPMN文件:

在这里插入图片描述
建好如下文件:
在这里插入图片描述

表说明:

大部分表是支持流程设计器的, 真正流程运行所需的表并不多。
表的功能一般可以通过第二个词语缩写来进行区分。


ACT_RE_*
’RE’表示repository(存储)。RepositoryService接口操作的表。带此前缀的表包含的是静态信息,如流程定义、流程的资源(图片,规则等)。
ACT_RU_*
’RU’表示runtime。这是运行时的表存储着流程变量,用户任务、变量、职责(job)等运行时的数据。flowable只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
ACT_ID_*
’ID’表示identity(组织机构)。这些表包含标识的信息,如用户、用户组等。
一般在正式系统中, 会将这些表用业务系统的组织机构, 角色表进行替换。
ACT_HI_*
’HI’表示history。就是这些表包含着历史的相关数据,如结束的流程实例、变量、任务等。
ACT_GE_*
普通数据,各种情况都使用的数据。
表名表说明
一般数据对应表
ACT_GE_BYTEARRAY通用的流程定义和流程资源
ACT_GE_PROPERTY系统相关属性
流程历史对应表
ACT_HI_ACTINST历史的流程实例详情
ACT_HI_ATTACHMENT历史的流程附件
ACT_HI_COMMENT历史的说明性信息
ACT_HI_DETAIL历史的流程运行中的细节信息
ACT_HI_IDENTITYLINK历史的流程运行过程中用户关系
ACT_HI_PROCINST历史的流程实例
ACT_HI_TASKINST历史的任务实例
ACT_HI_VARINST历史的流程运行中的变量信息
用户用户组对应表
ACT_ID_BYTEARRAY二进制数据表
ACT_ID_GROUP用户组信息表
ACT_ID_INFO用户信息详情表
ACT_ID_MEMBERSHIP人与组关系表
ACT_ID_PRIV权限表
ACT_ID_PRIV_MAPPING用户或组权限关系表
ACT_ID_PROPERTY属性表
ACT_ID_TOKEN系统登录日志表
ACT_ID_USER用户表
流程定义表
ACT_RE_MODEL模型信息
ACT_RE_DEPLOYMENT部署单元信息
ACT_RE_PROCDEF已部署的流程定义
运行实例表
ACT_RU_DEADLETTER_JOB正在运行的任务表
ACT_RU_EVENT_SUBSCR运行时事件
ACT_RU_EXECUTION运行时流程执行实例
ACT_RU_HISTORY_JOB历史作业表
ACT_RU_IDENTITYLINK运行时用户关系信息
ACT_RU_JOB运行时作业表
ACT_RU_SUSPENDED_JOB暂停作业表
ACT_RU_TASK运行时任务表
ACT_RU_TIMER_JOB定时作业表
ACT_RU_VARIABLE运行时变量表
其他表
ACT_EVT_LOG事件日志表
ACT_PROCDEF_INFO流程定义信息

在这里插入图片描述

互斥网关(Exclusive Gateway),又称排他网关,他有且仅有一个有效出口,可以理解为if…else if… else if…else,就和我们平时写代码的一样。
并行网关(Parallel Gateway),他的所有出口都会被执行,可以理解为开多线程同时执行多个任务。
包容性网关(Inclusive Gateway),只要满足条件的出口都会执行,可以理解为 if(…) do, if (…) do, if (…) do,所有的条件判断都是同级别的。

工作流测方法总结:

import com.tt.flowable.FlowerApplication;
import com.tt.flowable.pojo.ResponseBean;
import com.tt.flowable.pojo.leave.leaveInfo;
import org.apache.commons.collections.CollectionUtils;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Attachment;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.idm.api.Group;
import org.flowable.idm.api.User;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;@SpringBootTest(classes = FlowerApplication.class)
public class flowtest {@Autowiredprivate RuntimeService runtimeService;@Autowiredprivate TaskService taskService;@Autowiredprivate HistoryService historyService;@AutowiredRepositoryService repositoryService;/*** 查询用户*/@Testpublic void queryUser(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//通过IdentityService完成相关的用户和组的管理IdentityService identityService = processEngine.getIdentityService();//使用userIdUser u=identityService.createUserQuery().userId("用户01").singleResult();List<User> list=identityService.createUserQuery().list();for(User user:list){System.out.println(user.getId()+";"+user.getFirstName());}}/*** 新增用户act_id_user*/@Testpublic void createUser(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();//通过IdentityService完成相关的用户和组的管理IdentityService identityService = processEngine.getIdentityService();User user = identityService.newUser("0001");user.setFirstName("QQ");//user.setLastName("01");user.setEmail("6666666@qq.com");identityService.saveUser(user);}/*** 创建用户组 ACT_ID_GROUP*/@Testpublic void createGroup(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();IdentityService identityService = processEngine.getIdentityService(); // 创建Group对象并指定相关的信息Group group = identityService.newGroup("G1");group.setName("研发部");group.setType("T1"); // 创建Group对应的表结构数据identityService.saveGroup(group);}/*** 将用户分配给对应的Group* ACT_ID_MEMBERSHIP* 用户和组是一个多对多的关联关联*/@Testpublic void userGroup(){ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();IdentityService identityService = processEngine.getIdentityService(); // 根据组的编号找到对应的Group对象Group group = identityService.createGroupQuery().groupId("G1").singleResult();//查询用户组List<User> list = identityService.createUserQuery().list();for (User user : list) { // 将用户分配给对应的组if("用".equals(user.getFirstName())){identityService.createMembership(user.getId(),group.getId());}}}/*** 状态*/@Testvoid testStatus(){boolean status=taskService.createTaskQuery().taskId("taskId").singleResult().isSuspended();//Suspended=2中止,=1正常if (status){//是否挂起(中止)}}/*** 终止删除流程、挂起(中止)*/@Testvoid deletetask(){Map<String,Object> map=new HashMap<>();String processInstanceId="";List<Task> procInsId = taskService.createNativeTaskQuery().sql("select * from ACT_HI_TASKINST where PROC_INST_ID_ = #{procInsId} ORDER BY START_TIME_ desc").parameter("procInsId", processInstanceId).list();if (!procInsId.get(1).getAssignee().equals("revokeVo.getUserCode()")){map.put("result",false);map.put("message","当前流程已不在下级节点!");}//作废-删除流程,删除流程历史记录runtimeService.deleteProcessInstance(processInstanceId,"不请了删除流程实例");//删除任务,无processInstanceId任务时会报错。historyService.deleteHistoricProcessInstance(processInstanceId);runtimeService.suspendProcessInstanceById(processInstanceId);//挂起runtimeService.activateProcessInstanceById(processInstanceId);;//激活}/*** 获取下一节点信息(bpmn20.xml中的节点信息)* @param taskId*/void getNextWork(String taskId){Task currentTask = taskService.createTaskQuery().taskId(taskId).singleResult(); // 获取当前任务List<SequenceFlow> outgoingFlows =((FlowNode)(repositoryService.getBpmnModel(currentTask.getProcessDefinitionId()).getMainProcess()).getFlowElement(currentTask.getTaskDefinitionKey())).getOutgoingFlows();for (SequenceFlow outgoingFlow : outgoingFlows) {String nextTaskId = outgoingFlow.getTargetRef();//节点的id,非任务String nextTaskName = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId()).getMainProcess().getFlowElement(nextTaskId).getName();System.out.println("Next task ID: " + nextTaskId + ", Next task name: " + nextTaskName);// taskService.setAssignee(nextTaskId,"三三");// 下一个审批节点FlowElement targetFlow = outgoingFlow.getTargetFlowElement();//如果是用户任务则获取待办人或组if (targetFlow instanceof UserTask){// 如果下个审批节点为userTaskUserTask userTask = (UserTask) targetFlow;userTask.setAssignee("主管审批");}}}// 删除流程 :(data:删除原因)@Testpublic void deleteProcessInstanceId(){try{String processInstanceId="a4b311ec-9e13-11ee-ac38-00ffb3ed8746";//需要添加这段代码,否则审批意见表ACT_HI_COMMENT审批的userid是空的// Authentication.setAuthenticatedUserId("老总");//审核意见//taskService.addComment("2122f113-9d70-11ee-bd37-00ffb3ed8746","5145648b-9b13-11ee-8409-00ffb3ed8746","批准");runtimeService.deleteProcessInstance(processInstanceId,"删除原因");//删除运行中流程historyService.deleteHistoricProcessInstance(processInstanceId);//删除历史流程}catch (Exception e){e.printStackTrace();}}//提交申请@Testpublic void sub() {// 员工提交请假申请Map<String, Object> map = new HashMap<>();map.put("days", 4);map.put("name", "QQ");map.put("reason","调价");//map.put("userIds", "laozhao,laowang,lisi");//1、赋值审批分配人keytry{//启动流程ProcessInstance a1=runtimeService.startProcessInstanceByKey("leaveprocessmore",map.get("name").toString(),map);Task task = taskService.createTaskQuery().processInstanceId(a1.getId()).singleResult();/* Map<String,Object> m=new HashMap<>();m.put("userIds", "老王,老赵");//2、赋值审批分配人key同1taskService.setVariables(task.getId(),m);*///处理任务taskService.complete(task.getId());}catch (Exception e){e.printStackTrace();}}/*** 候选人为多个时需要先认领任务在处理,认领后其他人不能再操作此任务*/@Testvoid taskClaim(){//认领List<Task> list = taskService.createTaskQuery().taskCandidateUser("QQ").list();for (Task task : list) {taskService.claim(task.getId(), "QQ");}//处理审批任务list = taskService.createTaskQuery().taskAssignee("QQ").list();for (Task task : list) {Map<String, Object> leaves = new HashMap<>();leaves.put("approved", true);Authentication.setAuthenticatedUserId("QQ");//审核意见taskService.addComment("54981962-9e15-11ee-8123-00ffb3ed8746","54122175-9e15-11ee-8123-00ffb3ed8746","主管批准");taskService.complete(task.getId(), leaves);}}/*** 认领任务*/@Testvoid taskClaimUser() {//认领List<Task> list = taskService.createTaskQuery().taskCandidateUser("老赵").list();for (Task task : list) {taskService.claim(task.getId(), "老赵");}}/*** 放弃、取消已认领任务*/@Testpublic void taskUnClaim() {List<Task> list = taskService.createTaskQuery().taskAssignee("老赵").list();//根据任务id查询//Task task= taskService.createTaskQuery().taskId("").singleResult();for (Task task : list) {taskService.unclaim(task.getId());//放弃认领}}/*** 更换认领人*/@Testpublic void taskClaimUserChange() {//根据任务id查询Task task= taskService.createTaskQuery().taskId("").singleResult();taskService.setAssignee(task.getId(), "老王");}/*** 附件应用-保存* attachmenttype附件类型* attachmentdescription附件说明*/@Testvoid saveAttavhment(){Attachment attachment=taskService.createAttachment("bpmn20.xml","0b0ffc62-9e16-11ee-8e0c-00ffb3ed8746","54122175-9e15-11ee-8123-00ffb3ed8746","流程文件","记录流程文件","E:\\springcloud\\flowable-master\\target\\classes\\processes\\请假more.bpmn20.xml");taskService.saveAttachment(attachment);}/*** 附件应用-查询*/@Testvoid taskAttavhment(){Attachment attachment=taskService.getAttachment("a3a30f3a-9e18-11ee-aef3-00ffb3ed8746");//获取附件对象InputStream inputStream= taskService.getAttachmentContent("a3a30f3a-9e18-11ee-aef3-00ffb3ed8746");List<Attachment> attachmentList=taskService.getProcessInstanceAttachments("54122175-9e15-11ee-8123-00ffb3ed8746");for(Attachment att:attachmentList){System.out.println("url:"+att.getUrl()+";description:"+att.getDescription());}}/*** 老板审批(批量)*/@Testvoid approveMore(){//查询所属任务List<Task> list = taskService.createTaskQuery().taskCandidateGroup("boss").list();Map<String,Object> leaves=new HashMap<>();leaves.put("bossapproved", true);for (Task task : list) {taskService.complete(task.getId(), leaves);if (true) {//如果是同意,还需要继续走一步Task t = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();if (t == null) {System.out.println("工作流已完结!!!!");break;}taskService.complete(t.getId());}}}/*** 老板审批(单任务)、记录审批意见*/@Testvoid approveSingle(){//查询所属任务Task task = taskService.createTaskQuery().processInstanceId("5145648b-9b13-11ee-8409-00ffb3ed8746").singleResult();Map<String,Object> leaves=new HashMap<>();leaves.put("bossapproved", true);//需要添加这段代码,否则审批意见表ACT_HI_COMMENT审批的userid是空的Authentication.setAuthenticatedUserId("老总");//审核意见taskService.addComment("2122f113-9d70-11ee-bd37-00ffb3ed8746","5145648b-9b13-11ee-8409-00ffb3ed8746","批准");taskService.complete(task.getId(), leaves);if (true) {//如果是同意,还需要继续走一步Task t = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).singleResult();if (t == null) {System.out.println("工作流已完结!!!!");return;}taskService.complete(t.getId());}}/***查询审批意见*/@Testvoid queryTaskComment(){List<Comment> taskComments = taskService.getTaskComments("2122f113-9d70-11ee-bd37-00ffb3ed8746");for (Comment taskComment : taskComments) {System.out.println("审批人:" + taskComment.getUserId());System.out.println("审批意见:" + taskComment.getFullMessage());}}/*** 设置候选人*/@Testvoid setCandidate(){Map<String, Object> userIds = new HashMap<>();userIds.put("userIds", "老赵,老王,老李");ProcessInstance pi = runtimeService.startProcessInstanceByKey("leaveprocessmore",userIds);System.out.println("id:"+pi.getId()+";activityId:"+pi.getActivityId());}/*** 追加候选人*/@Testvoid addCandidate(){List<Task> list = taskService.createTaskQuery().taskCandidateUser("老赵").list();List<Task> listt = taskService.createTaskQuery().taskId("taskId").list();for (Task task : list)taskService.addCandidateUser(task.getId(),"老李");}/*** 删除候选人*/@Testvoid delCandidate() {List<Task> list = taskService.createTaskQuery().taskCandidateUser("老赵").list();for (Task task : list) {taskService.deleteCandidateUser(task.getId(), "老李");}}/*** 查询流程历史参与人记录*/@Testvoid queryHistoryUser() {List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().list();for (HistoricProcessInstance historicProcessInstance : list) {List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForProcessInstance(historicProcessInstance.getId());for (HistoricIdentityLink link : links)System.out.println("userId:,taskId:,type:,processInstanceId:"+ link.getUserId()+";"+link.getTaskId()+";"+ link.getType()+";"+ link.getProcessInstanceId());}}/*** 查询一个 Task的历史候选人与处理人*/void queryHistoryTaskUser() {List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().list();for (HistoricTaskInstance historicTaskInstance : list) {List<HistoricIdentityLink> links = historyService.getHistoricIdentityLinksForTask(historicTaskInstance.getId());for (HistoricIdentityLink link : links) {System.out.println("userId:,taskId:,type:,processInstanceId:" + link.getUserId() + ";" +link.getTaskId() + ";" + link.getType() + ";" + link.getProcessInstanceId());}}}/*** 查询分配人或候选用户人的任务*/@Testvoid querytask(){//候选用户及多个候选用户时List<Task> task = taskService.createTaskQuery().taskCandidateUser("李思思").list();//分配人List<Task> tasks = taskService.createTaskQuery().taskAssignee("李思思").list();//候选用户或分配人List<Task> task2 =taskService.createTaskQuery().taskCandidateOrAssigned("lisi").list();System.out.println(task.size()+"=="+tasks.size());}/*** 获取历史流程*/@Testvoid queryHistory() {List<ProcessInstance> processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").orderByStartTime().desc().list();if (CollectionUtils.isEmpty(processInstance)) {System.out.println("------------------------------------------");}// 获取最近的一个流程List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstance.get(0).getId())// 只查询已经完成的活动.finished()// 按照结束时间排序.orderByHistoricActivityInstanceEndTime().desc().list();List<String> collect = activities.stream().map(a -> "活动名称:" + a.getActivityName() + ";活动执行时间:" + a.getDurationInMillis() + "毫秒").collect(Collectors.toList());for (String s : collect) {System.out.println(s);}}/*** 请假列表(已完结、未完结)*/@Testpublic void searchResult() {String name = "李思思";List<leaveInfo> leaveInfos = new ArrayList<>();//已完结的// List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(name).finished().orderByProcessInstanceEndTime().desc().list();//未完结的List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(name).unfinished().orderByProcessInstanceStartTime().list();for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {leaveInfo leaveInfo = new leaveInfo();Date startTime = historicProcessInstance.getStartTime();Date endTime = historicProcessInstance.getEndTime();List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(historicProcessInstance.getId()).list();for (HistoricVariableInstance historicVariableInstance : historicVariableInstances) {String leaveName = historicVariableInstance.getVariableName();Object value = historicVariableInstance.getValue();if ("reason".equals(leaveName)) {leaveInfo.setReason((String) value);} else if ("days".equals(leaveName)) {leaveInfo.setDays(Integer.parseInt(value.toString()));} else if ("approved".equals(leaveName)) {leaveInfo.setStatus((Boolean) value);} else if ("name".equals(leaveName)) {leaveInfo.setName((String) value);}}leaveInfo.setStartTime(startTime);leaveInfo.setEndTime(endTime);leaveInfos.add(leaveInfo);}System.out.println(leaveInfos.size());}}

主管审批使用监听器动态设置候选人:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

MyUserTaskListener代码:

package com.tt.flowable.lister;import org.flowable.engine.RuntimeService;
import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.task.service.delegate.TaskListener;
import org.springframework.beans.factory.annotation.Autowired;import java.util.Map;/*** 委派任务* 监听器类型:任务监听器* 主管审批动态分配候选人*/
public class MyUserTaskListener implements TaskListener {@Overridepublic void notify(DelegateTask delegateTask) {delegateTask.addCandidateUser("老赵");delegateTask.addCandidateUser("老王");System.out.println("任务监听器:"+delegateTask.getId()+";"+delegateTask.getName());}
}

主要表信息:
在这里插入图片描述
串行任务撤回(主动):

    1、先判断是否可撤回:上一个节点是当前用户审核的,并且下一节点审批人还未处理时,可撤回到上一节点;2、撤回步骤:保存并删除BpmnModel当前流程节点输出流;3、把当前流程节点输出流方向改成上一个节点;4、撤回任务完成后,再次恢复当前节点的原有输出流;

service

  @AutowiredRepositoryService repositoryService;//串行任务【主动撤回】public void proBack(String instanceId,String canBackTaskId,String userId){/*1、先判断是否可撤回:上一个节点是当前用户审核的,并且下一节点审批人还未处理时,可撤回到上一节点;2、撤回步骤:保存并删除BpmnModel当前流程节点输出流;3、把当前流程节点输出流方向改成上一个节点;4、撤回任务完成后,再次恢复当前节点的原有输出流;*/Map<String,Object> result = canBack(instanceId, canBackTaskId, userId);Boolean canBack=(Boolean)result.get("canBack");HistoricTaskInstance currentTask=(HistoricTaskInstance)result.get("currentTask");if(!canBack){System.out.println("流程:"+instanceId+",任务:"+canBackTaskId+"已经处理,不可撤回");return;}//流程撤回HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery().processInstanceId(instanceId).singleResult();//历史的【流程】实例BpmnModel bpmnModel = repositoryService.getBpmnModel(instance.getProcessDefinitionId());//ProcessDefinitionId:leaveprocessmore:11:20f8063c-9641-11ee-b051-00ffb3ed8746//上一流程HistoricTaskInstance canBackTask = historyService.createHistoricTaskInstanceQuery().taskId(canBackTaskId).taskAssignee(userId).singleResult();if(canBackTask==null){System.out.println("未找到要回退的任务");return;}FlowNode canBackNode = (FlowNode)bpmnModel.getFlowElement(canBackTask.getTaskDefinitionKey());FlowNode currentNode=(FlowNode)bpmnModel.getFlowElement(currentTask.getTaskDefinitionKey());//记录原始输出流程List<SequenceFlow> sourceOuts =new ArrayList<>();sourceOuts.addAll(currentNode.getOutgoingFlows());//清理活动方向currentNode.getOutgoingFlows().clear();//创建新流List<SequenceFlow> newFlows=new ArrayList<>();SequenceFlow newSequenceFlow = new SequenceFlow();newSequenceFlow.setId("newSequenceFlowId");newSequenceFlow.setSourceFlowElement(currentNode);//bossApproval老总newSequenceFlow.setTargetFlowElement(canBackNode);//上一环节targetRef=supApproval(主管)newFlows.add(newSequenceFlow);currentNode.setOutgoingFlows(newFlows);//记录备注信息Authentication.setAuthenticatedUserId(userId);taskService.addComment(currentTask.getId(), currentTask.getProcessInstanceId(), "撤回");//完成撤回任务taskService.complete(currentTask.getId());//恢复原方向currentNode.setOutgoingFlows(sourceOuts);//删除当前任务历史记录,根据业务需要是否删除historyService.deleteHistoricTaskInstance(currentTask.getId());}/*** 判断流程是否能够撤回* 上一个节点是当前用户审核,并且下一节点审批人还未处理时,可撤回到上一节点* @return*/public Map<String,Object> canBack(String instanceId,String canBackTaskId,String userId){boolean canBack=false;Map<String,Object> map=new HashMap<>();//查看流程历史节点List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().processInstanceId(instanceId).orderByHistoricTaskInstanceStartTime().asc().list();boolean hasUserTask=false;HistoricTaskInstance currentTask=null;for(HistoricTaskInstance task:list) {if(task.getId().equalsIgnoreCase(canBackTaskId)&&task.getAssignee().equalsIgnoreCase(userId)){//找到了处理人处理的任务,查看下一个任务是否已经处理hasUserTask=true;continue;}if(hasUserTask){//上一个任务是当前人处理的,查看当前任务是否已经被处理hasUserTask=false;if(null==task.getEndTime()){canBack=true;currentTask=task;break;}}}if(canBack){System.out.println("未处理的流程可撤回,任务ID:"+currentTask.getId()+",任务接收人:"+currentTask.getAssignee());}map.put("canBack",canBack);map.put("currentTask",currentTask);//方法返回值为:是否可回退、当前任务return map;}

controller

 /*** 撤回* @param instanceId 流程id ACT_HI_PROCINST.PROC_INST_ID_* @param canBackTaskId 想撤回到节点的id  act_hi_taskinst.ID_* @param userId 撤回人*/@GetMapping("/proBack")public void proBack(String instanceId,String canBackTaskId,String userId) {leaveService.proBack(instanceId,canBackTaskId,userId);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/292246.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

骨传导耳机品牌年度排行榜,骨传导耳机十大品牌排行榜分享

随着科技的飞速发展&#xff0c;耳机市场也在不断迭代更新&#xff0c;骨传导耳机作为一种新兴的产品类型&#xff0c;凭借其独特的优势逐渐成为消费者的宠儿。骨传导耳机通过骨骼传递声音&#xff0c;有效降低了传统耳机对耳朵的负担&#xff0c;不仅保护了听力&#xff0c;还…

CloudCanal x Debezium 打造实时数据流动新范式

简述 Debezium 是一个开源的数据订阅工具&#xff0c;主要功能为捕获数据库变更事件发送到 Kafka。 CloudCanal 近期实现了从 Kafka 消费 Debezium 格式数据&#xff0c;将其 同步到 StarRocks、Doris、Elasticsearch、MongoDB、ClickHouse 等 12 种数据库和数仓&#xff0c;…

通过fastlane打包上传到appstore

需要mac设备 一、Fastlane 安装 1、安装 Xcode command line tools&#xff1a; 命令行 xcode-select --install 2、安装Fastlane 命令行 brew install fastlane 3、初始化Fastlane cd到你的需要打包的项目目录&#xff0c;然后执行命令 fastlane init 解释&#xff1a…

华为云开发者日·2023年度创享峰会圆满落幕 | 开发者是底色

“ 共创科技未来&#xff0c;点亮智慧之光。” 文 | 云舒 编辑 | 小白 出品&#xff5c;极新 一场科技盛宴&#xff0c;汇聚了无数智慧与激情。2023年12月20日&#xff0c;华为云开发者日2023年度创享峰会在北京市中关村科学城东升科技园盛大举行。 华为云开发者日是面向全…

plc无线远程模块,实现PLC远程数采

在工业自动化领域&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;的应用日益广泛。然而&#xff0c;传统的PLC数据采集方式通常受限于有线连接&#xff0c;限制了数据的灵活性和可访问性。在这个背景下&#xff0c;PLC无线远程模块成为了解决方案的热点。今天&#x…

飞天使-k8s知识点4-验证安装好后功能

文章目录 接k8s知识点2之验证集群功能创建dashboard验证安装nginx 并访问tomcat 访问 接k8s知识点2之验证集群功能 [rootkubeadm-master2 tmp]# kubectl run net-test1 --imagealpine sleep 36000 pod/net-test1 created [rootkubeadm-master2 tmp]# kubectl get pod NAME …

配置MUX VLAN示例(接入层设备)

一、组网需求 在企业网络中&#xff0c;企业所有员工都可以访问企业的服务器。但对于企业来说&#xff0c;希望企业内部部分员工之间可以互相交流&#xff0c;而部分员工之间是隔离的&#xff0c;不能够互相访问。为了解决上述问题&#xff0c;可在连接终端的交换机上部署MUX …

活动回顾 (上) | 2023 Meet TVM 系列活动完美收官

作者&#xff1a;xixi 编辑&#xff1a;三羊、李宝珠 2023 Meet TVM 年终聚会于 12 月 16 日在上海圆满落幕&#xff0c;本次 meetup 不仅邀请到了 4 位 AI 编译器专家为大家带来了精彩的分享&#xff0c;还新增了圆桌讨论环节&#xff0c;以更多元的视角和各位共同讨论大模型…

mysql统计函数round失效问题

mysql统计函数round失效问题 目录1、问题2、找到原因3、解决办法4、类似问题欢迎品论区补充~ 目录 1、问题 我的mysql版本为&#xff1a;8.1.0 我在mysql使用sum对数据统计后使用round函数进行四舍五入取整&#xff0c;发现像16.145这样的数字取小数后2位后是16.14而非16.15。…

模型评估方法

目录 数据集切分 交叉验证 交叉验证实例 混淆矩阵 实例 代码实现 阈值 全局阈值处理 自适应阈值处理 阈值对结果的影响 ROC曲线 数据集切分 数据集切分是指将一个数据集分割成训练集和测试集的过程。常用的方法是随机切分&#xff0c;即将数据集中的样本按照一定比…

小红书kos和kop有什么区别,营销玩法有哪些

相信熟悉媒介传播的朋友&#xff0c;对于kol和koc都不陌生。但随着平台的发展和市场的进步&#xff0c;又出现了kos和kop。那么小红书kos和kop有什么区别&#xff0c;营销玩法有哪些&#xff1f; 一、什么是kos和kop KOS&#xff0c;全称叫做Key Opinion Sales&#xff0c;意思…

智能变电站集中监控辅助决策系统解决方案

项目背景 智能变电站是坚强智能电网的重要基础和支撑。它不仅是电网运行数据的采集源头和命令执行单元&#xff0c;而且与其他环节的联系非常紧密&#xff0c;为统一坚强智能电网的安全和优质提供了关键支撑。为了更有效地管理和控制变电站的各种设备和子系统&#xff0c;如视…