Browse Source

重构工作流(8),支持task跳转了

zhouhao 6 years ago
parent
commit
c40a6c6e76

+ 7 - 2
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/flowable/utils/JumpTaskCmd.java

@@ -1,5 +1,6 @@
 package org.hswebframework.web.workflow.flowable.utils;
 package org.hswebframework.web.workflow.flowable.utils;
 
 
+import org.activiti.engine.history.HistoricActivityInstance;
 import org.activiti.engine.impl.context.Context;
 import org.activiti.engine.impl.context.Context;
 import org.activiti.engine.impl.interceptor.Command;
 import org.activiti.engine.impl.interceptor.Command;
 import org.activiti.engine.impl.interceptor.CommandContext;
 import org.activiti.engine.impl.interceptor.CommandContext;
@@ -9,6 +10,8 @@ import org.activiti.engine.impl.pvm.process.ActivityImpl;
 import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
 import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
 import org.activiti.engine.task.Comment;
 import org.activiti.engine.task.Comment;
 
 
+import java.util.List;
+
 /**
 /**
  * Created by Administrator on 2016/4/20 0020.
  * Created by Administrator on 2016/4/20 0020.
  */
  */
@@ -24,13 +27,15 @@ public class JumpTaskCmd implements Command<Comment> {
 
 
     @Override
     @Override
     public Comment execute(CommandContext commandContext) {
     public Comment execute(CommandContext commandContext) {
-        for (TaskEntity taskEntity : Context.getCommandContext().getTaskEntityManager().findTasksByExecutionId(executionId)) {
-            Context.getCommandContext().getTaskEntityManager().deleteTask(taskEntity, "jump", false);
+
+        for (TaskEntity taskEntity : commandContext.getTaskEntityManager().findTasksByExecutionId(executionId)) {
+            commandContext.getTaskEntityManager().deleteTask(taskEntity, "jump", false);
         }
         }
         ExecutionEntity executionEntity = Context.getCommandContext().getExecutionEntityManager().findExecutionById(executionId);
         ExecutionEntity executionEntity = Context.getCommandContext().getExecutionEntityManager().findExecutionById(executionId);
         ProcessDefinitionImpl processDefinition = executionEntity.getProcessDefinition();
         ProcessDefinitionImpl processDefinition = executionEntity.getProcessDefinition();
         ActivityImpl activity = processDefinition.findActivity(activityId);
         ActivityImpl activity = processDefinition.findActivity(activityId);
         executionEntity.executeActivity(activity);
         executionEntity.executeActivity(activity);
+
         return null;
         return null;
     }
     }
 }
 }

+ 3 - 15
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/service/BpmTaskService.java

@@ -4,6 +4,7 @@ import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.impl.pvm.process.ActivityImpl;
 import org.activiti.engine.impl.pvm.process.ActivityImpl;
 import org.activiti.engine.task.Task;
 import org.activiti.engine.task.Task;
 import org.hswebframework.web.workflow.service.request.CompleteTaskRequest;
 import org.hswebframework.web.workflow.service.request.CompleteTaskRequest;
+import org.hswebframework.web.workflow.service.request.JumpTaskRequest;
 import org.hswebframework.web.workflow.service.request.RejectTaskRequest;
 import org.hswebframework.web.workflow.service.request.RejectTaskRequest;
 
 
 import java.util.Collection;
 import java.util.Collection;
@@ -78,27 +79,14 @@ public interface BpmTaskService {
 
 
     /**
     /**
      * 流程任意跳转
      * 流程任意跳转
-     *
-     * @param procInstId 流程实例ID
-     * @param activity   流程环节ID
      */
      */
-    Task jumpTask(String procInstId, String activity);
+    void jumpTask(JumpTaskRequest request);
 
 
     /**
     /**
-     * 驳回
-     *
-     * @param request
+     * 驳回任务
      */
      */
     void reject(RejectTaskRequest request);
     void reject(RejectTaskRequest request);
 
 
-    /**
-     * 设置办理人
-     *
-     * @param taskId 当前环节ID
-     * @param userId 用户ID
-     */
-    void setAssignee(String taskId, String userId);
-
     /**
     /**
      * 结束流程
      * 结束流程
      *
      *

+ 11 - 24
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/service/imp/BpmProcessServiceImpl.java

@@ -86,31 +86,18 @@ public class BpmProcessServiceImpl extends AbstractFlowableService implements Bp
 
 
             List<Task> tasks = bpmTaskService.selectTaskByProcessId(processInstance.getProcessDefinitionId());
             List<Task> tasks = bpmTaskService.selectTaskByProcessId(processInstance.getProcessDefinitionId());
 
 
-            //指定了下一环节
-            if (!StringUtils.isNullOrEmpty(request.getNextActivityId())) {
-                //跳转
-                Task afterJump = bpmTaskService.jumpTask(processInstance.getProcessDefinitionId(), request.getNextActivityId());
-
-                //设置候选人
-                candidateUserSetter.accept(afterJump);
-
-                tasks.stream()
-                        .map(Task::getId)
-                        .forEach(bpmTaskService::removeHiTask);
+            //当前节点
+            String activityId = processInstance.getActivityId();
+            if (activityId == null) {
+                //所有task设置候选人
+                tasks.forEach(candidateUserSetter);
             } else {
             } else {
-                //当前节点
-                String activityId = processInstance.getActivityId();
-                if (activityId == null) {
-                    //所有task设置候选人
-                    tasks.forEach(candidateUserSetter);
-                } else {
-                    candidateUserSetter.accept(taskService
-                            .createTaskQuery()
-                            .processInstanceId(processInstance.getProcessInstanceId())
-                            .taskDefinitionKey(activityId)
-                            .active()
-                            .singleResult());
-                }
+                candidateUserSetter.accept(taskService
+                        .createTaskQuery()
+                        .processInstanceId(processInstance.getProcessInstanceId())
+                        .taskDefinitionKey(activityId)
+                        .active()
+                        .singleResult());
             }
             }
 
 
             workFlowFormService.saveProcessForm(processInstance, SaveFormRequest
             workFlowFormService.saveProcessForm(processInstance, SaveFormRequest

+ 208 - 114
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/service/imp/BpmTaskServiceImpl.java

@@ -4,9 +4,9 @@ import lombok.SneakyThrows;
 import org.activiti.engine.history.HistoricActivityInstance;
 import org.activiti.engine.history.HistoricActivityInstance;
 import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.history.HistoricTaskInstance;
 import org.activiti.engine.history.HistoricTaskInstance;
+import org.activiti.engine.history.HistoricVariableInstance;
 import org.activiti.engine.impl.RepositoryServiceImpl;
 import org.activiti.engine.impl.RepositoryServiceImpl;
 import org.activiti.engine.impl.TaskServiceImpl;
 import org.activiti.engine.impl.TaskServiceImpl;
-import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
 import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
 import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
 import org.activiti.engine.impl.pvm.PvmActivity;
 import org.activiti.engine.impl.pvm.PvmActivity;
 import org.activiti.engine.impl.pvm.PvmTransition;
 import org.activiti.engine.impl.pvm.PvmTransition;
@@ -19,9 +19,9 @@ import org.activiti.engine.task.Task;
 import org.hswebframework.ezorm.rdb.executor.SqlExecutor;
 import org.hswebframework.ezorm.rdb.executor.SqlExecutor;
 import org.hswebframework.utils.StringUtils;
 import org.hswebframework.utils.StringUtils;
 import org.hswebframework.web.BusinessException;
 import org.hswebframework.web.BusinessException;
+import org.hswebframework.web.Maps;
 import org.hswebframework.web.NotFoundException;
 import org.hswebframework.web.NotFoundException;
 import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.Authentication;
-import org.hswebframework.web.authorization.User;
 import org.hswebframework.web.workflow.dao.entity.ProcessHistoryEntity;
 import org.hswebframework.web.workflow.dao.entity.ProcessHistoryEntity;
 import org.hswebframework.web.workflow.service.ProcessHistoryService;
 import org.hswebframework.web.workflow.service.ProcessHistoryService;
 import org.hswebframework.web.workflow.service.config.ProcessConfigurationService;
 import org.hswebframework.web.workflow.service.config.ProcessConfigurationService;
@@ -31,6 +31,7 @@ import org.hswebframework.web.workflow.flowable.utils.JumpTaskCmd;
 import org.hswebframework.web.workflow.service.WorkFlowFormService;
 import org.hswebframework.web.workflow.service.WorkFlowFormService;
 import org.hswebframework.web.workflow.service.config.CandidateInfo;
 import org.hswebframework.web.workflow.service.config.CandidateInfo;
 import org.hswebframework.web.workflow.service.request.CompleteTaskRequest;
 import org.hswebframework.web.workflow.service.request.CompleteTaskRequest;
+import org.hswebframework.web.workflow.service.request.JumpTaskRequest;
 import org.hswebframework.web.workflow.service.request.RejectTaskRequest;
 import org.hswebframework.web.workflow.service.request.RejectTaskRequest;
 import org.hswebframework.web.workflow.service.request.SaveFormRequest;
 import org.hswebframework.web.workflow.service.request.SaveFormRequest;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
@@ -38,13 +39,19 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 
 
 import java.util.*;
 import java.util.*;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 
 
 
 /**
 /**
- * @Author wangwei
- * @Date 2017/8/7.
+ * @author wangwei
+ * @author zhouhao
  */
  */
 @Service
 @Service
 @Transactional(rollbackFor = Throwable.class)
 @Transactional(rollbackFor = Throwable.class)
@@ -71,17 +78,26 @@ public class BpmTaskServiceImpl extends AbstractFlowableService implements BpmTa
     public List<Task> selectNowTask(String procInstId) {
     public List<Task> selectNowTask(String procInstId) {
         return taskService.createTaskQuery()
         return taskService.createTaskQuery()
                 .processInstanceId(procInstId)
                 .processInstanceId(procInstId)
-                .active().list();
+                .active()
+                .list();
     }
     }
 
 
     @Override
     @Override
     public List<Task> selectTaskByProcessId(String procInstId) {
     public List<Task> selectTaskByProcessId(String procInstId) {
-        return taskService.createTaskQuery().processInstanceId(procInstId).active().list();
+        return taskService
+                .createTaskQuery()
+                .processInstanceId(procInstId)
+                .active()
+                .list();
     }
     }
 
 
     @Override
     @Override
     public Task selectTaskByTaskId(String taskId) {
     public Task selectTaskByTaskId(String taskId) {
-        return taskService.createTaskQuery().taskId(taskId).active().singleResult();
+        return taskService
+                .createTaskQuery()
+                .taskId(taskId)
+                .active()
+                .singleResult();
     }
     }
 
 
     @Override
     @Override
@@ -118,7 +134,7 @@ public class BpmTaskServiceImpl extends AbstractFlowableService implements BpmTa
             throw new BusinessException("只能完成自己的任务");
             throw new BusinessException("只能完成自己的任务");
         }
         }
         Map<String, Object> variable = new HashMap<>();
         Map<String, Object> variable = new HashMap<>();
-        variable.put("oldTaskId", task.getId());
+        variable.put("preTaskId", task.getId());
         Map<String, Object> transientVariables = new HashMap<>();
         Map<String, Object> transientVariables = new HashMap<>();
 
 
         if (null != request.getVariables()) {
         if (null != request.getVariables()) {
@@ -141,16 +157,18 @@ public class BpmTaskServiceImpl extends AbstractFlowableService implements BpmTa
             transientVariables.putAll(request.getFormData());
             transientVariables.putAll(request.getFormData());
         }
         }
 
 
-        taskService.complete(task.getId(), variable, transientVariables);
+        taskService.complete(task.getId(), null, transientVariables);
 
 
         //跳转
         //跳转
         if (!StringUtils.isNullOrEmpty(request.getNextActivityId())) {
         if (!StringUtils.isNullOrEmpty(request.getNextActivityId())) {
-            jumpTask(task, request.getNextActivityId());
+            doJumpTask(task, request.getNextActivityId(), (t) -> {
+            });
         }
         }
 
 
         //下一步候选人
         //下一步候选人
         List<Task> tasks = selectNowTask(task.getProcessInstanceId());
         List<Task> tasks = selectNowTask(task.getProcessInstanceId());
         for (Task next : tasks) {
         for (Task next : tasks) {
+            setVariablesLocal(next.getId(), variable);
             if (!StringUtils.isNullOrEmpty(request.getNextClaimUserId())) {
             if (!StringUtils.isNullOrEmpty(request.getNextClaimUserId())) {
                 taskService.addCandidateUser(next.getId(), request.getNextClaimUserId());
                 taskService.addCandidateUser(next.getId(), request.getNextClaimUserId());
             } else {
             } else {
@@ -174,99 +192,47 @@ public class BpmTaskServiceImpl extends AbstractFlowableService implements BpmTa
         processHistoryService.insert(history);
         processHistoryService.insert(history);
     }
     }
 
 
-
     @Override
     @Override
     @SneakyThrows
     @SneakyThrows
     public void reject(RejectTaskRequest request) {
     public void reject(RejectTaskRequest request) {
         request.tryValidate();
         request.tryValidate();
-        String activityId = request.getActivityId();
-
-        //从任务历史中查找
-        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery()
-                .taskDefinitionKey(activityId)
-                .processInstanceId(request.getProcessInstanceId())
-                .singleResult();
-        if (task == null) {
-            throw new NotFoundException("历史任务环节不存在");
+        String taskId = request.getTaskId();
+        Task curTask = selectTaskByTaskId(taskId);
+        if (curTask == null) {
+            throw new NotFoundException("任务不存在或未激活");
         }
         }
-
-        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
-                .processInstanceId(task.getProcessInstanceId())
+        ProcessInstance processInstance = runtimeService
+                .createProcessInstanceQuery()
+                .processInstanceId(curTask.getProcessInstanceId())
                 .singleResult();
                 .singleResult();
-        if (processInstance == null) {
-            throw new NotFoundException("流程已经结束");
-        }
-
-        Map<String, Object> variables = processInstance.getProcessVariables();
-
-        ProcessDefinitionEntity definition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(task.getProcessDefinitionId());
-        if (definition == null) {
-            throw new BusinessException("流程定义未找到");
-        }
-
-        ActivityExecution execution = (ActivityExecution) runtimeService.createExecutionQuery()
-                .executionId(task.getExecutionId()).singleResult();
-        // 是否并行节点
-        if (execution.isConcurrent()) {
-            throw new BusinessException("并行节点不允许驳回");
-        }
-
-        // 是否存在定时任务
-        long num = managementService.createJobQuery().executionId(task.getExecutionId()).count();
-        if (num > 0) {
-            throw new BusinessException("当前环节不允许驳回");
-        }
-
-        // 取得上一步活动
-        ActivityImpl currActivity = definition.findActivity(task.getTaskDefinitionKey());
-        List<PvmTransition> nextTransitionList = currActivity.getIncomingTransitions();
-        // 清除当前活动的出口
-        List<PvmTransition> oriPvmTransitionList = new ArrayList<>();
-        List<PvmTransition> pvmTransitionList = currActivity.getOutgoingTransitions();
-        oriPvmTransitionList.addAll(pvmTransitionList);
-        pvmTransitionList.clear();
-
-        // 建立新出口
-        List<TransitionImpl> newTransitions = new ArrayList<>();
-        for (PvmTransition nextTransition : nextTransitionList) {
-            PvmActivity nextActivity = nextTransition.getSource();
-            ActivityImpl nextActivityImpl = definition.findActivity(nextActivity.getId());
-            TransitionImpl newTransition = currActivity.createOutgoingTransition();
-            newTransition.setDestination(nextActivityImpl);
-            newTransitions.add(newTransition);
-        }
-        // 完成任务
-        List<Task> tasks = taskService.createTaskQuery()
-                .processInstanceId(processInstance.getId())
-                .taskDefinitionKey(task.getTaskDefinitionKey())
-                .list();
 
 
-        for (Task t : tasks) {
-            taskService.complete(t.getId(), variables);
-            historyService.deleteHistoricTaskInstance(t.getId());
-            //删除连线
-            List<HistoricActivityInstance> instance = historyService.createHistoricActivityInstanceQuery()
-                    .processInstanceId(processInstance.getProcessInstanceId())
-                    .activityId(t.getTaskDefinitionKey())
-                    .list();
-            for (HistoricActivityInstance historicActivityInstance : instance) {
-                sqlExecutor.delete("delete from act_hi_actinst where id_= #{id}", historicActivityInstance);
-            }
-        }
-       // 恢复方向
-        for (TransitionImpl transitionImpl : newTransitions) {
-            currActivity.getOutgoingTransitions().remove(transitionImpl);
-        }
-        pvmTransitionList.addAll(oriPvmTransitionList);
-
-        //重新设置候选人
-        List<Task> nowTasks = selectNowTask(processInstance.getProcessInstanceId());
-        Task tmp = null;
-        for (Task nowTask : nowTasks) {
-            setCandidate(request.getRejectUserId(), nowTask);
-            tmp = nowTask;
+        ProcessDefinitionEntity entity = (ProcessDefinitionEntity)
+                ((RepositoryServiceImpl) repositoryService)
+                        .getDeployedProcessDefinition(curTask.getProcessDefinitionId());
+
+        ActivityImpl currActivity = entity.findActivity(curTask.getTaskDefinitionKey());
+
+        List<PvmActivity> transitions = new ArrayList<>();
+        //查找上一个环节
+        findActivity(currActivity,
+                activity ->
+                        activity.getIncomingTransitions()
+                                .stream()
+                                .map(PvmTransition::getSource)
+                                .collect(Collectors.toList()),
+                activity -> transitions.isEmpty() && "userTask".equals(activity.getProperty("type")),
+                transitions::add);
+
+        if (!transitions.isEmpty()) {
+            //跳转到上一环节
+            PvmActivity transition = transitions.get(transitions.size() - 1);
+            doJumpTask(curTask, transition.getId(), newTask -> {
+            });
+        } else {
+            throw new BusinessException("无法获取上一步任务");
         }
         }
 
 
+        //记录日志
         ProcessHistoryEntity history = ProcessHistoryEntity.builder()
         ProcessHistoryEntity history = ProcessHistoryEntity.builder()
                 .businessKey(processInstance.getBusinessKey())
                 .businessKey(processInstance.getBusinessKey())
                 .type("reject")
                 .type("reject")
@@ -275,37 +241,162 @@ public class BpmTaskServiceImpl extends AbstractFlowableService implements BpmTa
                 .creatorName(request.getRejectUserName())
                 .creatorName(request.getRejectUserName())
                 .processDefineId(processInstance.getProcessDefinitionId())
                 .processDefineId(processInstance.getProcessDefinitionId())
                 .processInstanceId(processInstance.getProcessInstanceId())
                 .processInstanceId(processInstance.getProcessInstanceId())
-                .taskId(tmp != null ? tmp.getId() : null)
-                .taskDefineKey(tmp != null ? tmp.getTaskDefinitionKey() : null)
-                .taskName(tmp != null ? tmp.getName() : null)
+                .taskId(taskId)
+                .taskDefineKey(curTask.getTaskDefinitionKey())
+                .taskName(curTask.getName())
                 .data(request.getData())
                 .data(request.getData())
                 .build();
                 .build();
 
 
-        historyService.deleteHistoricTaskInstance(task.getId());
         processHistoryService.insert(history);
         processHistoryService.insert(history);
     }
     }
 
 
-    public Task jumpTask(Task task, String activityId) {
+    protected void findActivity(
+            PvmActivity activity,
+            Function<PvmActivity, List<PvmActivity>> function,
+            Predicate<PvmActivity> predicate,
+            Consumer<PvmActivity> consumer) {
+
+        List<PvmActivity> activities = function.apply(activity);
+        for (PvmActivity pvmActivity : activities) {
+            consumer.accept(pvmActivity);
+            if (predicate.test(pvmActivity)) {
+                return;
+            }
+            //往下查找
+            findActivity(pvmActivity, function, predicate, consumer);
+        }
+    }
+
+    @SneakyThrows
+    public void doJumpTask(Task task, String activityId, Consumer<Task> newTaskConsumer) {
+
+        ProcessDefinitionEntity entity = (ProcessDefinitionEntity)
+                ((RepositoryServiceImpl) repositoryService)
+                        .getDeployedProcessDefinition(task.getProcessDefinitionId());
+
+        String sourceId = task.getTaskDefinitionKey();
+
+        ActivityImpl targetActivity = entity.findActivity(activityId);
+        ActivityImpl sourceActivity = entity.findActivity(sourceId);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("流程[{}({})]跳转[{}]->[{}]",
+                    entity.getName(),
+                    entity.getId(),
+                    sourceActivity.getId(),
+                    targetActivity.getId());
+        }
+
+        //回退的节点
+        List<PvmActivity> backActivities = new ArrayList<>();
+        //如果目标节点的Outgoing中有源节点,说明是回退需要删除对应的连线
+        findActivity(targetActivity,
+                activity -> activity
+                        .getOutgoingTransitions()
+                        .stream()
+                        .map(PvmTransition::getDestination)
+                        .collect(Collectors.toList()),
+                activity -> sourceActivity.getId().equals(activity.getId()),
+                backActivities::add);
+
+        //回退
+        if (!backActivities.isEmpty()) {
+            for (PvmActivity pvmTransition : backActivities) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("流程[{}({})]回退[{}]->[{}],删除链接线:{}",
+                            entity.getName(),
+                            entity.getId(),
+                            sourceActivity.getId(),
+                            targetActivity.getId(),
+                            pvmTransition.getId());
+                }
+                //删除连线
+                List<HistoricActivityInstance> instance = historyService
+                        .createHistoricActivityInstanceQuery()
+                        .processInstanceId(task.getProcessInstanceId())
+                        .activityId(pvmTransition.getId())
+                        .list();
+                for (HistoricActivityInstance historicActivityInstance : instance) {
+                    sqlExecutor.delete("delete from act_hi_actinst where id_= #{id}", historicActivityInstance);
+                }
+            }
+        }
+        //执行回退命令
         TaskServiceImpl taskServiceImpl = (TaskServiceImpl) taskService;
         TaskServiceImpl taskServiceImpl = (TaskServiceImpl) taskService;
         taskServiceImpl.getCommandExecutor().execute(new JumpTaskCmd(task.getExecutionId(), activityId));
         taskServiceImpl.getCommandExecutor().execute(new JumpTaskCmd(task.getExecutionId(), activityId));
-        return task;
-    }
+        //设置候选人并回调
+        selectNowTask(task.getProcessInstanceId())
+                .forEach(t -> {
+                    //设置候选人
+                    setCandidate(task.getAssignee(), t);
+                    newTaskConsumer.accept(t);
+                });
 
 
-    @Override
-    public Task jumpTask(String taskId, String activity) {
-        return jumpTask(selectTaskByTaskId(taskId), activity);
     }
     }
 
 
     @Override
     @Override
-    public void setAssignee(String taskId, String userId) {
-        taskService.setAssignee(taskId, userId);
+    public void jumpTask(JumpTaskRequest request) {
+        request.tryValidate();
+
+        Task task = taskService.createTaskQuery()
+                .taskId(request.getTaskId())
+                .singleResult();
+
+        //记录跳转后的节点到日志
+        List<Map<String, String>> targetTask = new ArrayList<>();
+
+        doJumpTask(task, request.getTargetActivityId(), t -> {
+            Map<String, String> target = new HashMap<>();
+            target.put("taskId", t.getId());
+            target.put("taskName", t.getName());
+            target.put("activityId", t.getTaskDefinitionKey());
+            targetTask.add(target);
+        });
+
+        if (request.isRecordLog()) {
+
+            ProcessInstance processInstance = runtimeService
+                    .createProcessInstanceQuery()
+                    .processInstanceId(task.getProcessInstanceId())
+                    .singleResult();
+
+            Map<String, Object> data = new HashMap<>();
+            data.put("targetTask", targetTask);
+            if (request.getData() != null) {
+                data.putAll(request.getData());
+            }
+
+            ProcessHistoryEntity history = ProcessHistoryEntity.builder()
+                    .businessKey(processInstance.getBusinessKey())
+                    .type("jump")
+                    .typeText("流程跳转")
+                    .creatorId(request.getJumpUserId())
+                    .creatorName(request.getJumpUserName())
+                    .processDefineId(processInstance.getProcessDefinitionId())
+                    .processInstanceId(processInstance.getProcessInstanceId())
+                    .taskId(task.getId())
+                    .taskDefineKey(task.getTaskDefinitionKey())
+                    .taskName(task.getName())
+                    .data(data)
+                    .build();
+
+            processHistoryService.insert(history);
+        }
     }
     }
 
 
     @Override
     @Override
     public void endProcess(String procInstId) {
     public void endProcess(String procInstId) {
-        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
-        ActivityImpl activity = bpmActivityService.getEndEvent(processInstance.getProcessDefinitionId());
-        jumpTask(procInstId, activity.getId());
+//        ProcessInstance processInstance = runtimeService
+//                .createProcessInstanceQuery()
+//                .processInstanceId(procInstId)
+//                .singleResult();
+//        ActivityImpl activity = bpmActivityService.getEndEvent(processInstance.getProcessDefinitionId());
+//        List<Task> tasks = selectNowTask(procInstId);
+//
+//        if (!tasks.isEmpty()) {
+//            doJumpTask(tasks.get(0).getId(), activity.getId());
+//        }
+
     }
     }
 
 
     @Override
     @Override
@@ -338,11 +429,14 @@ public class BpmTaskServiceImpl extends AbstractFlowableService implements BpmTa
             List<CandidateInfo> candidateInfoList = processConfigurationService
             List<CandidateInfo> candidateInfoList = processConfigurationService
                     .getActivityConfiguration(doingUserId, task.getProcessDefinitionId(), task.getTaskDefinitionKey())
                     .getActivityConfiguration(doingUserId, task.getProcessDefinitionId(), task.getTaskDefinitionKey())
                     .getCandidateInfo(task);
                     .getCandidateInfo(task);
-
-            for (CandidateInfo candidateInfo : candidateInfoList) {
-                Authentication user = candidateInfo.user();
-                if (user != null) {
-                    taskService.addCandidateUser(task.getId(), user.getUser().getId());
+            if (CollectionUtils.isEmpty(candidateInfoList)) {
+                logger.warn("任务:{}未能设置候选人,此任务可能无法办理!", task);
+            } else {
+                for (CandidateInfo candidateInfo : candidateInfoList) {
+                    Authentication user = candidateInfo.user();
+                    if (user != null) {
+                        taskService.addCandidateUser(task.getId(), user.getUser().getId());
+                    }
                 }
                 }
             }
             }
         } else {
         } else {

+ 39 - 0
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/service/request/JumpTaskRequest.java

@@ -0,0 +1,39 @@
+package org.hswebframework.web.workflow.service.request;
+
+import lombok.*;
+import org.hibernate.validator.constraints.NotBlank;
+import org.hswebframework.web.commons.bean.ValidateBean;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author zhouhao
+ * @since 3.0.0-RC
+ */
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class JumpTaskRequest implements ValidateBean {
+    private static final long serialVersionUID = 7625759475416169067L;
+
+    @NotBlank(message = "[jumpUserId]不能为空")
+    private String jumpUserId;
+
+    @NotBlank(message = "[jumpUserName]不能为空")
+    private String jumpUserName;
+
+    @NotBlank(message = "[taskId]不能为空")
+    private String taskId;
+
+    @NotBlank(message = "[targetActivityId]不能为空")
+    private String targetActivityId;
+
+    //是否记录到流程日志
+    private boolean recordLog = true;
+
+    //自定义数据,将会记录到流程历史记录里,比如回退原因等
+    private Map<String, Object> data = new HashMap<>();
+}

+ 2 - 5
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/service/request/RejectTaskRequest.java

@@ -28,11 +28,8 @@ public class RejectTaskRequest implements ValidateBean {
     @NotBlank(message = "[rejectUserName]不能为空")
     @NotBlank(message = "[rejectUserName]不能为空")
     private String rejectUserName;
     private String rejectUserName;
 
 
-    @NotBlank(message = "[processInstanceId]不能为空")
-    private String processInstanceId;
-
-    @NotBlank(message = "[activityId]不能为空")
-    private String activityId;
+    @NotBlank(message = "[taskId]不能为空")
+    private String taskId;
 
 
     //自定义数据,将会记录到流程历史记录里,比如回退原因等
     //自定义数据,将会记录到流程历史记录里,比如回退原因等
     private Map<String, Object> data = new HashMap<>();
     private Map<String, Object> data = new HashMap<>();

+ 0 - 5
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/service/request/StartProcessRequest.java

@@ -55,11 +55,6 @@ public class StartProcessRequest implements ValidateBean {
      */
      */
     private String nextClaimUserId;
     private String nextClaimUserId;
 
 
-    /**
-     * 下一环节的ID,如果指定了此属性,则流程启动后自动跳转到该环节
-     */
-    private String nextActivityId;
-
     /**
     /**
      * 流程变量
      * 流程变量
      */
      */

+ 24 - 5
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/web/FlowableProcessController.java

@@ -35,6 +35,7 @@ import org.hswebframework.web.workflow.service.config.ProcessConfigurationServic
 import org.hswebframework.web.workflow.service.BpmProcessService;
 import org.hswebframework.web.workflow.service.BpmProcessService;
 import org.hswebframework.web.workflow.service.BpmTaskService;
 import org.hswebframework.web.workflow.service.BpmTaskService;
 import org.hswebframework.web.workflow.service.request.CompleteTaskRequest;
 import org.hswebframework.web.workflow.service.request.CompleteTaskRequest;
+import org.hswebframework.web.workflow.service.request.JumpTaskRequest;
 import org.hswebframework.web.workflow.service.request.RejectTaskRequest;
 import org.hswebframework.web.workflow.service.request.RejectTaskRequest;
 import org.hswebframework.web.workflow.service.request.StartProcessRequest;
 import org.hswebframework.web.workflow.service.request.StartProcessRequest;
 import org.hswebframework.web.workflow.util.QueryUtils;
 import org.hswebframework.web.workflow.util.QueryUtils;
@@ -314,17 +315,15 @@ public class FlowableProcessController {
         return ResponseMessage.ok();
         return ResponseMessage.ok();
     }
     }
 
 
-    @PutMapping("/reject/{processInstanceId}/{activityId}")
+    @PutMapping("/reject/{taskId}")
     @Authorize(merge = false)
     @Authorize(merge = false)
     @ApiOperation("驳回")
     @ApiOperation("驳回")
-    public ResponseMessage<Void> reject(@PathVariable String processInstanceId,
-                                        @PathVariable String activityId,
+    public ResponseMessage<Void> reject(@PathVariable String taskId,
                                         @RequestBody Map<String, Object> data,
                                         @RequestBody Map<String, Object> data,
                                         Authentication authentication) {
                                         Authentication authentication) {
         // 驳回
         // 驳回
         bpmTaskService.reject(RejectTaskRequest.builder()
         bpmTaskService.reject(RejectTaskRequest.builder()
-                .processInstanceId(processInstanceId)
-                .activityId(activityId)
+                .taskId(taskId)
                 .rejectUserId(authentication.getUser().getId())
                 .rejectUserId(authentication.getUser().getId())
                 .rejectUserName(authentication.getUser().getName())
                 .rejectUserName(authentication.getUser().getName())
                 .data(data)
                 .data(data)
@@ -332,6 +331,26 @@ public class FlowableProcessController {
         return ResponseMessage.ok();
         return ResponseMessage.ok();
     }
     }
 
 
+    @PutMapping("/jump/{taskId}/{activityId}")
+    @Authorize(merge = false)
+    @ApiOperation("流程跳转")
+    public ResponseMessage<Void> jump(@PathVariable String taskId,
+                                      @PathVariable String activityId,
+                                      @RequestBody Map<String, Object> data,
+                                      Authentication authentication) {
+        // 流程跳转
+        bpmTaskService.jumpTask(JumpTaskRequest
+                .builder()
+                .taskId(taskId)
+                .targetActivityId(activityId)
+                .recordLog(true)
+                .jumpUserId(authentication.getUser().getId())
+                .jumpUserName(authentication.getUser().getUsername())
+                .data(data)
+                .build());
+        return ResponseMessage.ok();
+    }
+
     @PostMapping("/next-task-candidate/{taskId}")
     @PostMapping("/next-task-candidate/{taskId}")
     @Authorize(merge = false)
     @Authorize(merge = false)
     public ResponseMessage<List<CandidateDetail>> candidateList(@PathVariable String taskId,
     public ResponseMessage<List<CandidateDetail>> candidateList(@PathVariable String taskId,

+ 16 - 8
hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/web/diagram/ProcessInstanceHighlightsResource.java

@@ -13,11 +13,8 @@
 
 
 package org.hswebframework.web.workflow.web.diagram;
 package org.hswebframework.web.workflow.web.diagram;
 
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
 
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
@@ -25,6 +22,8 @@ import org.activiti.engine.HistoryService;
 import org.activiti.engine.RepositoryService;
 import org.activiti.engine.RepositoryService;
 import org.activiti.engine.RuntimeService;
 import org.activiti.engine.RuntimeService;
 import org.activiti.engine.history.HistoricActivityInstance;
 import org.activiti.engine.history.HistoricActivityInstance;
+import org.activiti.engine.history.HistoricActivityInstanceQuery;
+import org.activiti.engine.history.HistoricProcessInstance;
 import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
 import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
 import org.activiti.engine.impl.pvm.PvmTransition;
 import org.activiti.engine.impl.pvm.PvmTransition;
 import org.activiti.engine.impl.pvm.process.ActivityImpl;
 import org.activiti.engine.impl.pvm.process.ActivityImpl;
@@ -60,15 +59,24 @@ public class ProcessInstanceHighlightsResource {
         JSONArray activitiesArray = new JSONArray();
         JSONArray activitiesArray = new JSONArray();
         JSONArray flowsArray = new JSONArray();
         JSONArray flowsArray = new JSONArray();
 
 
-        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
+        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
                 .processInstanceId(processInstanceId)
                 .processInstanceId(processInstanceId)
                 .singleResult();
                 .singleResult();
         ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService
         ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService
                 .getProcessDefinition(processInstance.getProcessDefinitionId());
                 .getProcessDefinition(processInstance.getProcessDefinitionId());
 
 
         responseJSON.put("processDefinitionId", processInstance.getProcessDefinitionId());
         responseJSON.put("processDefinitionId", processInstance.getProcessDefinitionId());
-
-        List<String> highLightedActivities = runtimeService.getActiveActivityIds(processInstanceId);
+        List<String> highLightedActivities;
+        if (processInstance.getEndTime() != null) {
+            highLightedActivities = historyService
+                    .createHistoricActivityInstanceQuery()
+                    .processInstanceId(processInstanceId)
+                    .activityType("endEvent")
+                    .list().stream().map(HistoricActivityInstance::getActivityId)
+                    .collect(Collectors.toList());
+        } else {
+            highLightedActivities = runtimeService.getActiveActivityIds(processInstanceId);
+        }
         List<String> highLightedFlows = getHighLightedFlows(processDefinition, processInstanceId);
         List<String> highLightedFlows = getHighLightedFlows(processDefinition, processInstanceId);
 
 
         activitiesArray.addAll(highLightedActivities);
         activitiesArray.addAll(highLightedActivities);