当前位置:   article > 正文

ruoyi-flowable(z)

ruoyi-flowable

一.基础知识

flowable基础知识

二.上手ruoyi-flowable

1.设置申请人

流程表达式菜单
流程表达式菜单
把流程发起人表达式放入申请人 活动节点。
在这里插入图片描述

此时设计流程保存后,用户发起流程,点击提交表单,这时后台会先去 获取下一活动节点,如果下一活动节点有流程变量 就弹框 让用户选择变量指代的人员 并且写提交意见;如果没有就写提交意见,发起流程。

由于申请人节点用了 流程变量,所以会弹框让用户选择变量指代的人员,但是这样就有些奇怪。所以需要修改代码:
1.获取下一节点接口:遇到${INITIATOR} 就跳过
2.发起流程接口:给 ${INITIATOR} 设置指代对象,自动帮发起人完成审批流程
这样就可以直接 流转到 申请人的下一活动节点。

修改代码

1.获取下一节点接口:遇到${INITIATOR} 就跳过

@Override
    public AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(flowTaskVo.getDeploymentId()).singleResult();
        // Step 1. 获取当前节点并找到下一步节点
        FlowNextDto flowNextDto = new FlowNextDto();
        // Step 2. 获取当前流程所有流程变量(网关节点时需要校验表达式)
        List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasksByStart(repositoryService, processDefinition, flowTaskVo.getVariables());
        if (CollectionUtils.isNotEmpty(nextUserTask)) {
            for (UserTask userTask : nextUserTask) {
                MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics();
                // 会签节点
                if (Objects.nonNull(multiInstance)) {
                    flowNextDto.setVars(multiInstance.getInputDataItem());
                    flowNextDto.setType(ProcessConstants.PROCESS_MULTI_INSTANCE);
                    flowNextDto.setDataType(ProcessConstants.DYNAMIC);
                } else {
                    //如果遇到发起人流程变量 则跳过:启动时会自动添加
                    if (("${" + ProcessConstants.PROCESS_INITIATOR + "}").equals(userTask.getAssignee())) {
                        continue;
                    }
                    // 读取自定义节点属性 判断是否是否需要动态指定任务接收人员、组
                    String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
                    String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE);
                    flowNextDto.setVars(ProcessConstants.PROCESS_APPROVAL);
                    flowNextDto.setType(userType);
                    flowNextDto.setDataType(dataType);
                }
            }
        }
        return AjaxResult.success(flowNextDto);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

2.发起流程接口:给 ${INITIATOR} 设置指代对象,自动帮发起人完成审批流程

/**
     * 根据流程定义ID启动流程实例
     *
     * @param procDefId 流程模板ID
     * @param variables 流程变量
     * @return
     */
    @Override
    public AjaxResult startProcessInstanceById(String procDefId, Map<String, Object> variables) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId)
                    .latestVersion().singleResult();
            if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) {
                return AjaxResult.error("流程已被挂起,请先激活流程");
            }
            // 设置流程发起人Id到流程中
            SysUser sysUser = SecurityUtils.getLoginUser().getUser();
            identityService.setAuthenticatedUserId(sysUser.getUserId().toString());
            variables.put(ProcessConstants.PROCESS_INITIATOR, sysUser.getUserId());
            ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDefId, variables);
            // 如果第一活动节点为当前用户,则默认当前用户==申请人,则自动审批:给第一步申请人节点设置任务执行人和意见
            Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).taskAssignee(sysUser.getUserId().toString()).singleResult();
            if (Objects.nonNull(task)) {
                taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请");
//                taskService.setAssignee(task.getId(), sysUser.getUserId().toString());
                taskService.complete(task.getId(), variables);
            }
            return AjaxResult.success("流程启动成功");
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("流程启动错误");
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2.驳回

1.通过驳回按钮驳回,这种驳回 只能一级一级驳;(如上图)
2.通过设计流程图 排他网关+判断表单字段变量: 可以越级驳回(不需要驳回按钮)
3. 在申请人活动节点添加表单,这样驳回到申请人时 可以重新写表单。

示例:
2.通过设计流程图 排他网关+判断表单字段变量: 可以越级驳回(不许要驳回按钮)

排他网关:驳回:$ {sp == 1} ; 通过:$ {sp == 2};
sp字段在 图中 审批表单里面

在这里插入图片描述

修改代码

flowable/task/todo/detail/index.vue

第二种设计流程 用户表单的选项会影响 下一节点是哪个,但是原代码并未把新的表单选项 传给后端,后端还是用的 原先的值。

  /** 申请流程表单数据提交 */
    submitForm(formData) {
      // 根据当前任务或者流程设计配置的下一步节点 todo 暂时未涉及到考虑网关、表达式和多节点情况
      var params = {};
      //该节点 没有新增表单
      if (formData == null){
        params = {taskId: this.taskForm.taskId};
      } else {//todo 该节点有新增表单:考虑表单内有影响 选择 下一活动节点 的表达式值,所有一起传过去
        params = {...this.taskForm};
        params.formData = formData;
        // 表单是否禁用
        params.formData.formData.disabled = true;
        // 是否显示按钮
        params.formData.formData.formBtns = false;
        params.variables = Object.assign({}, params.variables, params.formData.valData);
        params.variables.variables = params.formData.formData;
      }
      getNextFlowNode(params).then(res => {
        const data = res.data;
        this.taskForm.formData = formData;
        if (data) {
          if (data.dataType === 'dynamic') {
            if (data.type === 'assignee') { // 指定人员
              this.checkSendUser = true;
              this.checkType = "single";
            } else if (data.type === 'candidateUsers') {  // 候选人员(多个)
              this.checkSendUser = true;
              this.checkType = "multiple";
            } else if (data.type === 'candidateGroups') { // 指定组(所属角色接收任务)
              this.checkSendRole = true;
            } else { // 会签
              // 流程设计指定的 elementVariable 作为会签人员列表
              this.multiInstanceVars = data.vars;
              this.checkSendUser = true;
              this.checkType = "multiple";
            }
          }
        }
        this.completeOpen = true;
        this.completeTitle = "流程审批";
      })
    },
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

todo下为新增代码
把新表单 流程变量和之前流程变量 合并

 /**
     * 获取下一节点
     *
     * @param flowTaskVo 任务
     * @return
     */
    @Override
    public AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo) {
        // Step 1. 获取当前节点并找到下一步节点
        Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult();
        FlowNextDto flowNextDto = new FlowNextDto();
        if (Objects.nonNull(task)) {
            // Step 2. 获取当前流程所有流程变量(网关节点时需要校验表达式)
            Map<String, Object> variables = taskService.getVariables(task.getId());
            //todo 如果当前节点有新增表单,考虑新增表单内有表达式值会影响 选择 下一活动节点,所有把流程变量合并
            if (flowTaskVo.getVariables() != null) {
                flowTaskVo.getVariables().forEach((key, value) -> variables.merge(key, value, (v1, v2) -> v2));
            }
            List<UserTask> nextUserTask = FindNextNodeUtil.getNextUserTasks(repositoryService, task, variables);
            if (CollectionUtils.isNotEmpty(nextUserTask)) {
                for (UserTask userTask : nextUserTask) {
                    MultiInstanceLoopCharacteristics multiInstance = userTask.getLoopCharacteristics();
                    // 会签节点
                    if (Objects.nonNull(multiInstance)) {
                        flowNextDto.setVars(multiInstance.getInputDataItem());
                        flowNextDto.setType(ProcessConstants.PROCESS_MULTI_INSTANCE);
                        flowNextDto.setDataType(ProcessConstants.DYNAMIC);
                    } else {
                        // 读取自定义节点属性 判断是否是否需要动态指定任务接收人员、组
                        String dataType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_DATA_TYPE);
                        String userType = userTask.getAttributeValue(ProcessConstants.NAMASPASE, ProcessConstants.PROCESS_CUSTOM_USER_TYPE);
                        flowNextDto.setVars(ProcessConstants.PROCESS_APPROVAL);
                        flowNextDto.setType(userType);
                        flowNextDto.setDataType(dataType);
                    }
                }
            } else {
                return AjaxResult.success("流程已完结", null);
            }
        }
        return AjaxResult.success(flowNextDto);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

如果遇到发起人流程变量 则跳过:因为该值固定 不需要用户重新赋值

 /**
     * 查找下一节点
     *
     * @param flowElements
     * @param flowElement
     * @param map
     * @param nextUser
     */
    public static void next(Collection<FlowElement> flowElements, FlowElement flowElement, Map<String, Object> map, List<UserTask> nextUser) {
        //如果是结束节点
        if (flowElement instanceof EndEvent) {
            //如果是子任务的结束节点
            if (getSubProcess(flowElements, flowElement) != null) {
                flowElement = getSubProcess(flowElements, flowElement);
            }
        }
        //获取Task的出线信息--可以拥有多个
        List<SequenceFlow> outGoingFlows = null;
        if (flowElement instanceof Task) {
            outGoingFlows = ((Task) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof Gateway) {
            outGoingFlows = ((Gateway) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof StartEvent) {
            outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof SubProcess) {
            outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows();
        } else if (flowElement instanceof CallActivity) {
            outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows();
        }
        if (outGoingFlows != null && outGoingFlows.size() > 0) {
            //遍历所有的出线--找到可以正确执行的那一条
            for (SequenceFlow sequenceFlow : outGoingFlows) {
                //1.有表达式,且为true
                //2.无表达式
                String expression = sequenceFlow.getConditionExpression();
                if (expression == null ||
                        expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) {
                    //出线的下一节点
                    String nextFlowElementID = sequenceFlow.getTargetRef();
                    if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) {
                        continue;
                    }

                    //查询下一节点的信息
                    FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements);
                    //调用流程
                    if (nextFlowElement instanceof CallActivity) {
                        CallActivity ca = (CallActivity) nextFlowElement;
                        if (ca.getLoopCharacteristics() != null) {
                            UserTask userTask = new UserTask();
                            userTask.setId(ca.getId());

                            userTask.setId(ca.getId());
                            userTask.setLoopCharacteristics(ca.getLoopCharacteristics());
                            userTask.setName(ca.getName());
                            nextUser.add(userTask);
                        }
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //用户任务
                    if (nextFlowElement instanceof UserTask) {
                        //如果遇到发起人流程变量 则跳过:因为该值固定 不需要用户重新赋值
                        if (!("${" + ProcessConstants.PROCESS_INITIATOR + "}").equals(((UserTask) nextFlowElement).getAssignee())) {
                            nextUser.add((UserTask) nextFlowElement);
                        }
                    }
                    //排他网关
                    else if (nextFlowElement instanceof ExclusiveGateway) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //并行网关
                    else if (nextFlowElement instanceof ParallelGateway) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //接收任务
                    else if (nextFlowElement instanceof ReceiveTask) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //服务任务
                    else if (nextFlowElement instanceof ServiceTask) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //子任务的起点
                    else if (nextFlowElement instanceof StartEvent) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                    //结束节点
                    else if (nextFlowElement instanceof EndEvent) {
                        next(flowElements, nextFlowElement, map, nextUser);
                    }
                }
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

3.动态流程变量

1.方法表达式

在该系统测试,会报异常,无法识别 表达式,不知道哪有问题

2.任务监听器
@PostConstruct

/**
 * 任务监听器
 * <p>
 * create(创建):在任务被创建且所有的任务属性设置完成后才触发
 * assignment(指派):在任务被分配给某个办理人之后触发
 * complete(完成):在配置了监听器的上一个任务完成时触发
 * delete(删除):在任务即将被删除前触发。请注意任务由completeTask正常完成时也会触发
 *
 * @author Tony
 * @date 2021/4/20
 */
@Slf4j
@Component
public class FlowTaskListener implements TaskListener {

    @Resource
    protected TaskService taskService;

    @Autowired
    private ISysRelationService relationService;

    @Autowired
    private SysRelationMapper relationMapper;

    @Resource
    private ISysUserService sysUserService;

    private static FlowTaskListener flowTaskListener;

    //!!!!必加 不然 @Autowired和@Resource 自动注入 都为null 
    @PostConstruct //通过@PostConstruct实现初始化bean之前进行的操作
    public void init() {
        flowTaskListener = this;
        flowTaskListener.taskService= this.taskService;
        flowTaskListener.relationService= this.relationService;
        flowTaskListener.relationMapper= this.relationMapper;
        flowTaskListener.sysUserService= this.sysUserService;
        // 初使化时将已静态化的testService实例化
    }

    @SneakyThrows
    @Override
    public void notify(DelegateTask delegateTask) {

        log.info("任务监听器:{}", delegateTask);
        // TODO  获取事件类型 delegateTask.getEventName(),可以通过监听器给任务执行人发送相应的通知消息
        
        //获取当前任务节点
        Task task = flowTaskListener.taskService.createTaskQuery().processInstanceId(delegateTask.getProcessInstanceId()).singleResult();
        //查询任务节点分配人信息
        SysUser sysUser = flowTaskListener.sysUserService.selectUserById(Long.parseLong(task.getAssignee()));
        //获取父部门下最高职位员工
        SysUserDeptPostVo param = new SysUserDeptPostVo();
        param.setDeptId(sysUser.getDept().getParentId());
        SysUserDeptPostVo sysUserDeptPostVo = flowTaskListener.relationService.getTopFromUsers(flowTaskListener.relationMapper.getUsersByDept(param));
        if (sysUserDeptPostVo != null) {
            //!!! 这么写没有历史记录
//            delegateTask.setAssignee(sysUserDeptPostVo.getUserId().toString());
            //!!!有历史记录
            flowTaskListener.taskService.setAssignee(delegateTask.getId(), sysUserDeptPostVo.getUserId().toString());
        } else {
            throw new Exception("获取上级人员失败,系统异常");
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

4.监听器

监听器

eg:${flowTaskListener} 第一个字母小写
1.类:com.ruoyi.flowable.listener.MytaskListener1
2.表达式: ${MytaskListener2.test1()}
3.委托表达式: ${MytaskListener3}

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/852345
推荐阅读
相关标签
  

闽ICP备14008679号