flowable进阶(单节点循环审批)


前言

什么是循环审批:

  • 例如: 我是一个客服,然后接到了一个订单退款的处理,按道理我看完没问题了,然后就是通过审核,平台返钱就行了, 部分流程图就是 : 订单退款>>>客服审核>>>平台退款;
  • 但是! 我就是一个打工人,所以我一个客服确实觉得没问题呀,但是我担心订单会造成绩效损失,我想让客服经理帮忙看看,然后客服经理看过后,觉得这老板会不会不开心呀,所以又想让老板看看;
  • 但是! 大家知道,这个流程图绘制完了,就不会随时更改的,像这种需求,表面上其实一直在客服审核这个节点,但是却一直有不同的人在审批,在不更改原有流程的情况下,如何实现这个需求呢?

一、现有方案

对于这个需求,现有的flowable方案有如下两种:

  1. 在该节点增加任务监听,通过任务监听动态分配给用户,创建子任务实现
  2. 并行任务会签,这个比较牵强,但是也能实现,就是通过会签达到多个人都审批的需求,
    相对比而言,第一个更符合需求

那么这两个方案的缺点是什么呢?

第一个方案,这里里面一般来说动态监听分配用户,只能在create时候分配一次,或者有其他方式,但一定是有限制的,无法做到无限扩展,例如突然弄个10个人审核,这根本无法实现
第二个方案,那就是不灵活,它无法临时起意,添加审核人,扩展复杂,只能在绘制流程图的时候就将这里考虑好才可以

二、单节点循环审批

此方案确实是工作中需要的,然后这个单节点循环审批的方案也是自行摸索了两天,搞定的,简单说下方案思路

  1. 在要循环审批的任务节点,连线自循环
  2. 将要循环审批的人存储到业务数据库,或者流程变量中
  3. 当前任务节点用${user}的方式分配
  4. 每次审批完毕,获取循环审批的人,设置下一次审批的人
  5. 利用任务监听,每次循环审批结束,判断是否可以结束,结束跳转到下一个节点,未结束继续循环

仍然是利用子任务实现,不过是动态的,废话不多说,直接上代码:

  • 循环审批流程图 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="cycle-approve" name="循环审批" isExecutable="true">
    <documentation>循环审批</documentation>
    <startEvent id="start" name="开始"/>
    <userTask id="shenpi" name="审批" flowable:assignee="${taskUser}">
      <extensionElements>
        <flowable:taskListener event="complete" class="com.example.demo.listen.CycleTaskHandler"/>
      </extensionElements>
    </userTask>
    <endEvent id="end" name="结束"/>
    <sequenceFlow id="sid-A95C71D2-732D-4CDD-8D79-66CD360471DC" sourceRef="start" targetRef="shenpi"/>
    <sequenceFlow id="sid-37809D0B-FC42-4D97-B1AF-CE9C93AACE71" name="循环审批" sourceRef="shenpi" targetRef="shenpi">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${res==true && approvalCount<3}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="pass" name="通过" sourceRef="shenpi" targetRef="end">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${res==false || approvalCount>=3}]]></conditionExpression>
    </sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_cycle-approve">
    <bpmndi:BPMNPlane bpmnElement="cycle-approve" id="BPMNPlane_cycle-approve">
      <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
        <omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="shenpi" id="BPMNShape_shenpi">
        <omgdc:Bounds height="80.0" width="100.0" x="255.0" y="138.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
        <omgdc:Bounds height="28.0" width="28.0" x="645.0" y="164.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sid-37809D0B-FC42-4D97-B1AF-CE9C93AACE71" id="BPMNEdge_sid-37809D0B-FC42-4D97-B1AF-CE9C93AACE71">
        <omgdi:waypoint x="330.0" y="137.99998"/>
        <omgdi:waypoint x="330.0" y="87.0"/>
        <omgdi:waypoint x="305.00003" y="87.0"/>
        <omgdi:waypoint x="305.0" y="138.00002"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-A95C71D2-732D-4CDD-8D79-66CD360471DC" id="BPMNEdge_sid-A95C71D2-732D-4CDD-8D79-66CD360471DC">
        <omgdi:waypoint x="129.94999949366624" y="178.0"/>
        <omgdi:waypoint x="254.99999999993574" y="178.0"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="pass" id="BPMNEdge_pass">
        <omgdi:waypoint x="354.9499999999221" y="178.0"/>
        <omgdi:waypoint x="645.0" y="178.0"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
  • 循环审批图示
    图示

sid-37809D0B-FC42-4D97-B1AF-CE9C93AACE71 这个循环审批分支 ,只要是res为true,那么就会一直循环

  • 具体实现
package com.example.demo.controller;

import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.variable.api.persistence.entity.VariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

/**
 * 多人多次循环审批
 *  https://doc.apipost.net/docs/37e932d3447c000?locale=zh-cn
 */
@Controller
@RequestMapping("/cycle")
public class CycleController {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ProcessEngine processEngine;

    /**
     * 添加报销
     *
     * @param userId    用户Id
     * @param money     报销金额
     * @param descption 描述
     */
    @RequestMapping(value = "add")
    @ResponseBody
    public String addExpense(String userId) {
        //启动流程
        HashMap<String, Object> map = new HashMap<>();
        map.put("taskUser", userId);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("cycle-approve", map);
        return "提交成功.流程Id为:" + processInstance.getId();
    }

    /**
     * 获取审批管理列表
     */
    @RequestMapping(value = "/list")
    @ResponseBody
    public ResponseEntity list(String userId) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
        Task task = tasks.get(0);
        return ResponseEntity.ok(task.getId());
    }


    /**
     * 批准
     *
     * @param taskId 任务ID
     */
    @RequestMapping(value = "apply")
    @ResponseBody
    public String apply(String taskId, boolean res,String userId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        if (task == null) {
            throw new RuntimeException("流程不存在");
        }
        //通过审核
        HashMap<String, Object> map = new HashMap<>();
        map.put("res", res);
        // 每次设置下一循环审批的办理人
        map.put("taskUser", userId);
        taskService.complete(taskId, map);
        return "processed ok!";
    }

    /**
     * 拒绝
     */
    @ResponseBody
    @RequestMapping(value = "reject")
    public String reject(String taskId) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("outcome", "驳回");
        taskService.complete(taskId, map);
        return "reject";
    }

    /**
     * 生成流程图
     *
     * @param processId 任务ID
     */
    @RequestMapping(value = "processDiagram")
    public void genProcessDiagram(HttpServletResponse httpServletResponse, String processId) throws Exception {
        List<ProcessInstance> t = runtimeService.createProcessInstanceQuery().list();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();


        //流程走完的不显示图
        if (pi == null) {
            return;
        }
        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
        String InstanceId = task.getProcessInstanceId();
        List<Execution> executions = runtimeService
                .createExecutionQuery()
                .processInstanceId(InstanceId)
                .list();

        //得到正在执行的Activity的Id
        List<String> activityIds = new ArrayList<>();
        List<String> flows = new ArrayList<>();
        for (Execution exe : executions) {
            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
            activityIds.addAll(ids);
        }

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
//        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 1.0);
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, Collections.emptyList(), engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), null, 1.0, false);
        OutputStream out = null;
        byte[] buf = new byte[1024];
        int legth = 0;
        try {
            out = httpServletResponse.getOutputStream();
            while ((legth = in.read(buf)) != -1) {
                out.write(buf, 0, legth);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }

}

可以参考apipost 的请求: 循环审批请求示例
apply方法中 userId 每次变更为下一个要审批的人

  • 任务监听
@Slf4j
public class CycleTaskHandler implements TaskListener {

    @Override
    public void notify(DelegateTask delegateTask) {
        // 获取当前审批结果
        boolean res = (boolean) delegateTask.getVariable("res");

        // 获取当前审批次数
        if (delegateTask.getVariable("approvalCount") == null) {
            delegateTask.setVariable("approvalCount", 0);
        }
        int approvalCount = (int) delegateTask.getVariable("approvalCount");
        // 更新审批次数
        delegateTask.setVariable("approvalCount", ++approvalCount);

        // 如果审批次数达到3次且每次res都为true,则设置res为false
        if (approvalCount >= 3 && res) {
            delegateTask.setVariable("res", false);
        }
    }
}

这里除了用么个审批人创建任务外,还利用了任务监听来计算执行次数,可以使判断更加灵活
利用流程变量 approvalCount 来控制是否结束,像结束当前节点,直接设置res未false,否则一直为true,一直自循环
这样处理后,理论上可以实现无限自循环审批


总结

基于flowable的单节点循环审批至此已经实现,感兴趣的小伙伴可以自行尝试,对了,此技术点已经申请专利,商用需付费

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寂寞旅行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值