Activiti 撤回到已审批的任意节点

Activiti6.0 撤回到已审批的任意节点

Activiti6.0 多实例并发子流程和汇签
Activiti6.0 生成流程图和已审批、待审批节点高亮
Activiti6 集成代码前后端(含代码)

由于客户需求,添加撤回到已审批的任意节点功能,所以这边记录一下.

效果如下图:
在这里插入图片描述

流程图
在这里插入图片描述
在这里插入图片描述

代码如下:
pom

  <properties>     
        <activiti.version>6.0.0</activiti.version>
    </properties>

 	<dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring</artifactId>
            <version>${activiti.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.mybatis</groupId>
                    <artifactId>mybatis</artifactId>
                </exclusion>
            </exclusions>

        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>${activiti.version}</version>
        </dependency>

getRunNodes 根据任务id查询已经执行的任务节点信息

   /**
     * 根据任务id查询已经执行的任务节点信息
     */
@Override
    public DynamicFormConf getRunNodes(tring taskId) {
            DynamicFormConf dynamicFormConf = new DynamicFormConf();
            // 获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序
            List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery()
                            .processInstanceId(taskId)
                            .activityType("userTask")   //用户任务
                            .finished()       //已经执行的任务节点
                            .orderByHistoricActivityInstanceEndTime()
                            .asc()
                            .list();

            // 已执行的节点ID集合
         if(StringUtils.isNotEmpty(historicActivityInstanceList)){
             Map<String,String> map= new LinkedHashMap<String,String>();
            // map = historicActivityInstanceList.stream().collect(Collectors.toMap(HistoricActivityInstance::getActivityId,HistoricActivityInstance::getActivityName,(k1,k2)->k1));
           for (HistoricActivityInstance historicActivityInstance:historicActivityInstanceList){
               if(!map.containsKey(historicActivityInstance.getActivityId())){
                   map.put(historicActivityInstance.getActivityId(),historicActivityInstance.getActivityName());
               }
           }
             dynamicFormConf.setRunNodes(map);
         }
        return dynamicFormConf;
    }

runNodes 驳回到指定节点

/**
     * 驳回到指定节点
     * @param approvalOpinionVO    //申请流程 审批信息
     * @param task  //任务信息
     * @param map
     * @return
     */
    @Override
    public boolean runNodes(ApprovalOpinionVO approvalOpinionVO, Task task, Map<String, Object> map) {
        String myTaskId = null;
        //判断当前用户是否为该节点处理人
        if (UserUtils.getUserId().equals(task.getAssignee())) {
            myTaskId = task.getId();
        }
        //如果当前节点处理人不是该用户,就无法进行驳回操作
        if (null == myTaskId) {
            throw new CustomException("当前用户无法驳回");
        }
        //获取当前节点
        String currActivityId = task.getTaskDefinitionKey();
        String processDefinitionId = task.getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        FlowNode currFlow = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currActivityId);

        if (null == currFlow) {
            List<SubProcess> subProcessList = bpmnModel.getMainProcess().findFlowElementsOfType(SubProcess.class, true);
            for (SubProcess subProcess : subProcessList) {
                FlowElement flowElement = subProcess.getFlowElement(currActivityId);
                if (flowElement != null) {
                    currFlow = (FlowNode) flowElement;
                    break;
                }
            }
        }
		//获取目标节点
        FlowNode targetFlow = (FlowNode) bpmnModel.getFlowElement(approvalOpinionVO.getRunNodeId());

        //如果不是同一个流程(子流程)不能驳回
        if (!(currFlow.getParentContainer().equals(targetFlow.getParentContainer()))) {
            throw new CustomException("此处无法进行驳回操作");
        }

        //记录原活动方向
        List<SequenceFlow> oriSequenceFlows = Lists.newArrayList();
        oriSequenceFlows.addAll(currFlow.getOutgoingFlows());

        //清理活动方向
        currFlow.getOutgoingFlows().clear();

        //建立新的方向
        List<SequenceFlow> newSequenceFlows = Lists.newArrayList();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        newSequenceFlow.setId(uuid);
        newSequenceFlow.setSourceFlowElement(currFlow);  //原节点
        newSequenceFlow.setTargetFlowElement(targetFlow);  //目标节点
        newSequenceFlows.add(newSequenceFlow);
        currFlow.setOutgoingFlows(newSequenceFlows);

        //审批意见叠加
        //variables 审批意见     act_ru_variable  变量表 
        Map<String, Object> variables = task.getProcessVariables();
        //拒绝,通过,驳回 驳回指定节点
        List<ApprovalOpinionDTO> approvalOpinionDTOs = new ArrayList<>();
        //获取工作流审批记录
        Object options = variables.get(Constant.ACT_APPLY_OPINION_LIST);
        if (null != options) {
            approvalOpinionDTOs = JSONObject.parseArray(options.toString(), ApprovalOpinionDTO.class);
        }
        //添加审批过后的返回提升信息
        //ApprovalOpinionConverter 实体类转换器(没有 vo dto 等要求的可以不用转换,直接用一个类就可以了) 
        ApprovalOpinionDTO applyOpinionDTO = ApprovalOpinionConverter.INSTANCE.vo2dto(approvalOpinionVO);
        applyOpinionDTO.setFlagStr(applyOpinionDTO.getTaskNodeName()+"撤回到"+targetFlow.getName());
        approvalOpinionDTOs.add(applyOpinionDTO);
        map.put(Constant.ACT_APPLY_OPINION_LIST, JSONObject.toJSONString(approvalOpinionDTOs));

        //完成节点任务
        taskService.complete(task.getId(), map);
        //恢复原方向
        currFlow.setOutgoingFlows(oriSequenceFlows);
        return true;
    }

handleApproval 审批处理

/**
     * 审批处理
     *
     * @param approvalOpinionVO
     * @return
     */
    @Override
    public boolean handleApproval(ApprovalOpinionVO approvalOpinionVO) {
        Task task = taskService.createTaskQuery()
                .taskTenantId(UserUtils.getTenantId())  //租户
                .taskId(approvalOpinionVO.getTaskId())
                .includeProcessVariables()     //节点审批信息
                .singleResult();
        if (task == null) {
            throw new CustomException("流程未启动或已执行完成");
        }
        if (StringUtils.isNotEmpty(task.getAssignee()) && !UserUtils.getUserId().equals(task.getAssignee())) {
            throw new CustomException("当前用户不是审核人,无法进行审核");
        }

		//task.getAssignee() 获取审批人
        if (StringUtils.isEmpty(task.getAssignee())) {
            Set<String> candidates = getCandiates(task.getId());
            if (candidates.isEmpty()) {
                throw new CustomException("当前用户不是审核人,无法进行审核");
            }
            if (candidates.contains(UserUtils.getUserId())) {
                taskService.claim(task.getId(), UserUtils.getUserId());
            }
        }
		//获取流程变量
        Map<String, Object> processVariables = task.getProcessVariables();
        approvalOpinionVO.setCreateTime(new Date());
        approvalOpinionVO.setOpId(UserUtils.getUserId());
        approvalOpinionVO.setOpName(SystemUtil.getCurrUserDto().getName());
        approvalOpinionVO.setTaskNodeName(task.getName());
        Map<String, Object> map = Maps.newHashMap();
        map.put(Constant.ACT_TASK_FLAG, approvalOpinionVO.getFlag());    //审批操作 结果 

        //驳回操作
       // if (DictEnum.APPLY_APPROVAL_OPINION_REJECT.getKey().equals(approvalOpinionVO.getFlag())) {
      //      return rejected(approvalOpinionVO, task, map);
       // }
        //驳回到指定步骤
        if(DictEnum.APPLY_APPROVAL_OPINION_ASSIGN.getKey().equals(approvalOpinionVO.getFlag())){
            return  runNodes(approvalOpinionVO, task, map);
        }
        // 审批信息叠加
        List<ApprovalOpinionDTO> opinionDTOS = new ArrayList<>();
        Object options = processVariables.get(Constant.ACT_APPLY_OPINION_LIST);
        if (options != null) {
            opinionDTOS = JSONObject.parseArray(options.toString(), ApprovalOpinionDTO.class);
        }
        ApprovalOpinionDTO applyOpinionDTO = ApprovalOpinionConverter.INSTANCE.vo2dto(approvalOpinionVO);
        opinionDTOS.add(applyOpinionDTO);
        map.put(Constant.ACT_APPLY_OPINION_LIST, JSONObject.toJSONString(opinionDTOS));
        taskService.complete(applyOpinionDTO.getTaskId(), map);
        return true;
    }
/*补充知识点
*complete(String taskId, Map<String,Object> variables)
*参数:taskId(对应act_ru_task中的id_),variables(下一次任务所需要的参数)
*作用:完成这一次任务,并且下一步任务需要流程变量的
*/

DynamicFormConf

/**
 * 动态表单配置
 *
 * @author Create by YLL
 * @date 2020/4/1 14:37
 */
@Data
public class DynamicFormConf implements Serializable {
    /**
     * 审批操作类型
     */
    private Map<String, String> approvalTypeFlag;

    /**
     * 是否可以修改表单
     */
    private boolean writable;

    /**
     * 字段是否可编辑(批量控制)
     */
    private boolean fieldWritable;

    /**
     * 是否可以指定下一步办理人
     */
    private boolean nextAssign;

    /**
     * 存放已经执行的节点信息
     */
    private Map<String,String> runNodes;
}

ApprovalOpinionVO

/**
 * 申请流程 审批信息
 *
 * @author LEN
 */
@Data
public class ApprovalOpinionVO implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 审批人id
     */
    private String opId;
    /**
     * 审批人姓名
     */
    private String opName;
    /**
     * 审批意见
     */
    private String opinion;
    /**
     * 审批时间
     */
    private Date createTime;
    /**
     * 是否通过 0拒绝,1同意,2驳回
     */
    private String flag;
    /**
     * 流程任务id
     */
    private String taskId;
    /**
     * 当前节点名称
     */
    private String taskNodeName;

    /**
     * 附件
     */
    private List<AttachmentEntityImpl> attachments;

    /**
     * 下一步审核人是否默认 0 默认,1 审批人指定
     */
    private String defNextAssignee;

    /**
     * 指定的下一步审核人
     */
    private UserDTO nextAssignee;

    /**
     * 驳回到指定节点id
     */
    private String runNodeId;

    /**
     * id
     */
    private String id;
}

ApprovalOpinionDTO


/**
 * 申请流程 审批信息
 *
 * @author LEN
 */
@Data
public class ApprovalOpinionDTO implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 审批人id
     */
    private String opId;
    /**
     * 审批人姓名
     */
    private String opName;
    /**
     * 审批意见
     */
    private String opinion;
    /**
     * 审批时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date createTime;
    /**
     * DIctEnum  流程审批操作类型:
     * 是否通过 0拒绝,1同意,2驳回。。。
     */
    private String flag;

    /**
     * 审批结果对应的字典值
     */
    private String flagStr;
    /**
     * 流程id
     */
    private String taskId;
    /**
     * 当前节点名称
     */
    private String taskNodeName;

    /**
     * 附件
     */
    private List<AttachmentEntityImpl> attachments;

    /**
     * 下一步审核人是否默认 0 默认,1 审批人指定
     */
    private String defNextAssignee;

    /**
     * 指定的下一步审核人
     */
    private UserDTO nextAssignee;

    /**
     * 驳回到指定节点id
     */
    private String runNodeId;

    /**
     * 节点样式
     */
    private String style;

}

Constant 自定义系统常量

/**
 * 系统常量
 *
 * @author len.sun
 * @data 2018-01-14
 */
public class Constant {
  /**
     * 工作流 审批记录
     */
    public static final String ACT_APPLY_OPINION_LIST = "applyOpinionList";
   }

ApprovalOpinionConverter 实体类转换器
没有 vo dto 等要求的项目可以不用转换,直接用一个类就可以了

/**
 * 申请实体类转换器
 *
 * @Auther len.sun
 * @Date 2019/6/15
 */
@Mapper
public abstract class ApprovalOpinionConverter {

    public static ApprovalOpinionConverter INSTANCE = Mappers.getMapper(ApprovalOpinionConverter.class);


    /**
     * vo转dto
     * @param vo
     * @return
     */
    public abstract ApprovalOpinionDTO vo2dto(ApprovalOpinionVO vo);

}

欢迎大家留言点赞和讨论
如有转载请标明出处

  • 10
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 28
    评论
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值