Activiti工作流会签三 撤销,审批,驳回

撤销操作

在发请流程之后经常会遇到,内容有误,申请撤销的操作。下面就聊一聊撤销操作。

我们定义一个接口,核心代码只有一行,就是 runtimeService.deleteProcessInstance(procInstId, "canceled-"+reason); 第一个参数是流程实例id 5001 这个ID是在启动实例的时候的工作流生成的,这里要区分的一点就是,这里的ID和流程定义ID不一定。流程ID是由三部分组成key:version:random number 忘记的可以看前面的文章。 第二个参数是撤销理由。 是的! 工作流引擎就是这么人性画, 撤销的时候居然还给一个参数用来设置理由 !!!! 真是牛逼!

其他的代码就是本项目中业务代码了,纯属存起来为了查询显示方便而已。

  @Autowired
    private RuntimeService runtimeService;

 @RequestMapping(value = "/cancel", method = RequestMethod.POST)
    @ApiOperation(value = "撤回申请")
    public Result<Object> cancel(@RequestParam String id,
                                 @RequestParam String procInstId,
                                 @RequestParam(required = false) String reason){

        if(StrUtil.isBlank(reason)){
            reason = "";
        }
        runtimeService.deleteProcessInstance(procInstId, "canceled-"+reason);
        ActBusiness actBusiness = actBusinessService.getById(id);
        actBusiness.setStatus(ActivitiConstant.STATUS_CANCELED);
        actBusiness.setResult(ActivitiConstant.RESULT_TO_SUBMIT);
        actBusinessService.updateById(actBusiness);
        return ResultUtil.success("操作成功");
    }

撤销之后仍然可以在次发起流程,方法和第一次一样。

审批通过

审批通过总共要分成两部分,第一步分查询当前节点的一个节点。第二步,审批通过。

先来看看查询下一个任务

完成这个任务我们需要两个查询条件第一个条件就是当前节点的ID( sid-5C4F7308-E8EC-476E-84D8-63D17C2B08D5,请注意这里和当前流程的当前任务ID不是同一个。),第二个查询条件就是当前节点所在的流程定义的IDleave:1:7) ,请注意这些ID的区分。

业务流程如下:

  1. 第一步我们通过流程定义的ID获取流程定义实例。在流程定义实例中保存了所有的流程节点。
  2. 我们遍历所有的节点并和当前任务节点进行比较。
  3. 我们获取到下一个节点。
  4. 判断节点的类型,拼装返回结果。
@RequestMapping(value = "/getNextNode/{procDefId}/{currActId}", method = RequestMethod.GET)
    @ApiOperation(value = "通过当前节点定义id获取下一个节点")
    public Result<ProcessNodeVo> getNextNode(@ApiParam("当前节点定义id") @PathVariable String procDefId,
                                             @ApiParam("当前节点定义id") @PathVariable String currActId){

        ProcessNodeVo node = new ProcessNodeVo();

        // 当前执行节点id
        ProcessDefinitionEntity dfe = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(procDefId);
        // 获取所有节点
        List<ActivityImpl> activitiList = dfe.getActivities();
        // 判断出当前流程所处节点,根据路径获得下一个节点实例
        for(ActivityImpl activityImpl : activitiList){
            if (activityImpl.getId().equals(currActId)) {
                // 获取下一个节点
                List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();

                PvmActivity pvmActivity = pvmTransitions.get(0).getDestination();

                String type = pvmActivity.getProperty("type").toString();
                if("userTask".equals(type)){
                    // 用户任务节点
                    Boolean customUser = actNodeService.hasCustomUser(pvmActivity.getId());
                    if(customUser){
                        // 是否为自选用户节点
                        node.setType(ActivitiConstant.NODE_TYPE_CUSTOM);
                    }else {
                        node.setType(ActivitiConstant.NODE_TYPE_TASK);
                        node.setTitle(pvmActivity.getProperty("name").toString());
                        // 设置关联用户
                        List<User> users = getNodetUsers(pvmActivity.getId());
                        node.setUsers(removeDuplicate(users));
                    }
                }else if("exclusiveGateway".equals(type)){
                    // 排他网关
                    node.setType(ActivitiConstant.NODE_TYPE_EG);
                }else if("parallelGateway".equals(type)){
                    // 平行网关
                    node.setType(ActivitiConstant.NODE_TYPE_PG);
                }else if("endEvent".equals(type)){
                    // 结束
                    node.setType(ActivitiConstant.NODE_TYPE_END);
                }else{
                    throw new TbootException("流程设计错误,包含无法处理的节点");
                }
                break;
            }
        }

        return new ResultUtil<ProcessNodeVo>().setData(node);
    }

开始审批

我们一起来看看下面几个核心的API

  1. 添加审批意见 taskService.addComment(id, procInstId, comment); 第一个是传的当前任务的ID(例如:5032) 第二个参数是当前流程实例的ID (例如:5021),第三个参数是审批意见(例如:同意)
  2. 查询出当前任务,然后通过Task task = taskService.createTaskQuery().taskId(id).singleResult(); taskService.complete(id);
  3. 查询出所有的任务节点List tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
  4. 完成当前任务,可以看看看下方的两个图,请注意当前流程实现 ID,和当前任务ID。审批通过到这一步就已经完成了,下面还会有一些处理只是对任务的一些特殊情况进行处理。
  5. 记录实际审批人员iHistoryIdentityService.insert(SnowFlakeUtil.nextId().toString(), ActivitiConstant.EXECUTOR_TYPE, securityUtil.getCurrUser().getId(), id, procInstId); 这个接口也是业务中自己写的接口不过是操作的工作流的表。ACT_HI_IDENTITYLINK 大家可以看看这个表的信息
    在这里插入图片描述
    在这里插入图片描述

    @Autowired
    private TaskService taskService;

@RequestMapping(value = "/pass", method = RequestMethod.POST)
    @ApiOperation(value = "任务节点审批通过")
    public Result<Object> pass(@ApiParam("任务id") @RequestParam String id,
                               @ApiParam("流程实例id") @RequestParam String procInstId,
                               @ApiParam("下个节点审批人") @RequestParam(required = false) String[] assignees,
                               @ApiParam("优先级") @RequestParam(required = false) Integer priority,
                               @ApiParam("意见评论") @RequestParam(required = false) String comment,
                               @ApiParam("是否发送站内消息") @RequestParam(defaultValue = "false") Boolean sendMessage,
                               @ApiParam("是否发送短信通知") @RequestParam(defaultValue = "false") Boolean sendSms,
                               @ApiParam("是否发送邮件通知") @RequestParam(defaultValue = "false") Boolean sendEmail){

        if(StrUtil.isBlank(comment)){
            comment = "";
        }
        taskService.addComment(id, procInstId, comment);
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        Task task = taskService.createTaskQuery().taskId(id).singleResult();
        taskService.complete(id);
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
        // 判断下一个节点
        if(tasks!=null&&tasks.size()>0){
            for(Task t : tasks){
                if(assignees==null||assignees.length<1){
                    // 如果下个节点未分配审批人为空 取消结束流程
                    List<User> users = actProcessService.getNode(t.getTaskDefinitionKey()).getUsers();
                    if(users==null||users.size()==0){
                        runtimeService.deleteProcessInstance(procInstId, "canceled-审批节点未分配审批人,流程自动中断取消");
                        ActBusiness actBusiness = actBusinessService.getById(pi.getBusinessKey());
                        actBusiness.setStatus(ActivitiConstant.STATUS_CANCELED);
                        actBusiness.setResult(ActivitiConstant.RESULT_TO_SUBMIT);
                        actBusinessService.updateById(actBusiness);
                        break;
                    }else{
                        // 避免重复添加
                        List<String> list = iRunIdentityService.selectByConditions(t.getId(), "candidate");
                        if(list==null||list.size()==0) {
                            // 分配了节点负责人分发给全部
                            for (User user : users) {
                                taskService.addCandidateUser(t.getId(), user.getId());
                                // 异步发消息
                                messageUtil.sendActMessage(user.getId(), ActivitiConstant.MESSAGE_TODO_CONTENT, sendMessage, sendSms, sendEmail);
                            }
                            taskService.setPriority(t.getId(), task.getPriority());
                        }
                    }
                }else{
                    // 避免重复添加
                    List<String> list = iRunIdentityService.selectByConditions(t.getId(), "candidate");
                    if(list==null||list.size()==0) {
                        for(String assignee : assignees){
                            taskService.addCandidateUser(t.getId(), assignee);
                            // 异步发消息
                            messageUtil.sendActMessage(assignee, ActivitiConstant.MESSAGE_TODO_CONTENT, sendMessage, sendSms, sendEmail);
                            taskService.setPriority(t.getId(), priority);
                        }
                    }
                }
            }
        } else {
            ActBusiness actBusiness = actBusinessService.getById(pi.getBusinessKey());
            actBusiness.setStatus(ActivitiConstant.STATUS_FINISH);
            actBusiness.setResult(ActivitiConstant.RESULT_PASS);
            actBusinessService.updateById(actBusiness);
            // 异步发消息
            messageUtil.sendActMessage(actBusiness.getUserId(), ActivitiConstant.MESSAGE_PASS_CONTENT, sendMessage, sendSms, sendEmail);
        }
        // 记录实际审批人员
        iHistoryIdentityService.insert(SnowFlakeUtil.nextId().toString(),
                ActivitiConstant.EXECUTOR_TYPE, securityUtil.getCurrUser().getId(), id, procInstId);
        return ResultUtil.success("操作成功");
    }

驳回

我们这里还是要操作两步,首先获取可以返回的节点,获取到节点信息之后,然后在执行驳回操作。

获取可以返回的任务节点

这里最核心的API是 historyService查询历史任务节点中结束的任务节点。然后将结点拼装成业务中自己定义的bean对象返回。 返回结果如下图。 id 为任务ID, key为流程定义中的节点ID。 name为任务名称。
在这里插入图片描述
这是一个数组,因为我们只有一个人审批过了,所以这里只有一条记录。 然后我们在真正驳回的时候选择是驳回到发起人,还是哪一个已经审批的人那里。 Activiti工作流会签三 撤销,审批,驳回_第1张图片


 @Autowired
    private HistoryService historyService;

@RequestMapping(value = "/getBackList/{procInstId}", method = RequestMethod.GET)
    @ApiOperation(value = "获取可返回的节点")
    public Result<Object> getBackList(@PathVariable String procInstId){

        List<HistoricTaskVo> list = new ArrayList<>();
        List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(procInstId)
                .finished().list();

        taskInstanceList.forEach(e -> {
            HistoricTaskVo htv = new HistoricTaskVo(e);
            list.add(htv);
        });

        // 去重
        LinkedHashSet<String> set = new LinkedHashSet<String>(list.size());
        List<HistoricTaskVo> newList = new ArrayList<>();
        list.forEach(e->{
            if(set.add(e.getName())){
                newList.add(e);
            }
        });

        return ResultUtil.data(newList);
    }

开始驳回任务

我们通过上面一步获取到节点信息之后就可以开始真证的驳回了。 我们还是来分析一下核心代码逻辑。

  1. 给当前任务设置驳回的理由 ·taskService.addComment(id, procInstId, comment);
  2. 取得想要的历史任务,然后跳转过去。
    @Autowired
    private ManagementService managementService;


         //取得流程定义
        ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(procDefId);
        // 获取历史任务的Activity
        ActivityImpl hisActivity = definition.findActivity(backTaskKey);
        // 实现跳转
        managementService.executeCommand(new JumpTask(procInstId, hisActivity.getId()));

这是请求参数大家可以自行对接一下。
Activiti工作流会签三 撤销,审批,驳回_第2张图片

  1. 给任务节点分配人员
  // 重新分配原节点审批人
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
        if(tasks!=null&&tasks.size()>0){
            tasks.forEach(e->{
                for(String assignee:assignees){
                    taskService.addCandidateUser(e.getId(), assignee);
                    // 异步发消息
                    messageUtil.sendActMessage(assignee, ActivitiConstant.MESSAGE_TODO_CONTENT, sendMessage, sendSms, sendEmail);
                }
                if(priority!=null){
                    taskService.setPriority(e.getId(), priority);
                }
            });
        }
  1. 对操作保存记录。
 // 记录实际审批人员
        iHistoryIdentityService.insert(SnowFlakeUtil.nextId().toString(),
                ActivitiConstant.EXECUTOR_TYPE, securityUtil.getCurrUser().getId(), id, procInstId);

到此驳回就已经完成。

 @RequestMapping(value = "/backToTask", method = RequestMethod.POST)
    @ApiOperation(value = "任务节点审批驳回至指定历史节点")
    public Result<Object> backToTask(@ApiParam("任务id") @RequestParam String id,
                                     @ApiParam("驳回指定节点key") @RequestParam String backTaskKey,
                                     @ApiParam("流程实例id") @RequestParam String procInstId,
                                     @ApiParam("流程定义id") @RequestParam String procDefId,
                                     @ApiParam("原节点审批人") @RequestParam(required = false) String[] assignees,
                                     @ApiParam("优先级") @RequestParam(required = false) Integer priority,
                                     @ApiParam("意见评论") @RequestParam(required = false) String comment,
                                     @ApiParam("是否发送站内消息") @RequestParam(defaultValue = "false") Boolean sendMessage,
                                     @ApiParam("是否发送短信通知") @RequestParam(defaultValue = "false") Boolean sendSms,
                                     @ApiParam("是否发送邮件通知") @RequestParam(defaultValue = "false") Boolean sendEmail){


        if(StrUtil.isBlank(comment)){
            comment = "";
        }
        taskService.addComment(id, procInstId, comment);
        // 取得流程定义
        ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(procDefId);
        // 获取历史任务的Activity
        ActivityImpl hisActivity = definition.findActivity(backTaskKey);
        // 实现跳转
        managementService.executeCommand(new JumpTask(procInstId, hisActivity.getId()));
        // 重新分配原节点审批人
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
        if(tasks!=null&&tasks.size()>0){
            tasks.forEach(e->{
                for(String assignee:assignees){
                    taskService.addCandidateUser(e.getId(), assignee);
                    // 异步发消息
                    messageUtil.sendActMessage(assignee, ActivitiConstant.MESSAGE_TODO_CONTENT, sendMessage, sendSms, sendEmail);
                }
                if(priority!=null){
                    taskService.setPriority(e.getId(), priority);
                }
            });
        }
        // 记录实际审批人员
        iHistoryIdentityService.insert(SnowFlakeUtil.nextId().toString(),
                ActivitiConstant.EXECUTOR_TYPE, securityUtil.getCurrUser().getId(), id, procInstId);
        return ResultUtil.success("操作成功");
    }

总结

工作流的会签的流程我们就介绍完了, 下面对定点进行总结。

  1. 要分清工作流的各个阶段的概念和对应的ID
    模型(key 为定义如:leave)----->BpmnModel (包含工作流程定义xml和图片等内容,里面没有ID。获取的时候可以通过Model的key查询)—> 流程定义部署模型(是通过BpmnModel部署的,id为key:version:RandomNumber三部分组成) -----> 流程节点ID(id为字符串加数字) ---->流程任务(ID为数字) 。 这一部分比较重要!!!!!!
  2. 节点分为网关和任务,需要进行判断,然后在操作。
  3. 要并行网关的时候,直接查询就可以获取到需要处理的并行任务。
  4. 审批,驳回,撤销,都是工作流的API不需要特殊处理。 注意点是,我们自己的业务表中的数据要更新。

交个朋友

Activiti工作流会签三 撤销,审批,驳回_第3张图片

你可能感兴趣的:(工作流,activiti,会签,驳回,审批,撤销)