Flowable
Flowable是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义(用于定义流程的行业XML标准), 创建这些流程定义的流程实例,进行查询,访问运行中或历史的流程实例与相关数据。
节点绘图流程:

目前的功能:
- 流程定义设计
- 流程 缺失或待改进的功能:
- 定义流程模型时,只能用${}变量插值符号来引入,这涉及到需要修改后端编码
- 发起事务后没有撤销实例的接口或操作。
flowable 任务多实例
概念部分
- 
会签:指同一个审批节点设置多个人,如ABC三人,三人会同时收到审批,需全部同意之后,审批才可到下一审批节点; 
- 
或签:指同一个审批节点设置多个人,如ABC三人,三人会同时收到审批,只要其中任意一人审批即可到下一审批节点。 
如果一个活动是多实例,将通过在该活动底部的三条短线表示。三条竖线代表实例会并行执行,而三条横线代表顺序执行。

例如上面流程,在设置一级部门初审时,设置全局流程变量为列表,对或签的审核人员进行安排。其中需要注意的是完成条件设置为1,代表只要列表用户中存在一人通过审核即可进行下个流程。会签选择100%即可
代码部分
if (Func.isEmpty(guideScreen.getId())) {
            // 保存guideScreen,写入id,createUser等基本表信息
            save(guideScreen);
            List<String> taskUserList = new ArrayList<>();
            Method method;
            //读取对象中的taskUser1,taskUser2,taskUser3
            for (int i = 1; i <= 3; i++) {
                try {
                    method = guideScreen.getClass().getMethod("getTaskUser" + i);
                    //获取taskUser1,taskUser2,taskUser3
                    if (Func.isNotEmpty(method.invoke(guideScreen))) {
                        taskUserList.add(TaskUtil.getTaskUser(method.invoke(guideScreen).toString()));
                    } else {
                        break;
                    }
                } catch (Exception e) {
                    log.error("获取taskUser{}失败", i);
                    e.printStackTrace();
                }
            }
            // 启动流程
            Kv variables = Kv.create()
                .set(ProcessConstant.TASK_VARIABLE_CREATE_USER, AuthUtil.getUserName())
                .set("taskUserList", taskUserList)
                .set("auditUser", TaskUtil.getTaskUser(Func.toStr(guideScreen.getAuditUser())));
            R<BladeFlow> result = flowClient.startProcessInstanceById(guideScreen.getProcessDefinitionId(), FlowUtil.getBusinessKey(businessTable, String.valueOf(guideScreen.getId())), variables);
            if (result.isSuccess()) {
                log.debug("引导屏审核流程已启动,流程ID:{}", result.getData().getProcessInstanceId());
                // 返回流程id写入guideScreen
                guideScreen.setProcessInstanceId(result.getData().getProcessInstanceId());
                updateById(guideScreen);
            } else {
                System.out.println(result.getMsg());
                throw new ServiceException("开启引导屏审核流程失败");
            }
        }else {
            updateById(guideScreen);
        }
需要注意的代码的功能是从 guideScreen 对象中读取 taskUser1, taskUser2, taskUser3 这三个属性的值,并将这些值处理后添加到 taskUserList 中。具体步骤如下:
- 声明 Method 对象:
Method method;//Method 是 Java 反射(Reflection)API 中的一个类,属于 java.lang.reflect 包。它表示的是类或接口中的某个具体方法的抽象。通过 Method 对象,程序可以在运行时动态地调用某个对象的方法,而不需要在编译时明确地知道这个方法的名称或签名。
这里声明了一个 Method 对象,用于后续动态调用 guideScreen 对象的 getTaskUserX 方法。
- 循环获取 taskUser1,taskUser2,taskUser3:
for (int i = 1; i <= 3; i++) {
这个循环从 i=1 开始,直到 i=3,也就是循环三次,用来依次处理 taskUser1, taskUser2, 和 taskUser3。
- 动态获取方法并调用:
method = guideScreen.getClass().getMethod("getTaskUser" + i);
在循环内部,通过字符串拼接的方式动态生成方法名,比如当 i=1 时,方法名就是 getTaskUser1。接着使用反射机制获取这个方法。
- 调用方法并处理结果:
if (Func.isNotEmpty(method.invoke(guideScreen))) {
    taskUserList.add(TaskUtil.getTaskUser(method.invoke(guideScreen).toString()));
} else {
    break;
}
使用 method.invoke(guideScreen) 调用获取的 getTaskUserX 方法,取得返回值。
- 
如果该返回值不为空(通过 Func.isNotEmpty()判断),则调用TaskUtil.getTaskUser()进行处理,并将结果添加到taskUserList中。
- 
如果该返回值为空,则跳出循环,不再处理后续的 taskUserX属性。
- 异常处理:
} catch (Exception e) {
    log.error("获取taskUser{}失败", i);
    e.printStackTrace();
}
如果在获取方法或调用方法的过程中出现任何异常,代码会捕获并记录错误日志,日志信息中会标明是哪个 taskUser 读取失败,同时打印异常堆栈跟踪信息。
设置流程变量的四种方式
流程设计分为三种:
- 
全局流程变量:在整个流程执行期间,这个流程变量都是有效的。 
- 
本地流程变量:这个只针对流程中某一个具体的 Task(任务)有效,这个任务执行完毕后,这个流程变量就失效了。 
- 
临时流程变量:顾名思义就是临时的,这个不会存入到数据库中。 
全局流程变量设计
启动前设置
Kv variables = Kv.create()
                .set(ProcessConstant.TASK_VARIABLE_CREATE_USER, AuthUtil.getUserName())
                .set("taskUserList", taskUserList)
                .set("auditUser", TaskUtil.getTaskUser(User.toString));
R<BladeFlow> result = flowClient.startProcessInstanceById(Id,variables);
当变量是对象时候记得传能够序列化,即实现了 Serializable 接口,具体变量存储表位置在ACT_HI_VARINST 是存储流程执行的历史信息的,ACT_RU_VARIABLE 则是保存流程运行时候的信息的。
相关查询查阅:
Flowable 设置流程变量的四种方式详解_flowable 流程变量-CSDN博客
通过 Task 设置(启动成功后设置)
taskService.setVariables(task.getId(),variables);//读取Task用set方法即可设置
完成任务时设置
Map<String, Object> variables = flow.getVariables();
if (variables == null) {
    variables = Kv.create();
}
variables.put(ProcessConstant.PASS_KEY, flow.isPass());
// 完成任务
taskService.complete(taskId, variables);//使用complete函数
通过RuntimeService设置
Execution execution = runtimeService.createExecutionQuery().singleResult();
runtimeService.setVariable(execution.getId(), "days", 10);
Map<String, Object> variables = new HashMap<>();
variables.put("reason", "休息一下");
variables.put("startTime", new Date());
runtimeService.setVariables(execution.getId(), variables);
本地流程变量
本地流程也就是每个Task的专属变量,通过这个Task会释放这个变量
Task任务 点监听
任务监听器类型
- 
create:在任务创建且所有任务属性设置完成之后才触发。 
- 
assignment:在任务被分配给某个班里人之后触发,它是在create事件触发前被触发。 
- 
complete:在配置了监听器的任务完成时触发,也就是说运行期任务删除之前触发。 
- 
delete:任务删除触发 
实例删除思路
public boolean deleteProcessGuideScreen(String processInstanceId) {
        // 获取业务表
        String businessTable = FlowUtil.getBusinessTable(ProcessConstant.GUIDE_SCREEN_KEY);
        if (Func.isNotEmpty(businessTable)) {
            // 删除业务数据,并检查是否成功
            int rowsAffected = baseMapper.deleteByProcessInstanceId(processInstanceId);
            if (rowsAffected <= 0) {
                log.error("删除业务数据失败,流程实例ID: {}", processInstanceId);
                return false;
                // 删除业务数据失败,返回false
            }
            // 删除流程数据
            R result = flowClient.deleteProcessInstance(processInstanceId, "删除流程实例");
            if (result.isSuccess()) {
                log.info("删除流程实例成功,流程实例ID: {}", processInstanceId);
                return true;
                // 成功删除流程实例,返回true
            } else {
                log.error("删除流程实例失败,流程实例ID: {}", processInstanceId);
                return false;
                // 删除流程实例失败,返回false
            }
        }
        // 如果业务表为空,直接返回true
        return true;
    }