activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

一 流程模块整体介绍

1 流程包括以下模块,之前讲了流程图的设计和部署,当一个流程部署后,即可根据当前的流程图启动一个流程实例了

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

2 我的任务

我的任务包括流程点上配置执行人的任务(已取消,改为所有任务都要认领后才能在我的任务中出现),还有在可认领任务中认领的任务;

3 可认领任务

包括流程点上设置候选人或者获选角色中该任务可被这些人领取,当一个人领取任务后,其他人便不能再领取该任务

4 所有任务列表

包含项目中所有进行中的任务列表

二 流程模块具体介绍

测试的请假流程图如下:(其中部门经理审核为会签任务)

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

对应的候选人和候选角色设置如下

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

1 启动流程实例

  /**
     * 启动流程
     * @param businessKey 业务对象key
     * @param processDefinitionKey 流程定义key
     * @param map 启动参数
     * @param comments 备注说明
     * @param singleton 是否只能启动单流程实例,如果为true,即一个业务对象只能启动一个流程定义的一个流程实例,但对不同的流程定义可以分别启动一个流程实例
     * @return 是否成功
     */
    public boolean startProcess(String businessKey,String processDefinitionKey,Map<String, Object> map,String comments,Boolean singleton){
        try{
            if(businessKey==null||"".equals(businessKey)){
                log.error("ActivitiService startProcess error:business key is null");
                return false;
            }
            if(processDefinitionKey==null||"".equals(processDefinitionKey)){
                log.error("ActivitiService startProcess error:process definition key is null");
                return false;
            }
            if(singleton){
                List<ProcessInstance> processInstances=runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessKey,processDefinitionKey).list();
                if(processInstances.size()>0){
                    log.error("ActivitiService startProcess error:can not start singleton process,process instance with the same businessKey and processDefinitionKey already exists");
                    return false;
                }
            }

            map.put("start", true);
            //否则历史记录中没有启动用户名
            Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
            ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);

            if (processInstance != null) {
                //comments目前是添加到流程启动后生成的task上的
                if(comments!=null&&!"".equals(comments)){
                    List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).list();
                    if (tasks.size() > 0) {
                        taskService.addComment(tasks.get(0).getId(), processInstance.getProcessInstanceId(),comments);
                    }
                }
                return true;
            } else {
                log.error("ActivitiService startProcess error:no process instance created");
                return false;
            }
        }catch(Exception e){
            log.error("ActivitiService startProcess error:",e);
            return false;
        }
    }

其中,最重要的启动流程实例的代码为

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);

启动一个流程实例至少需要一个processDefinitionKey,用来确定使用的哪一个流程定义;业务中可以按需要传入参数map等,例如请假流程中可以将请假天数传入到map中,根据天数判断审核请假流程是否需要部门经理审核;

2 认领任务

认领任务的主要代码为

taskService.claim(taskId, userId);

传入任务的id和认领人的id即可

 /**
     * 接件操作
     * @param taskId 任务id
     * @param userId 接件用户
     * @return 是否成功
     */
    public boolean claimTask(String taskId,String processInstanceId,String userId){
        try
        {
            Authentication.setAuthenticatedUserId(userId);
            taskService.claim(taskId, userId);
            if(processInstanceId==null||"".equals(processInstanceId)) {
                Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
                processInstanceId=task.getProcessInstanceId();
            }
            taskService.addComment(taskId,processInstanceId,"认领任务");
            return true;
        }
        catch (Exception e)
        {
            log.error("ActivitiService claimTask error:",e);
            return false;
        }
    }

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

3 我的任务-流转任务

认领了任务后,便可以在我的任务中对任务进行操作

若该任务的流转方向只有一条,那么直接执行任务即可;

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

若任务的流转方向有很多条,那么需选择流转方向,这里的下拉列表是从BpmnModel中获取FlowNode后,flowNode.getOutgoingFlows方法获取的

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

代码如下:

  /**
     * 获取当前任务的所有分支条件集合
     * 例如:["审核.通过"},{"审核.不通过"}]
     * @param task
     * @return
     */
    public List<String> getOutgoingFlows(Task task) {
        try {
            String processDefinitionId = task.getProcessDefinitionId();
            BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(processDefinitionId);

            Execution execution = processEngine.getRuntimeService().createExecutionQuery().executionId(task.getExecutionId()).singleResult();
            String activityId = execution.getActivityId();
            execution.getProcessInstanceId();

            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);

            List<SequenceFlow> sequenceFlows = flowNode.getOutgoingFlows();

            return returnOutgoingString(sequenceFlows,bpmnModel);
        }catch (Exception e){
            log.error("activitiService getOutgoingFlows error:"+e);
            return null;
        }
    }

    public List<String> returnOutgoingString(List<SequenceFlow> sequenceFlows, BpmnModel bpmnModel){
        List<String> result = new ArrayList<>();
        for (SequenceFlow sequenceFlow : sequenceFlows) {
            if(sequenceFlow.getTargetFlowElement() instanceof ExclusiveGateway){
                FlowElement flowElement = sequenceFlow.getTargetFlowElement();
                FlowNode flowNode1 = (FlowNode) bpmnModel.getMainProcess().getFlowElement(flowElement.getId());
                List<String> subOutgoingMaps = returnOutgoingString(flowNode1.getOutgoingFlows(),bpmnModel);
                if(subOutgoingMaps.size()>0 && subOutgoingMaps!=null) {
                    result.addAll(subOutgoingMaps);
                }
            } else {
                Matcher m = pattern.matcher(sequenceFlow.getConditionExpression().trim());
                Matcher m1 = mutiInstanceFlowPattern.matcher(sequenceFlow.getConditionExpression().trim());
                String condition = null;

                if(m.find() && m.group(1) != null && !"".equals(m.group(1))){
                     condition = m.group(1);
                } else if(m1.find() && m1.group(1) != null && !"".equals(m1.group(1))){
                     condition = m1.group(1);
                }

                if(condition != null){
                    //返回给前端流转时选择的内容不带ProviderName
                    if(condition.indexOf("|")>0)
                        condition=condition.split("\\|")[1];
                    if(!result.contains(condition)){
                        result.add(condition);
                    }
                }

            }
        }
        return result;
    }

如果当前流程实例对应的任务有多条说明该任务可能是会签任务,执行时,只需执行自己的任务即可;以下代码最重要的为最下面的taskNext方法;

具体过程为:

(1)找到当前的task

(2)为当前的任务添加备注(addComment)

(3)流程变量中添加resultInfo变量

    如果当前流程为会签流程,则resultInfo为一个数组,保存了每个会签用户的流转信息;

    如果当前流程为普通流程,则resultInfo为一个Object变量,保存了当前流程的流转信息;

 /**
     * 流程流转
     * @param instanceId 流程实例id
     * @param map 参数
     * @param comments 备注说明
     * @param userId 流转谁的任务
     * @return 是否成功
     */
    public boolean instanceNext(String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
        try
        {
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(instanceId).list();
            if (tasks.size() > 0)
            {
                if (tasks.size() > 1) {
                    if(userId!=null&&!"".equals(userId))
                        return instanceOwnNext(instanceId,map,resultInfo,comments,userId);
                    else {
                        log.error("ActivitiService instanceNext error:more than one task found,if it is a multi-instance task,please fill the last userId param or use taskNext instead.");
                        return false;
                    }
                }
                else
                    return taskNext(tasks.get(0).getId(),instanceId,map,resultInfo,comments,userId);
            } else
                return false;
        }
        catch (Exception e)
        {
            log.error("ActivitiService instanceNext error:",e);
            return false;
        }
    }
  public boolean instanceOwnNext(String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
        try{
            List<Task> tasks=taskService.createTaskQuery().processInstanceId(instanceId).taskAssignee(userId).list();
            if(tasks.size()>0) {
                if (tasks.size() > 1) {
                    log.info("ActivitiService instanceOwnNext info:more than one task assigned to the user found,all tasks will be process forward.");
                }
                boolean result=true;
                for(Task task:tasks){
                    result=result&&taskNext(task.getId(),instanceId,map,resultInfo,comments,userId);
                }
                return result;
            }
            else
            {
                log.error("ActivitiService instanceOwnNext error:no task exists");
                return false;
            }
        }catch(Exception e){
            log.error("ActivitiService instanceOwnNext error:",e);
            return false;
        }
    }
  /**
     * 流程流转
     * @param instanceId 流程实例id
     * @param map 参数
     * @param comments 备注说明
     * @param userId 流转谁的任务
     * @return 是否成功
     */
    public boolean instanceNext(String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
        try
        {
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(instanceId).list();
            if (tasks.size() > 0)
            {
                if (tasks.size() > 1) {
                    if(userId!=null&&!"".equals(userId))
                        return instanceOwnNext(instanceId,map,resultInfo,comments,userId);
                    else {
                        log.error("ActivitiService instanceNext error:more than one task found,if it is a multi-instance task,please fill the last userId param or use taskNext instead.");
                        return false;
                    }
                }
                else
                    return taskNext(tasks.get(0).getId(),instanceId,map,resultInfo,comments,userId);
            } else
                return false;
        }
        catch (Exception e)
        {
            log.error("ActivitiService instanceNext error:",e);
            return false;
        }
    }
 /**
     * 流程流转
     * @param taskId 任务实例id
     * @param instanceId 流程实例id
     * @param map 参数
     * @param comments 备注说明
     * @param userId 流转谁的任务
     * @return 是否成功
     */
    public boolean taskNext(String taskId,String instanceId,Map<String,Object> map,String resultInfo,String comments,String userId){
        try
        {
            Task task=taskService.createTaskQuery().taskId(taskId).singleResult();

            if(resultInfo !=null){
                resultInfo = java.net.URLDecoder.decode(resultInfo,"UTF-8");
            }
            Authentication.setAuthenticatedUserId(userId);
            taskService.addComment(taskId, instanceId,resultInfo!=null?resultInfo:""+";"+comments!=null?comments:"");

            if(userId!=null&&!"".equals(userId)){
                if(task.getAssignee()!=null&&!task.getAssignee().equals(userId))
                    log.warn("ActivitiService taskNext warn:task process forward by a user who is not the assignee but " + userId);
            }

            //如果是会签流程,需要将这些resultInfo累计到一个List中写入map,否则,直接写入map
            Map variables = runtimeService.getVariables(task.getExecutionId());

            Object nrOfInstances = variables.get("nrOfInstances");//多实例总数
            if(nrOfInstances!=null){
                int numOfInstances = (int)nrOfInstances;
                if(numOfInstances >0) {
                    int nrOfCompletedInstances = (int)variables.get("nrOfCompletedInstances");//已经循环的次数
                    List<String> resultInfos = new ArrayList<>();
                    if(nrOfCompletedInstances > 0) {
                        Object resultInfoObj = variables.get("resultInfo");
                        if(resultInfoObj != null) {
                            resultInfos = (List<String>)variables.get("resultInfo");
                        }
                    }

                    resultInfos.add(resultInfo);
                    map.put("resultInfo",resultInfos);
                }
            }
            else
                map.put("resultInfo",resultInfo);
            map.put("start", false);
            taskService.complete(taskId,map);
            return true;
        }
        catch (Exception e)
        {
            log.error("ActivitiService taskNext error:",e);
            return false;
        }
    }

4 我的任务-跳转任务

从当前任务跳转到流程中的任意一个节点中。例如当前任务流转错误后,想要跳回到前一个节点;

第一步,获取跳转任务列表

思路为:根据当前流程实例查找BpmnModel对象,该对象存储了流程图中的相关信息,那么可以从该变量中获取所有用户任务的名称和id

  public List<Map> getUserTaskDefines(String taskId){
        List<Map> result = null;
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if(task != null){
            result = new ArrayList<>();
            BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(task.getProcessDefinitionId());
            Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
            for (FlowElement f : flowElements) {
                if (f instanceof UserTask) {
                    if(!f.getName().equals(task.getName())) {
                        Map map = new HashMap();
                        map.put("key", f.getName());
                        map.put("value", f.getId());
                        result.add(map);
                    }
                }
            }
        }
        //组合返回List<Map>列表,Key为 usetask.name,value为 usertask.id
        return result;
    }

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

第二步,跳转任务

主要过程为:

  1. 取出BpmnModel
  2. 根据BpmnModel获取当前流程点和目标流程点
  3. 记录原活动的方向
  4. 清理活动方向
  5. 建立新方向
  6. 添加跳转的流转记录
  7. 执行任务
  8. 恢复原方向
 public boolean taskJump(Task fromTask,String targetTask,Map<String,Object> map,String comments,String userId){
        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();

        if(userId!=null&&!"".equals(userId)){
            if(fromTask.getAssignee()!=null&&!fromTask.getAssignee().equals(userId))
                log.warn("ActivitiService taskJump warn:task jumped by a user who is not the assignee but " + userId);
        }
        TaskService taskService=processEngine.getTaskService();
        String processDefinitionId = fromTask.getProcessDefinitionId();
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
        BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(processDefinitionId);
        FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(targetTask);

        Execution execution = processEngine.getRuntimeService().createExecutionQuery().executionId(fromTask.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();

        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);

        //记录原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
        oriSequenceFlows.addAll(flowNode.getOutgoingFlows());

        //清理活动方向
        flowNode.getOutgoingFlows().clear();
        //建立新方向
        List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(flowNode);
        newSequenceFlow.setTargetFlowElement(myFlowNode);
        newSequenceFlowList.add(newSequenceFlow);
        flowNode.setOutgoingFlows(newSequenceFlowList);
        //若不加,则comments里userId为空
        Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
        taskService.addComment(fromTask.getId(), fromTask.getProcessInstanceId(),"跳转操作。由“"+fromTask.getName()+"”跳转至“"+myFlowNode.getName()+"”。"+(comments!=null?comments:""));
        map.put("start", false);
        //完成任务
        taskService.complete(fromTask.getId(),map);
        //恢复原方向
        flowNode.setOutgoingFlows(oriSequenceFlows);
        return true;
    }

5 更换执行人

activiti6 第三部分 流程中具体功能-流转,跳转,更换执行人,更换当前任务使用的流程定义的版本,查看流转记录

具体过程为:先取消任务分配(taskService.unClaim(taskId),然后再由更换的执行人认领任务

   /**
     * 当流程列表为task时用
     * 更换执行人
     * @param taskId
     * @param userId
     * @return
     */
    public boolean changeAssignee(String taskId, String comments,String userId){
        try {
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            if (task != null) {
                
                boolean unClaim = activitiService.unclaimTask(task.getId(), userService.getCurrentUser().getUsername());
                if (unClaim) {
                    Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
                    taskService.addComment(task.getId(), task.getProcessInstanceId(), "更换执行人操作。行人由“" + task.getAssignee() + "”更换为至“" + userId + "”。" + (comments != null ? comments : ""));
                    return activitiService.claimTask(task.getId(), userId);
                } else {
                    return false;
                }
            }
            return false;
        } catch (Exception e){
            logger.error("change assignee error:"+e);
            return false;
        }
    }
  /**
     * 接件后撤件
     * @param taskId 任务id
     * @param userId 撤谁的件
     * @return 是否成功
     */
    public boolean unclaimTask(String taskId,String userId){
        try
        {
            if(userId!=null&&!"".equals(userId)){
                Task task=taskService.createTaskQuery().taskId(taskId).singleResult();
                if(task.getAssignee()!=null && !task.getAssignee().equals(userId))
                    log.warn("ActivitiService unclaimTask warn:task unclaim by a user who is not the assignee but " + userId);
            }
            taskService.unclaim(taskId);
            return true;
        }
        catch (Exception e)
        {
            log.error("ActivitiService unclaimTask error:",e);
            return false;
        }
    }

 /**
     * 接件操作
     * @param taskId 任务id
     * @param userId 接件用户
     * @return 是否成功
     */
    public boolean claimTask(String taskId,String processInstanceId,String userId){
        try
        {
            Authentication.setAuthenticatedUserId(userId);
            taskService.claim(taskId, userId);
            if(processInstanceId==null||"".equals(processInstanceId)) {
                Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
                processInstanceId=task.getProcessInstanceId();
            }
            taskService.addComment(taskId,processInstanceId,"认领任务");
            return true;
        }
        catch (Exception e)
        {
            log.error("ActivitiService claimTask error:",e);
            return false;
        }
    }

6 更换版本

第一步获取版本列表;


    /**
     * 查找当前流程定义的所有版本
     * @param processDefinitionKey
     * @return
     */
    public List<Integer> getVersionsByProcessDefinitionKey(String processDefinitionKey) {
        try {
            List<ProcessDefinition> processDefinitions = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
            List<Integer> list = new ArrayList<>();
            for (ProcessDefinition processDefinition : processDefinitions) {
                list.add(processDefinition.getVersion());
            }
            return list;
        } catch (Exception e) {
            log.error("查询流程所有版本号发生错误", e);
            return null;
        }
    }

第二步,使用命令执行器CommandExecutor执行版本变更


   /**
     * 当前任务流程版本变更到versionSelected
     * @param versionSelected
     * @param taskId
     * @return
     */
    public int changeVersion(int versionSelected,String taskId,String comments) {
        try {
            Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
            if(task!=null) {
                List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).list();
                return changeVersion(versionSelected,processInstances,comments);
            }
            return -1;
        } catch (Exception e) {
            log.error("更换流程实例版本发生错误,任务id为:"+taskId, e);
            return -1;
        }
    }

    /**
     *
     * 将选中的流程实例(processInstances)变更到选中的版本(versionSelected)
     * @param versionSelected
     * @param processInstances
     * @return
     */
    public int changeVersion(int versionSelected, List<ProcessInstance> processInstances,String comments) {
        try {
            CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
            String processDefinitionKey = processInstances.get(0).getProcessDefinitionKey();
            List<String> processPointsId = new ArrayList<>();
            int count = 0;

            //根据变更版本和processDefinitionKey获取变更的流程定义
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).processDefinitionVersion(versionSelected).singleResult();
            BpmnModel model = repositoryService.getBpmnModel(processDefinition.getId());
            if(model != null) {
                Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
                for(FlowElement flowElement : flowElements) {
                    if(flowElement instanceof UserTask) {
                        processPointsId.add(flowElement.getId());
                    }
                }
            }

            outerLoop:
            for (ProcessInstance processInstance : processInstances) {
                //当前执行的任务节点包含在更新的流程定义的任务节点内时,执行版本变换
                List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
                for (Task task : tasks) {
                    if (!processPointsId.contains(task.getTaskDefinitionKey())) {
                        count += 1;
                        continue outerLoop;
                    }
                }
                commandExecutor.execute(new SetProcessDefinitionVersionCmd(processInstance.getId(), versionSelected));
                Authentication.setAuthenticatedUserId(userService.getCurrentUser().getUsername());
                taskService.addComment(tasks.get(0).getId(),processInstance.getId(),"流程版本由“"+processInstance.getProcessDefinitionVersion()+"”变更为“"+versionSelected+"”;"+(comments!=null?comments:""));
            }
            return count;
        } catch (Exception e) {
            log.error("更换流程实例版本发生错误", e);
            return -1;
        }
    }

7 查看流转记录


    public List<Map> getProcessComments(String taskId) {
        try {
            List<Map> maps = new ArrayList<>();
            Task task = this.taskService.createTaskQuery().taskId(taskId).singleResult();
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
//        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(pi.getId()).list();

            List<HistoricActivityInstance> hais = historyService.createHistoricActivityInstanceQuery().processInstanceId(pi.getId())
//                .activityType("userTask")
                    .list();
            // 查询每个历史活动的批注
            for (HistoricActivityInstance hai : hais) {
                Map<String, Object> map = new HashMap<>();
                String starter = null;
                //未完成的不显示
                if(hai.getEndTime()!=null) {
                    if (hai.getActivityType().equals("startEvent")) {
                        ListIterator<HistoricIdentityLink> historicIdentityLinkListIterator = historyService.getHistoricIdentityLinksForProcessInstance(hai.getProcessInstanceId()).listIterator();
                        while (historicIdentityLinkListIterator.hasNext()) {
                            HistoricIdentityLink l = historicIdentityLinkListIterator.next();
                            if (l.getType().equals("starter")) {
                                starter = l.getUserId();
                            }
                        }
                        CommentEntityImpl c = new CommentEntityImpl();
                        c.setTime(hai.getEndTime());
                        c.setUserId(starter);
                        LoginUser loginUser = userService.findUserByName(starter);
                        if (loginUser != null) {
                            map.put("userName", loginUser.getShowName());
                        } else {
                            map.put("userName", "");
                        }

                        map.put("historicActivityInstance", hai);
                        map.put("comments", c);
                        maps.add(map);
                    } else if (hai.getActivityType().equals("userTask")) {
                        String historytaskId = hai.getTaskId();
                        List<Comment> comments = taskService.getTaskComments(historytaskId);
                        if (comments != null && comments.size() > 0) {
                            for (Comment comment : comments) {
                                map = new HashMap<>();
                                LoginUser loginUser = userService.findUserByName(comment.getUserId());
                                if (loginUser != null) {
                                    map.put("userName", loginUser.getShowName());
                                } else {
                                    map.put("userName", "");
                                }

                                map.put("historicActivityInstance", hai);
                                map.put("comments", comment);
                                maps.add(map);
                            }
                        } else {
                            LoginUser loginUser = userService.findUserByName(hai.getAssignee());
                            if (loginUser != null) {
                                map.put("userName", loginUser.getShowName());
                            } else {
                                map.put("userName", "");
                            }
                            map.put("historicActivityInstance", hai);
                            map.put("comments", null);
                            maps.add(map);
                        }
                    }
                }
            }
            return maps;
        }catch (Exception e){
            log.error("getProcessComments error:"+e);
            return null;
        }
    }