不同版本使用方式不一样,案例使用两个版本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);}