business/src/main/java/com/ycl/controller/ProjectProcessController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
business/src/main/java/com/ycl/domain/form/RejectTaskForm.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
business/src/main/java/com/ycl/service/ProjectProcessService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
business/src/main/java/com/ycl/service/impl/ProjectProcessServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
flowable/src/main/java/com/ycl/service/common/TaskCommonService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
flowable/src/main/java/com/ycl/service/impl/FlowTaskServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
business/src/main/java/com/ycl/controller/ProjectProcessController.java
@@ -2,6 +2,7 @@ import com.ycl.common.group.Update; import com.ycl.common.group.Add; import com.ycl.domain.form.RejectTaskForm; import com.ycl.domain.query.TaskQuery; import org.springframework.validation.annotation.Validated; import org.springframework.security.access.prepost.PreAuthorize; @@ -81,6 +82,13 @@ return projectProcessService.taskIsAuditing(processDefId, taskId); } @PostMapping("/detail/task/reject") @ApiOperation(value = "驳回", notes = "驳回") // @PreAuthorize("@ss.hasPermi('projectProcess:detail')") public Result rejectTask(@RequestBody RejectTaskForm form) { return projectProcessService.rejectTask(form); } @GetMapping("/to_do_task") @ApiOperation(value = "分页", notes = "分页") business/src/main/java/com/ycl/domain/form/RejectTaskForm.java
New file @@ -0,0 +1,36 @@ package com.ycl.domain.form; import lombok.Data; /** * @author:xp * @date:2024/12/13 13:51 */ @Data public class RejectTaskForm { /** * 当前任务id * */ private String taskId; /** * 被驳回的任务定义id * */ private String rejectedTaskDefKey; /** * 审核意见 * */ private String auditOpinion; /** * 流程实例id * */ private String processInsId; } business/src/main/java/com/ycl/service/ProjectProcessService.java
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.ycl.common.base.Result; import com.ycl.domain.form.ProjectProcessForm; import com.ycl.domain.form.RejectTaskForm; import com.ycl.domain.query.ProjectProcessQuery; import com.ycl.domain.query.TaskQuery; import com.ycl.domain.vo.CustomerTaskVO; @@ -78,4 +79,12 @@ * @return */ Result taskIsAuditing(String processDefinitionId, String taskId); /** * 驳回任务 * * @param form * @return */ Result rejectTask(RejectTaskForm form); } business/src/main/java/com/ycl/service/impl/ProjectProcessServiceImpl.java
@@ -19,6 +19,7 @@ import com.ycl.domain.dto.FlowTaskDto; import com.ycl.domain.entity.ProjectInfo; import com.ycl.domain.entity.ProjectProcess; import com.ycl.domain.form.RejectTaskForm; import com.ycl.domain.vo.CustomerTaskVO; import com.ycl.domain.vo.ProjectProcessDetailVO; import com.ycl.mapper.ProjectInfoMapper; @@ -358,6 +359,13 @@ return Result.ok().data(needAuditing); } @Override public Result rejectTask(RejectTaskForm form) { Task task = taskService.createTaskQuery().taskId(form.getTaskId()).singleResult(); taskCommonService.reject(form.getRejectedTaskDefKey(), task.getTaskDefinitionKey(), task.getProcessInstanceId(), form.getTaskId(), form.getAuditOpinion()); return Result.ok("驳回成功"); } /** * 查询待办任务 * @@ -486,12 +494,14 @@ Task task = taskService.createTaskQuery().processInstanceId(process.getId()).taskDefinitionKey(userTask.getId()).singleResult(); if (Objects.isNull(task)) { // 如果任务在运行时没找到,那么可能为未开始或者已完成,需要从历史任务中再找一下 HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery() List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery() .processInstanceId(process.getProcessInstanceId()) .taskDefinitionKey(userTask.getId()) .includeIdentityLinks() .singleResult(); if (Objects.isNull(historicTask)) { .orderByHistoricTaskInstanceStartTime() .desc() .list(); if (CollectionUtils.isEmpty(historicTasks)) { vo.setPromoterName("暂无"); vo.setPromoterUnitName("暂无"); // 未开始的任务,其关联的用户组这些都可以从UserTask中拿到,因为本身未开始的任务是没有task的,所以这里直接查 @@ -534,18 +544,18 @@ } else { vo.setTaskStatus(TaskStatusEnum.FINISHED); // 如果是已完成的,信息需要单独赋值 vo.setTaskId(historicTask.getId()); vo.setExecutionId(historicTask.getExecutionId()); vo.setCreateTime(historicTask.getStartTime()); vo.setTaskId(historicTasks.get(0).getId()); vo.setExecutionId(historicTasks.get(0).getExecutionId()); vo.setCreateTime(historicTasks.get(0).getStartTime()); // 查询实际处理人 long handlerUserId = Long.parseLong(historicTask.getAssignee()); long handlerUserId = Long.parseLong(historicTasks.get(0).getAssignee()); SysUser handlerUser = sysUserService.selectUserById(handlerUserId); if (Objects.nonNull(handlerUser)) { vo.setHandlerId(handlerUserId); vo.setHandlerName(handlerUser.getNickName()); } vo.setTaskDefinitionKey(historicTask.getTaskDefinitionKey()); this.setPromoterAndHandler(vo, historicTask.getIdentityLinks()); vo.setTaskDefinitionKey(historicTasks.get(0).getTaskDefinitionKey()); this.setPromoterAndHandler(vo, historicTasks.get(0).getIdentityLinks()); } } else { vo.setTaskStatus(TaskStatusEnum.TODO); @@ -602,12 +612,14 @@ Task task = taskService.createTaskQuery().processInstanceId(process.getId()).taskDefinitionKey(userTask.getId()).singleResult(); if (Objects.isNull(task)) { // 如果任务在运行时没找到,那么可能为未开始或者已完成,只查询未开始的 HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery() List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery() .processInstanceId(process.getProcessInstanceId()) .taskDefinitionKey(userTask.getId()) .includeIdentityLinks() .singleResult(); if (Objects.isNull(historicTask)) { .orderByHistoricTaskInstanceStartTime() .desc() .list(); if (CollectionUtils.isEmpty(historicTasks)) { // 未开始的任务,其关联的用户组这些都可以从UserTask中拿到,因为本身未开始的任务是没有task的,所以这里直接查 if (StringUtils.isNotBlank(userTask.getAssignee())) { vo.setHandlerType(HandlerTypeEnum.USER); @@ -675,12 +687,14 @@ Task task = taskService.createTaskQuery().processInstanceId(process.getId()).taskDefinitionKey(userTask.getId()).singleResult(); if (Objects.isNull(task)) { // 如果任务在运行时没找到,那么可能为未开始或者已完成,只查询未开始的 HistoricTaskInstance historicTask = historyService.createHistoricTaskInstanceQuery() List<HistoricTaskInstance> historicTasks = historyService.createHistoricTaskInstanceQuery() .processInstanceId(process.getProcessInstanceId()) .taskDefinitionKey(userTask.getId()) .includeIdentityLinks() .singleResult(); if (Objects.isNull(historicTask)) { .orderByHistoricTaskInstanceStartTime() .desc() .list(); if (CollectionUtils.isEmpty(historicTasks)) { num++; } } @@ -792,10 +806,15 @@ // 发起人应为上一节点的处理人 List<String> beforeNodeKey = taskCommonService.getBeforeNodeInfo(taskVO.getProcessDefId(), taskVO.getTaskDefinitionKey()); List<SysUser> userList = beforeNodeKey.stream().map(key -> { HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskDefinitionKey(key).singleResult(); if (Objects.nonNull(historicTaskInstance)) { List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery() .processInstanceId(taskVO.getProcessInsId()) .taskDefinitionKey(key) .orderByHistoricTaskInstanceStartTime() .desc() .list(); // 之所以用list是因为如果某个任务被驳回过,且如果该任务再次执行时会有多条数据,取最新的一条 if (! CollectionUtils.isEmpty(historicTaskInstances)) { // 实际领取这个任务的人,也就是处理人 String assignee = historicTaskInstance.getAssignee(); String assignee = historicTaskInstances.get(0).getAssignee(); SysUser startUser = sysUserService.selectUserById(Long.parseLong(assignee)); return startUser; } else { flowable/src/main/java/com/ycl/service/common/TaskCommonService.java
@@ -2,6 +2,7 @@ import com.alibaba.fastjson2.JSONObject; import com.ycl.common.constant.ProcessConstants; import com.ycl.common.enums.FlowComment; import com.ycl.domain.entity.SysForm; import com.ycl.domain.vo.FormDetailVO; import com.ycl.flow.FindNextNodeUtil; @@ -9,9 +10,13 @@ import lombok.RequiredArgsConstructor; import org.flowable.bpmn.model.*; import org.flowable.bpmn.model.Process; import org.flowable.engine.HistoryService; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.task.api.Task; import org.flowable.task.api.history.HistoricTaskInstance; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; @@ -29,9 +34,11 @@ private final RuntimeService runtimeService; private final RepositoryService repositoryService; private final TaskService taskService; private final HistoryService historyService; /** * 通过当前节点定义key,获取其上一个节点以及当前节点的信息,如果前面是并行的会返回多个 * 通过当前节点定义key,获取其上一个节点的信息,如果前面是并行的会返回多个(包含当前节点) * * @param processDefId 流程定义id * @param currentNodeDefId 当前节点定义id @@ -91,6 +98,52 @@ defKeys.add(formDetailVO); } this.beforeNodeInfo(currentElement, defKeys); return defKeys; } /** * 获取当前节点的前置节点,不包含当前节点 * * @param processDefId * @param currentNodeDefId * @return */ public List<FormDetailVO> getBeforeNodeList(String processDefId, String currentNodeDefId) { // 获取流程定义 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionId(processDefId) .singleResult(); // 获取流程模型 BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefId); if (bpmnModel == null) { throw new RuntimeException("BpmnModel not found for processDefinitionId: " + processDefId); } // 获取流程对象 Process process = bpmnModel.getProcessById(processDefinition.getKey()); if (process == null) { throw new RuntimeException("Process not found for processDefinitionId: " + processDefId); } // 遍历流程元素,找到对应的任务节点 Collection<FlowElement> flowElements = process.getFlowElements(); UserTask currentElement = null; for (FlowElement flowElement : flowElements) { if (flowElement instanceof UserTask && flowElement.getId().equals(currentNodeDefId)) { currentElement = (UserTask) flowElement; break; } } if (Objects.isNull(currentElement)) { throw new RuntimeException("未找到该任务的流程定义节点"); } List<FormDetailVO> defKeys = new ArrayList<>(2); this.beforeNodeInfo(currentElement, defKeys); return defKeys; @@ -209,4 +262,34 @@ return Boolean.FALSE; } /** * 驳回任务 * * @param rejectedTaskDefKey 被驳回的任务key * @param rejectTaskDefKey 执行驳回操作所在的任务key * @param processInsId 流程实例id * @param taskId 当前任务id * @param msg 审核意见 */ public void reject(String rejectedTaskDefKey, String rejectTaskDefKey, String processInsId, String taskId, String msg) { // 驳回的核心api:runtimeService.createChangeActivityStateBuilder().moveXXX 的api,可以设置从当前节点移动到目标节点 // 驳回的核心:需要找到当前节点、以及要流转到的目标节点。其中比较麻烦的是处理并行等比较复杂的情况 /** * 驳回的情况分为以下三种: * * 1. 如果执行驳回操作的任务是在并行结束的后一个节点,那么被驳回的任务属于并行中的某个分支的结束节点 * 2. 如果执行驳回操作的节点是并行开始后的某一分支的开始节点,那么该节点和被驳回节点实际上属于串行, 只不过需要同时把其它并行分支也驳回了(这里并不需要手动处理) * 3. 如果 被驳回节点和驳回节点属于串行,则直接驳回无需考虑其它 */ // 所以重要的是判断两个任务之间是否存在特殊节点,目前只先考虑并行网关 // 设置两条评论 List<HistoricTaskInstance> rejectedTaskList = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInsId).taskDefinitionKey(rejectedTaskDefKey).orderByHistoricTaskInstanceStartTime().desc().list(); String msg1 = "驳回了:【" + rejectedTaskList.get(0).getName() + "】,驳回原因:"; taskService.addComment(taskId, processInsId, FlowComment.REJECT.getType(), msg1 + msg); // TODO 直接使用这个api好像有问题 runtimeService.createChangeActivityStateBuilder().processInstanceId(processInsId).moveActivityIdTo(rejectTaskDefKey, rejectedTaskDefKey).changeState(); } } flowable/src/main/java/com/ycl/service/impl/FlowTaskServiceImpl.java
@@ -1215,26 +1215,28 @@ // 这里只需要查自身以及上一个节点(如果并行的有多个)的表单数据 List<FormDetailVO> beforeNodes = taskCommonService.getBeforeNodeDefInfo(processDefId, processDefKey, sysFormService, Boolean.TRUE); List<String> beforeNodeDefIds = beforeNodes.stream().filter(item -> !item.getCurrent() || currentNeedData).map(FormDetailVO::getBeforeNodeDefId).collect(Collectors.toList()); Map<String, Object> newP = new HashMap<>(); if (CollectionUtils.isNotEmpty(beforeNodeDefIds)) { for (String key : parameters.keySet()) { // 过滤拿到目标数据,将目标表单数据放到新map中 if (beforeNodeDefIds.stream().anyMatch(defId -> key.startsWith(defId))) { if (key.contains(ProcessConstants.TASK_FORM_KEY)) { newP.put(key, parameters.get(key)); } else { newP.put(key.split("&")[1], parameters.get(key)); } } } } // 拿到目标表单后,再处理每个表单的数据 // 处理每个表单的数据 for (FormDetailVO formDetailVO : beforeNodes) { if (formDetailVO.getCurrent() && !currentNeedData) { continue; // 跳过当前节点,因为当前节点在获取前置节点时已经设置过了(但表单数据没有给) } Map<String, Object> newP = new HashMap<>(); if (CollectionUtils.isNotEmpty(beforeNodeDefIds)) { for (String key : parameters.keySet()) { // 过滤拿到目标表单数据,将目标表单数据放到新map中 if (key.startsWith(formDetailVO.getBeforeNodeDefId())) { if (key.contains(ProcessConstants.TASK_FORM_KEY)) { newP.put(key, parameters.get(key)); } else { newP.put(key.split("&")[1], parameters.get(key)); } } } } Object form = newP.get(formDetailVO.getBeforeNodeDefId() + "&" + ProcessConstants.TASK_FORM_KEY); if (Objects.nonNull(form)) { JSONObject formJson = JSONObject.parseObject(JSON.toJSONString(form));