Commit b13f71424b2fe3b5bbfa8bee979d4208f5a56779
1 parent
9630f236
feat: 工作流-》流程实例的任务节点回退
Showing
10 changed files
with
563 additions
and
40 deletions
yudao-framework/yudao-spring-boot-starter-flowable/src/main/java/cn/iocoder/yudao/framework/flowable/core/util/ModelUtils.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.flowable.core.util; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.ObjectUtil; | ||
| 4 | +import cn.hutool.core.util.StrUtil; | ||
| 5 | +import org.flowable.bpmn.converter.BpmnXMLConverter; | ||
| 6 | +import org.flowable.bpmn.model.Process; | ||
| 7 | +import org.flowable.bpmn.model.*; | ||
| 8 | +import org.flowable.common.engine.impl.util.io.StringStreamSource; | ||
| 9 | + | ||
| 10 | +import java.util.*; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * 流程模型转操作工具类 | ||
| 14 | + */ | ||
| 15 | +public class ModelUtils { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 根据节点,获取入口连线 | ||
| 19 | + * | ||
| 20 | + * @param source 起始节点 | ||
| 21 | + * @return 入口连线列表 | ||
| 22 | + */ | ||
| 23 | + public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) { | ||
| 24 | + List<SequenceFlow> sequenceFlows = new ArrayList<>(); | ||
| 25 | + if (source instanceof FlowNode) { | ||
| 26 | + sequenceFlows = ((FlowNode) source).getIncomingFlows(); | ||
| 27 | + } | ||
| 28 | + return sequenceFlows; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 根据节点,获取出口连线 | ||
| 34 | + * | ||
| 35 | + * @param source 起始节点 | ||
| 36 | + * @return 出口连线列表 | ||
| 37 | + */ | ||
| 38 | + public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) { | ||
| 39 | + List<SequenceFlow> sequenceFlows = new ArrayList<>(); | ||
| 40 | + if (source instanceof FlowNode) { | ||
| 41 | + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); | ||
| 42 | + } | ||
| 43 | + return sequenceFlows; | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * 获取流程元素信息 | ||
| 49 | + * | ||
| 50 | + * @param model bpmnModel对象 | ||
| 51 | + * @param flowElementId 元素ID | ||
| 52 | + * @return 元素信息 | ||
| 53 | + */ | ||
| 54 | + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { | ||
| 55 | + Process process = model.getMainProcess(); | ||
| 56 | + return process.getFlowElement(flowElementId); | ||
| 57 | + } | ||
| 58 | + | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * 找到 source 节点之前的所有用户任务节点 | ||
| 62 | + * | ||
| 63 | + * @param source 起始节点 | ||
| 64 | + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 | ||
| 65 | + * @param userTaskList 已找到的用户任务节点 | ||
| 66 | + * @return | ||
| 67 | + */ | ||
| 68 | + public static List<UserTask> getPreUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) { | ||
| 69 | + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; | ||
| 70 | + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; | ||
| 71 | + | ||
| 72 | + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 | ||
| 73 | + if (source instanceof StartEvent && source.getSubProcess() != null) { | ||
| 74 | + userTaskList = getPreUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + // 根据类型,获取入口连线 | ||
| 78 | + List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source); | ||
| 79 | + | ||
| 80 | + if (sequenceFlows != null) { | ||
| 81 | + // 循环找到目标元素 | ||
| 82 | + for (SequenceFlow sequenceFlow : sequenceFlows) { | ||
| 83 | + // 如果发现连线重复,说明循环了,跳过这个循环 | ||
| 84 | + if (hasSequenceFlow.contains(sequenceFlow.getId())) { | ||
| 85 | + continue; | ||
| 86 | + } | ||
| 87 | + // 添加已经走过的连线 | ||
| 88 | + hasSequenceFlow.add(sequenceFlow.getId()); | ||
| 89 | + // 类型为用户节点,则新增父级节点 | ||
| 90 | + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { | ||
| 91 | + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); | ||
| 92 | + } | ||
| 93 | + // 类型为子流程,则添加子流程开始节点出口处相连的节点 | ||
| 94 | + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { | ||
| 95 | + // 获取子流程用户任务节点 | ||
| 96 | + List<UserTask> childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); | ||
| 97 | + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 | ||
| 98 | + if (childUserTaskList != null && childUserTaskList.size() > 0) { | ||
| 99 | + userTaskList.addAll(childUserTaskList); | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + // 继续迭代 | ||
| 103 | + userTaskList = getPreUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); | ||
| 104 | + } | ||
| 105 | + } | ||
| 106 | + return userTaskList; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + /** | ||
| 110 | + * 迭代获取子流程用户任务节点 | ||
| 111 | + * | ||
| 112 | + * @param source 起始节点 | ||
| 113 | + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 | ||
| 114 | + * @param userTaskList 需要撤回的用户任务列表 | ||
| 115 | + * @return | ||
| 116 | + */ | ||
| 117 | + public static List<UserTask> findChildProcessUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) { | ||
| 118 | + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; | ||
| 119 | + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; | ||
| 120 | + | ||
| 121 | + // 根据类型,获取出口连线 | ||
| 122 | + List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source); | ||
| 123 | + | ||
| 124 | + if (sequenceFlows != null) { | ||
| 125 | + // 循环找到目标元素 | ||
| 126 | + for (SequenceFlow sequenceFlow : sequenceFlows) { | ||
| 127 | + // 如果发现连线重复,说明循环了,跳过这个循环 | ||
| 128 | + if (hasSequenceFlow.contains(sequenceFlow.getId())) { | ||
| 129 | + continue; | ||
| 130 | + } | ||
| 131 | + // 添加已经走过的连线 | ||
| 132 | + hasSequenceFlow.add(sequenceFlow.getId()); | ||
| 133 | + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 | ||
| 134 | + if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { | ||
| 135 | + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); | ||
| 136 | + continue; | ||
| 137 | + } | ||
| 138 | + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 | ||
| 139 | + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { | ||
| 140 | + List<UserTask> childUserTaskList = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); | ||
| 141 | + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 | ||
| 142 | + if (childUserTaskList != null && childUserTaskList.size() > 0) { | ||
| 143 | + userTaskList.addAll(childUserTaskList); | ||
| 144 | + continue; | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + // 继续迭代 | ||
| 148 | + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); | ||
| 149 | + } | ||
| 150 | + } | ||
| 151 | + return userTaskList; | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + | ||
| 155 | + /** | ||
| 156 | + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 | ||
| 157 | + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 | ||
| 158 | + * | ||
| 159 | + * @param source 起始节点 | ||
| 160 | + * @param target 目标节点 | ||
| 161 | + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 | ||
| 162 | + * @return 结果 | ||
| 163 | + */ | ||
| 164 | + public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) { | ||
| 165 | + visitedElements = visitedElements == null ? new HashSet<>() : visitedElements; | ||
| 166 | + //不能是开始事件和子流程 | ||
| 167 | + if (source instanceof StartEvent && isInEventSubprocess(source)) { | ||
| 168 | + return false; | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + // 根据类型,获取入口连线 | ||
| 172 | + List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source); | ||
| 173 | + if (sequenceFlows != null && sequenceFlows.size() > 0) { | ||
| 174 | + // 循环找到目标元素 | ||
| 175 | + for (SequenceFlow sequenceFlow : sequenceFlows) { | ||
| 176 | + // 如果发现连线重复,说明循环了,跳过这个循环 | ||
| 177 | + if (visitedElements.contains(sequenceFlow.getId())) { | ||
| 178 | + continue; | ||
| 179 | + } | ||
| 180 | + // 添加已经走过的连线 | ||
| 181 | + visitedElements.add(sequenceFlow.getId()); | ||
| 182 | + FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); | ||
| 183 | + // 这条线路存在目标节点,这条线路完成,进入下个线路 | ||
| 184 | + if (target.getId().equals(sourceFlowElement.getId())) { | ||
| 185 | + continue; | ||
| 186 | + } | ||
| 187 | + // 如果目标节点为并行网关,则不继续 | ||
| 188 | + if (sourceFlowElement instanceof ParallelGateway) { | ||
| 189 | + return false; | ||
| 190 | + } | ||
| 191 | + // 否则就继续迭代 | ||
| 192 | + boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements); | ||
| 193 | + if (!isSequential) { | ||
| 194 | + return false; | ||
| 195 | + } | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + return true; | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + /** | ||
| 202 | + * 判断当前节点是否属于不同的子流程 | ||
| 203 | + * | ||
| 204 | + * @param flowElement 被判断的节点 | ||
| 205 | + * @return true表示属于子流程 | ||
| 206 | + */ | ||
| 207 | + protected static boolean isInEventSubprocess(FlowElement flowElement) { | ||
| 208 | + FlowElementsContainer flowElementsContainer = flowElement.getParentContainer(); | ||
| 209 | + while (flowElementsContainer != null) { | ||
| 210 | + if (flowElementsContainer instanceof EventSubProcess) { | ||
| 211 | + return true; | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + if (flowElementsContainer instanceof FlowElement) { | ||
| 215 | + flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer(); | ||
| 216 | + } else { | ||
| 217 | + flowElementsContainer = null; | ||
| 218 | + } | ||
| 219 | + } | ||
| 220 | + return false; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + | ||
| 224 | + /** | ||
| 225 | + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 | ||
| 226 | + * | ||
| 227 | + * @param source 起始节点 | ||
| 228 | + * @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 | ||
| 229 | + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 | ||
| 230 | + * @param userTaskList 需要撤回的用户任务列表 | ||
| 231 | + * @return | ||
| 232 | + */ | ||
| 233 | + public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList, Set<String> hasSequenceFlow, List<UserTask> userTaskList) { | ||
| 234 | + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; | ||
| 235 | + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; | ||
| 236 | + | ||
| 237 | + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 | ||
| 238 | + if (source instanceof StartEvent && source.getSubProcess() != null) { | ||
| 239 | + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); | ||
| 240 | + } | ||
| 241 | + | ||
| 242 | + // 根据类型,获取出口连线 | ||
| 243 | + List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source); | ||
| 244 | + | ||
| 245 | + if (sequenceFlows != null) { | ||
| 246 | + // 循环找到目标元素 | ||
| 247 | + for (SequenceFlow sequenceFlow : sequenceFlows) { | ||
| 248 | + // 如果发现连线重复,说明循环了,跳过这个循环 | ||
| 249 | + if (hasSequenceFlow.contains(sequenceFlow.getId())) { | ||
| 250 | + continue; | ||
| 251 | + } | ||
| 252 | + // 添加已经走过的连线 | ||
| 253 | + hasSequenceFlow.add(sequenceFlow.getId()); | ||
| 254 | + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 | ||
| 255 | + if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) { | ||
| 256 | + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); | ||
| 257 | + continue; | ||
| 258 | + } | ||
| 259 | + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 | ||
| 260 | + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { | ||
| 261 | + List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); | ||
| 262 | + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 | ||
| 263 | + if (childUserTaskList != null && childUserTaskList.size() > 0) { | ||
| 264 | + userTaskList.addAll(childUserTaskList); | ||
| 265 | + continue; | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + // 继续迭代 | ||
| 269 | + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); | ||
| 270 | + } | ||
| 271 | + } | ||
| 272 | + return userTaskList; | ||
| 273 | + } | ||
| 274 | +} |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; | @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; | ||
| 4 | 4 | ||
| 5 | /** | 5 | /** |
| 6 | * Bpm 错误码枚举类 | 6 | * Bpm 错误码枚举类 |
| 7 | - * | 7 | + * <p> |
| 8 | * bpm 系统,使用 1-009-000-000 段 | 8 | * bpm 系统,使用 1-009-000-000 段 |
| 9 | */ | 9 | */ |
| 10 | public interface ErrorCodeConstants { | 10 | public interface ErrorCodeConstants { |
| @@ -45,7 +45,11 @@ public interface ErrorCodeConstants { | @@ -45,7 +45,11 @@ public interface ErrorCodeConstants { | ||
| 45 | // ========== 流程任务 1-009-005-000 ========== | 45 | // ========== 流程任务 1-009-005-000 ========== |
| 46 | ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1_009_005_000, "审批任务失败,原因:该任务不处于未审批"); | 46 | ErrorCode TASK_COMPLETE_FAIL_NOT_EXISTS = new ErrorCode(1_009_005_000, "审批任务失败,原因:该任务不处于未审批"); |
| 47 | ErrorCode TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "审批任务失败,原因:该任务的审批人不是你"); | 47 | ErrorCode TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "审批任务失败,原因:该任务的审批人不是你"); |
| 48 | - | 48 | + ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在"); |
| 49 | + ErrorCode TASK_IS_NOT_ACTIVITY = new ErrorCode(1_009_005_003, "当前任务不属于活动状态,不能操作"); | ||
| 50 | + ErrorCode TASK_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_004, " 当前节点相对于目标节点,不属于串行关系,无法回退"); | ||
| 51 | + ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_005, " 目标节点不存在"); | ||
| 52 | + ErrorCode TASK_ROLLBACK_FAIL = new ErrorCode(1_009_005_006, "回退任务失败,选择回退的节点没有需要回滚的任务!请重新选择其他任务节点"); | ||
| 49 | // ========== 流程任务分配规则 1-009-006-000 ========== | 53 | // ========== 流程任务分配规则 1-009-006-000 ========== |
| 50 | ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则"); | 54 | ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则"); |
| 51 | ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在"); | 55 | ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在"); |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
| @@ -75,4 +75,19 @@ public class BpmTaskController { | @@ -75,4 +75,19 @@ public class BpmTaskController { | ||
| 75 | return success(true); | 75 | return success(true); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | + @GetMapping("/get-return-list") | ||
| 79 | + @Operation(summary = "获取所有可回退的节点", description = "用于【流程详情】的【回退】按钮") | ||
| 80 | + @PreAuthorize("@ss.hasPermission('bpm:task:rollback')") | ||
| 81 | + public CommonResult<List<BpmTaskRollbackRespVO>> getReturnList(String taskId) { | ||
| 82 | + return success(taskService.findReturnTaskList(taskId)); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + @PutMapping("/rollback") | ||
| 86 | + @Operation(summary = "回退任务", description = "用于【流程详情】的【回退】按钮") | ||
| 87 | + @PreAuthorize("@ss.hasPermission('bpm:task:rollback')") | ||
| 88 | + public CommonResult<Boolean> getReturnList(@Valid @RequestBody BpmTaskRollbackReqVO reqVO) { | ||
| 89 | + taskService.taskReturn(reqVO); | ||
| 90 | + return success(true); | ||
| 91 | + } | ||
| 92 | + | ||
| 78 | } | 93 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRollbackReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import javax.validation.constraints.NotEmpty; | ||
| 7 | + | ||
| 8 | +@Schema(description = "管理后台 - 回退流程任务的 Request VO") | ||
| 9 | +@Data | ||
| 10 | +public class BpmTaskRollbackReqVO { | ||
| 11 | + | ||
| 12 | + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 13 | + @NotEmpty(message = "任务编号不能为空") | ||
| 14 | + private String id; | ||
| 15 | + | ||
| 16 | + @Schema(description = "回退到的任务Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 17 | + @NotEmpty(message = "回退到的任务Key不能为空") | ||
| 18 | + private String targetDefinitionKey; | ||
| 19 | + | ||
| 20 | + @Schema(description = "回退意见") | ||
| 21 | + private String reason; | ||
| 22 | + | ||
| 23 | +} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRollbackRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * | ||
| 8 | + */ | ||
| 9 | +@Schema(description = "管理后台 - 流程任务的 可回退的节点 Response VO") | ||
| 10 | +@Data | ||
| 11 | +public class BpmTaskRollbackRespVO { | ||
| 12 | + | ||
| 13 | + @Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one") | ||
| 14 | + private String taskDefinitionKey; | ||
| 15 | + | ||
| 16 | + @Schema(description = "任务名词", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批") | ||
| 17 | + private String name; | ||
| 18 | +} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java
| @@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils; | @@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||
| 6 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 6 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 7 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO; | 7 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO; |
| 8 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; | 8 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; |
| 9 | +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRollbackRespVO; | ||
| 9 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO; | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO; |
| 10 | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | 11 | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; |
| 11 | import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; | 12 | import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; |
| 12 | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | 13 | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| 13 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 14 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 15 | +import org.flowable.bpmn.model.FlowElement; | ||
| 14 | import org.flowable.common.engine.impl.db.SuspensionState; | 16 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 15 | import org.flowable.engine.history.HistoricProcessInstance; | 17 | import org.flowable.engine.history.HistoricProcessInstance; |
| 16 | import org.flowable.engine.runtime.ProcessInstance; | 18 | import org.flowable.engine.runtime.ProcessInstance; |
| @@ -55,8 +57,8 @@ public interface BpmTaskConvert { | @@ -55,8 +57,8 @@ public interface BpmTaskConvert { | ||
| 55 | } | 57 | } |
| 56 | 58 | ||
| 57 | default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks, | 59 | default List<BpmTaskDonePageItemRespVO> convertList2(List<HistoricTaskInstance> tasks, |
| 58 | - Map<String, BpmTaskExtDO> bpmTaskExtDOMap, Map<String, HistoricProcessInstance> historicProcessInstanceMap, | ||
| 59 | - Map<Long, AdminUserRespDTO> userMap) { | 60 | + Map<String, BpmTaskExtDO> bpmTaskExtDOMap, Map<String, HistoricProcessInstance> historicProcessInstanceMap, |
| 61 | + Map<Long, AdminUserRespDTO> userMap) { | ||
| 60 | return CollectionUtils.convertList(tasks, task -> { | 62 | return CollectionUtils.convertList(tasks, task -> { |
| 61 | BpmTaskDonePageItemRespVO respVO = convert2(task); | 63 | BpmTaskDonePageItemRespVO respVO = convert2(task); |
| 62 | BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId()); | 64 | BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId()); |
| @@ -82,8 +84,8 @@ public interface BpmTaskConvert { | @@ -82,8 +84,8 @@ public interface BpmTaskConvert { | ||
| 82 | BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser); | 84 | BpmTaskTodoPageItemRespVO.ProcessInstance convert(ProcessInstance processInstance, AdminUserRespDTO startUser); |
| 83 | 85 | ||
| 84 | default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks, | 86 | default List<BpmTaskRespVO> convertList3(List<HistoricTaskInstance> tasks, |
| 85 | - Map<String, BpmTaskExtDO> bpmTaskExtDOMap, HistoricProcessInstance processInstance, | ||
| 86 | - Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) { | 87 | + Map<String, BpmTaskExtDO> bpmTaskExtDOMap, HistoricProcessInstance processInstance, |
| 88 | + Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) { | ||
| 87 | return CollectionUtils.convertList(tasks, task -> { | 89 | return CollectionUtils.convertList(tasks, task -> { |
| 88 | BpmTaskRespVO respVO = convert3(task); | 90 | BpmTaskRespVO respVO = convert3(task); |
| 89 | BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId()); | 91 | BpmTaskExtDO taskExtDO = bpmTaskExtDOMap.get(task.getId()); |
| @@ -113,29 +115,35 @@ public interface BpmTaskConvert { | @@ -113,29 +115,35 @@ public interface BpmTaskConvert { | ||
| 113 | void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to); | 115 | void copyTo(BpmTaskExtDO from, @MappingTarget BpmTaskDonePageItemRespVO to); |
| 114 | 116 | ||
| 115 | @Mappings({@Mapping(source = "processInstance.id", target = "id"), | 117 | @Mappings({@Mapping(source = "processInstance.id", target = "id"), |
| 116 | - @Mapping(source = "processInstance.name", target = "name"), | ||
| 117 | - @Mapping(source = "processInstance.startUserId", target = "startUserId"), | ||
| 118 | - @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"), | ||
| 119 | - @Mapping(source = "startUser.nickname", target = "startUserNickname")}) | 118 | + @Mapping(source = "processInstance.name", target = "name"), |
| 119 | + @Mapping(source = "processInstance.startUserId", target = "startUserId"), | ||
| 120 | + @Mapping(source = "processInstance.processDefinitionId", target = "processDefinitionId"), | ||
| 121 | + @Mapping(source = "startUser.nickname", target = "startUserNickname")}) | ||
| 120 | BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, | 122 | BpmTaskTodoPageItemRespVO.ProcessInstance convert(HistoricProcessInstance processInstance, |
| 121 | - AdminUserRespDTO startUser); | 123 | + AdminUserRespDTO startUser); |
| 122 | 124 | ||
| 123 | default BpmTaskExtDO convert2TaskExt(Task task) { | 125 | default BpmTaskExtDO convert2TaskExt(Task task) { |
| 124 | BpmTaskExtDO taskExtDO = new BpmTaskExtDO().setTaskId(task.getId()) | 126 | BpmTaskExtDO taskExtDO = new BpmTaskExtDO().setTaskId(task.getId()) |
| 125 | - .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName()) | ||
| 126 | - .setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId()); | 127 | + .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName()) |
| 128 | + .setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId()); | ||
| 127 | taskExtDO.setCreateTime(LocalDateTimeUtil.of(task.getCreateTime())); | 129 | taskExtDO.setCreateTime(LocalDateTimeUtil.of(task.getCreateTime())); |
| 128 | return taskExtDO; | 130 | return taskExtDO; |
| 129 | } | 131 | } |
| 130 | 132 | ||
| 131 | default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, | 133 | default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, |
| 132 | - Task task) { | 134 | + Task task) { |
| 133 | BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO(); | 135 | BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO(); |
| 134 | reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId()) | 136 | reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId()) |
| 135 | - .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId()) | ||
| 136 | - .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName()) | ||
| 137 | - .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())); | 137 | + .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId()) |
| 138 | + .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName()) | ||
| 139 | + .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())); | ||
| 138 | return reqDTO; | 140 | return reqDTO; |
| 139 | } | 141 | } |
| 140 | 142 | ||
| 143 | + @Mapping(source = "taskDefinitionKey", target = "id") | ||
| 144 | + default List<BpmTaskRollbackRespVO> convertList(List<FlowElement> elementList) { | ||
| 145 | + return CollectionUtils.convertList(elementList, element -> new BpmTaskRollbackRespVO() | ||
| 146 | + .setName(element.getName()) | ||
| 147 | + .setTaskDefinitionKey(element.getId())); | ||
| 148 | + } | ||
| 141 | } | 149 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelService.java
| @@ -74,4 +74,12 @@ public interface BpmModelService { | @@ -74,4 +74,12 @@ public interface BpmModelService { | ||
| 74 | */ | 74 | */ |
| 75 | BpmnModel getBpmnModel(String id); | 75 | BpmnModel getBpmnModel(String id); |
| 76 | 76 | ||
| 77 | + /** | ||
| 78 | + * 获得流程定义编号对应的 BPMN Model | ||
| 79 | + * | ||
| 80 | + * @param processDefinitionId 流程定义编号 | ||
| 81 | + * @return BPMN Model | ||
| 82 | + */ | ||
| 83 | + BpmnModel getBpmnModelByDefinitionId(String processDefinitionId); | ||
| 84 | + | ||
| 77 | } | 85 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmModelServiceImpl.java
| @@ -233,6 +233,11 @@ public class BpmModelServiceImpl implements BpmModelService { | @@ -233,6 +233,11 @@ public class BpmModelServiceImpl implements BpmModelService { | ||
| 233 | return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true); | 233 | return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), true, true); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | + @Override | ||
| 237 | + public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) { | ||
| 238 | + return repositoryService.getBpmnModel(processDefinitionId); | ||
| 239 | + } | ||
| 240 | + | ||
| 236 | private void checkKeyNCName(String key) { | 241 | private void checkKeyNCName(String key) { |
| 237 | if (!ValidationUtils.isXmlNCName(key)) { | 242 | if (!ValidationUtils.isXmlNCName(key)) { |
| 238 | throw exception(MODEL_KEY_VALID); | 243 | throw exception(MODEL_KEY_VALID); |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| 1 | package cn.iocoder.yudao.module.bpm.service.task; | 1 | package cn.iocoder.yudao.module.bpm.service.task; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 4 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 6 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | 5 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| @@ -128,4 +127,17 @@ public interface BpmTaskService { | @@ -128,4 +127,17 @@ public interface BpmTaskService { | ||
| 128 | */ | 127 | */ |
| 129 | void updateTaskExtAssign(Task task); | 128 | void updateTaskExtAssign(Task task); |
| 130 | 129 | ||
| 130 | + /** | ||
| 131 | + * 获取当前人物的可回退的流程集合 | ||
| 132 | + * @param taskId 当前的任务ID | ||
| 133 | + * @return 可以回退的节点列表 | ||
| 134 | + */ | ||
| 135 | + List<BpmTaskRollbackRespVO> findReturnTaskList(String taskId); | ||
| 136 | + | ||
| 137 | + /** | ||
| 138 | + * 将任务回退到指定的 targetDefinitionKey 位置 | ||
| 139 | + * @param reqVO 回退的任务key和当前所在的任务ID | ||
| 140 | + */ | ||
| 141 | + void taskReturn(BpmTaskRollbackReqVO reqVO); | ||
| 142 | + | ||
| 131 | } | 143 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -8,21 +8,33 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | @@ -8,21 +8,33 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 8 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; | 8 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 9 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 9 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 10 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; | 10 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 11 | +import cn.iocoder.yudao.framework.flowable.core.util.ModelUtils; | ||
| 11 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | 12 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| 12 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; | 13 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 13 | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | 14 | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; |
| 14 | import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; | 15 | import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; |
| 15 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; | 16 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; |
| 16 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; | 17 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; |
| 18 | +import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; | ||
| 17 | import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; | 19 | import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; |
| 18 | import cn.iocoder.yudao.module.system.api.dept.DeptApi; | 20 | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| 19 | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | 21 | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| 20 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | 22 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 21 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 23 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 22 | import lombok.extern.slf4j.Slf4j; | 24 | import lombok.extern.slf4j.Slf4j; |
| 25 | +import org.flowable.bpmn.constants.BpmnXMLConstants; | ||
| 26 | +import org.flowable.bpmn.model.BpmnModel; | ||
| 27 | +import org.flowable.bpmn.model.FlowElement; | ||
| 28 | +import org.flowable.bpmn.model.UserTask; | ||
| 29 | +import org.flowable.common.engine.api.FlowableException; | ||
| 30 | +import org.flowable.common.engine.api.FlowableObjectNotFoundException; | ||
| 23 | import org.flowable.engine.HistoryService; | 31 | import org.flowable.engine.HistoryService; |
| 32 | +import org.flowable.engine.RepositoryService; | ||
| 33 | +import org.flowable.engine.RuntimeService; | ||
| 24 | import org.flowable.engine.TaskService; | 34 | import org.flowable.engine.TaskService; |
| 35 | +import org.flowable.engine.history.HistoricActivityInstance; | ||
| 25 | import org.flowable.engine.history.HistoricProcessInstance; | 36 | import org.flowable.engine.history.HistoricProcessInstance; |
| 37 | +import org.flowable.engine.repository.ProcessDefinition; | ||
| 26 | import org.flowable.engine.runtime.ProcessInstance; | 38 | import org.flowable.engine.runtime.ProcessInstance; |
| 27 | import org.flowable.task.api.Task; | 39 | import org.flowable.task.api.Task; |
| 28 | import org.flowable.task.api.TaskQuery; | 40 | import org.flowable.task.api.TaskQuery; |
| @@ -39,8 +51,7 @@ import java.time.LocalDateTime; | @@ -39,8 +51,7 @@ import java.time.LocalDateTime; | ||
| 39 | import java.util.*; | 51 | import java.util.*; |
| 40 | 52 | ||
| 41 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 53 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 42 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | ||
| 43 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | 54 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 44 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | 55 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; |
| 45 | 56 | ||
| 46 | /** | 57 | /** |
| @@ -68,12 +79,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -68,12 +79,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 68 | private BpmTaskExtMapper taskExtMapper; | 79 | private BpmTaskExtMapper taskExtMapper; |
| 69 | @Resource | 80 | @Resource |
| 70 | private BpmMessageService messageService; | 81 | private BpmMessageService messageService; |
| 82 | + @Resource | ||
| 83 | + private BpmModelService bpmModelService; | ||
| 84 | + @Resource | ||
| 85 | + private RuntimeService runtimeService; | ||
| 86 | + | ||
| 71 | 87 | ||
| 72 | @Override | 88 | @Override |
| 73 | public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { | 89 | public PageResult<BpmTaskTodoPageItemRespVO> getTodoTaskPage(Long userId, BpmTaskTodoPageReqVO pageVO) { |
| 74 | // 查询待办任务 | 90 | // 查询待办任务 |
| 75 | TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(String.valueOf(userId)) // 分配给自己 | 91 | TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(String.valueOf(userId)) // 分配给自己 |
| 76 | - .orderByTaskCreateTime().desc(); // 创建时间倒序 | 92 | + .orderByTaskCreateTime().desc(); // 创建时间倒序 |
| 77 | if (StrUtil.isNotBlank(pageVO.getName())) { | 93 | if (StrUtil.isNotBlank(pageVO.getName())) { |
| 78 | taskQuery.taskNameLike("%" + pageVO.getName() + "%"); | 94 | taskQuery.taskNameLike("%" + pageVO.getName() + "%"); |
| 79 | } | 95 | } |
| @@ -91,21 +107,21 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -91,21 +107,21 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 91 | 107 | ||
| 92 | // 获得 ProcessInstance Map | 108 | // 获得 ProcessInstance Map |
| 93 | Map<String, ProcessInstance> processInstanceMap = | 109 | Map<String, ProcessInstance> processInstanceMap = |
| 94 | - processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId)); | 110 | + processInstanceService.getProcessInstanceMap(convertSet(tasks, Task::getProcessInstanceId)); |
| 95 | // 获得 User Map | 111 | // 获得 User Map |
| 96 | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | 112 | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| 97 | - convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); | 113 | + convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); |
| 98 | // 拼接结果 | 114 | // 拼接结果 |
| 99 | return new PageResult<>(BpmTaskConvert.INSTANCE.convertList1(tasks, processInstanceMap, userMap), | 115 | return new PageResult<>(BpmTaskConvert.INSTANCE.convertList1(tasks, processInstanceMap, userMap), |
| 100 | - taskQuery.count()); | 116 | + taskQuery.count()); |
| 101 | } | 117 | } |
| 102 | 118 | ||
| 103 | @Override | 119 | @Override |
| 104 | public PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) { | 120 | public PageResult<BpmTaskDonePageItemRespVO> getDoneTaskPage(Long userId, BpmTaskDonePageReqVO pageVO) { |
| 105 | // 查询已办任务 | 121 | // 查询已办任务 |
| 106 | HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery().finished() // 已完成 | 122 | HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery().finished() // 已完成 |
| 107 | - .taskAssignee(String.valueOf(userId)) // 分配给自己 | ||
| 108 | - .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 | 123 | + .taskAssignee(String.valueOf(userId)) // 分配给自己 |
| 124 | + .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 | ||
| 109 | if (StrUtil.isNotBlank(pageVO.getName())) { | 125 | if (StrUtil.isNotBlank(pageVO.getName())) { |
| 110 | taskQuery.taskNameLike("%" + pageVO.getName() + "%"); | 126 | taskQuery.taskNameLike("%" + pageVO.getName() + "%"); |
| 111 | } | 127 | } |
| @@ -123,19 +139,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -123,19 +139,19 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 123 | 139 | ||
| 124 | // 获得 TaskExtDO Map | 140 | // 获得 TaskExtDO Map |
| 125 | List<BpmTaskExtDO> bpmTaskExtDOs = | 141 | List<BpmTaskExtDO> bpmTaskExtDOs = |
| 126 | - taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId)); | 142 | + taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId)); |
| 127 | Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId); | 143 | Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId); |
| 128 | // 获得 ProcessInstance Map | 144 | // 获得 ProcessInstance Map |
| 129 | Map<String, HistoricProcessInstance> historicProcessInstanceMap = | 145 | Map<String, HistoricProcessInstance> historicProcessInstanceMap = |
| 130 | - processInstanceService.getHistoricProcessInstanceMap( | ||
| 131 | - convertSet(tasks, HistoricTaskInstance::getProcessInstanceId)); | 146 | + processInstanceService.getHistoricProcessInstanceMap( |
| 147 | + convertSet(tasks, HistoricTaskInstance::getProcessInstanceId)); | ||
| 132 | // 获得 User Map | 148 | // 获得 User Map |
| 133 | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | 149 | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| 134 | - convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); | 150 | + convertSet(historicProcessInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); |
| 135 | // 拼接结果 | 151 | // 拼接结果 |
| 136 | return new PageResult<>( | 152 | return new PageResult<>( |
| 137 | - BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap), | ||
| 138 | - taskQuery.count()); | 153 | + BpmTaskConvert.INSTANCE.convertList2(tasks, bpmTaskExtDOMap, historicProcessInstanceMap, userMap), |
| 154 | + taskQuery.count()); | ||
| 139 | } | 155 | } |
| 140 | 156 | ||
| 141 | @Override | 157 | @Override |
| @@ -189,8 +205,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -189,8 +205,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 189 | 205 | ||
| 190 | // 更新任务拓展表为通过 | 206 | // 更新任务拓展表为通过 |
| 191 | taskExtMapper.updateByTaskId( | 207 | taskExtMapper.updateByTaskId( |
| 192 | - new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) | ||
| 193 | - .setReason(reqVO.getReason())); | 208 | + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) |
| 209 | + .setReason(reqVO.getReason())); | ||
| 194 | } | 210 | } |
| 195 | 211 | ||
| 196 | @Override | 212 | @Override |
| @@ -208,8 +224,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -208,8 +224,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 208 | 224 | ||
| 209 | // 更新任务拓展表为不通过 | 225 | // 更新任务拓展表为不通过 |
| 210 | taskExtMapper.updateByTaskId( | 226 | taskExtMapper.updateByTaskId( |
| 211 | - new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult()) | ||
| 212 | - .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason())); | 227 | + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult()) |
| 228 | + .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason())); | ||
| 213 | } | 229 | } |
| 214 | 230 | ||
| 215 | @Override | 231 | @Override |
| @@ -245,7 +261,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -245,7 +261,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 245 | @Override | 261 | @Override |
| 246 | public void createTaskExt(Task task) { | 262 | public void createTaskExt(Task task) { |
| 247 | BpmTaskExtDO taskExtDO = | 263 | BpmTaskExtDO taskExtDO = |
| 248 | - BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); | 264 | + BpmTaskConvert.INSTANCE.convert2TaskExt(task).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); |
| 249 | taskExtMapper.insert(taskExtDO); | 265 | taskExtMapper.insert(taskExtDO); |
| 250 | } | 266 | } |
| 251 | 267 | ||
| @@ -293,17 +309,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -293,17 +309,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 293 | @Override | 309 | @Override |
| 294 | public void updateTaskExtAssign(Task task) { | 310 | public void updateTaskExtAssign(Task task) { |
| 295 | BpmTaskExtDO taskExtDO = | 311 | BpmTaskExtDO taskExtDO = |
| 296 | - new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId()); | 312 | + new BpmTaskExtDO().setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setTaskId(task.getId()); |
| 297 | taskExtMapper.updateByTaskId(taskExtDO); | 313 | taskExtMapper.updateByTaskId(taskExtDO); |
| 298 | // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 | 314 | // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 |
| 299 | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { | 315 | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { |
| 300 | @Override | 316 | @Override |
| 301 | public void afterCommit() { | 317 | public void afterCommit() { |
| 302 | ProcessInstance processInstance = | 318 | ProcessInstance processInstance = |
| 303 | - processInstanceService.getProcessInstance(task.getProcessInstanceId()); | 319 | + processInstanceService.getProcessInstance(task.getProcessInstanceId()); |
| 304 | AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); | 320 | AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())); |
| 305 | messageService.sendMessageWhenTaskAssigned( | 321 | messageService.sendMessageWhenTaskAssigned( |
| 306 | - BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); | 322 | + BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); |
| 307 | } | 323 | } |
| 308 | }); | 324 | }); |
| 309 | } | 325 | } |
| @@ -316,4 +332,144 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -316,4 +332,144 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 316 | return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult(); | 332 | return historyService.createHistoricTaskInstanceQuery().taskId(id).singleResult(); |
| 317 | } | 333 | } |
| 318 | 334 | ||
| 335 | + @Override | ||
| 336 | + public List<BpmTaskRollbackRespVO> findReturnTaskList(String taskId) { | ||
| 337 | + // 当前任务 task | ||
| 338 | + Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); | ||
| 339 | + // 根据流程定义获取流程模型信息 | ||
| 340 | + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | ||
| 341 | + // 查询该任务的前置任务节点的key集合 | ||
| 342 | + Set<String> historyTaksDefinitionKeySet = getPreTaskByCurrentTask(task, bpmnModel); | ||
| 343 | + if (CollUtil.isEmpty(historyTaksDefinitionKeySet)) { | ||
| 344 | + return Collections.emptyList(); | ||
| 345 | + } | ||
| 346 | + // 获取当前任务节点元素 | ||
| 347 | + FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | ||
| 348 | + List<FlowElement> elementList = new ArrayList<>(); | ||
| 349 | + for (String activityId : historyTaksDefinitionKeySet) { | ||
| 350 | + FlowElement target = ModelUtils.getFlowElementById(bpmnModel, activityId); | ||
| 351 | + //不支持串行和子流程 | ||
| 352 | + boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>()); | ||
| 353 | + if (isSequential) { | ||
| 354 | + elementList.add(target); | ||
| 355 | + } | ||
| 356 | + } | ||
| 357 | + return BpmTaskConvert.INSTANCE.convertList(elementList); | ||
| 358 | + } | ||
| 359 | + | ||
| 360 | + /** | ||
| 361 | + * 查询当前流程实例符合条件可回退的 taskDefinitionKey 集合 | ||
| 362 | + * | ||
| 363 | + * @param task 当前任务 | ||
| 364 | + * @param bpmnModel 当前流程定义对应的流程模型 | ||
| 365 | + * @return 符合条件的已去重的 taskDefinitionKey集合 | ||
| 366 | + */ | ||
| 367 | + private Set<String> getPreTaskByCurrentTask(Task task, BpmnModel bpmnModel) { | ||
| 368 | + // 获取当前任务节点元素 | ||
| 369 | + FlowElement source = ModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); | ||
| 370 | + //拿到当前任务节点的前置节点集合 | ||
| 371 | + List<UserTask> preUserTaskList = ModelUtils.getPreUserTaskList(source, null, null); | ||
| 372 | + //需要保证这些节点都是在该source节点之前的 | ||
| 373 | + if (CollUtil.isNotEmpty(preUserTaskList)) { | ||
| 374 | + return convertSet(preUserTaskList, UserTask::getId); | ||
| 375 | + } | ||
| 376 | + return Collections.emptySet(); | ||
| 377 | + } | ||
| 378 | + | ||
| 379 | + @Transactional(rollbackFor = Exception.class) | ||
| 380 | + @Override | ||
| 381 | + public void taskReturn(BpmTaskRollbackReqVO reqVO) { | ||
| 382 | + // 当前任务 task | ||
| 383 | + Task task = validateRollback(reqVO.getId()); | ||
| 384 | + // 校验源头和目标节点的关系,并返回目标元素 | ||
| 385 | + FlowElement targetElement = validateRollbackProcessDefinition(task.getTaskDefinitionKey(), reqVO.getTargetDefinitionKey(), task.getProcessDefinitionId()); | ||
| 386 | + //调用flowable框架的回退逻辑 | ||
| 387 | + this.handlerRollback(task, targetElement, reqVO); | ||
| 388 | + // 更新任务扩展表 | ||
| 389 | + taskExtMapper.updateByTaskId( | ||
| 390 | + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.BACK.getResult()) | ||
| 391 | + .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason())); | ||
| 392 | + } | ||
| 393 | + | ||
| 394 | + /** | ||
| 395 | + * 校验当前流程是否可以操作回退 | ||
| 396 | + * | ||
| 397 | + * @param taskId 当前任务ID | ||
| 398 | + * @return | ||
| 399 | + */ | ||
| 400 | + private Task validateRollback(String taskId) { | ||
| 401 | + // 当前任务 task | ||
| 402 | + Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); | ||
| 403 | + if (null == task) { | ||
| 404 | + throw exception(TASK_NOT_EXISTS); | ||
| 405 | + } | ||
| 406 | + if (task.isSuspended()) { | ||
| 407 | + throw exception(TASK_IS_NOT_ACTIVITY); | ||
| 408 | + } | ||
| 409 | + return task; | ||
| 410 | + } | ||
| 411 | + | ||
| 412 | + /** | ||
| 413 | + * 回退流程节点时,校验源头节点和目标节点的关系 | ||
| 414 | + * | ||
| 415 | + * @param sourceKey 当前任务节点Key | ||
| 416 | + * @param targetKey 目标任务节点key | ||
| 417 | + * @param processDefinitionId 当前流程定义ID | ||
| 418 | + * @return 目标元素 | ||
| 419 | + */ | ||
| 420 | + private FlowElement validateRollbackProcessDefinition(String sourceKey, String targetKey, String processDefinitionId) { | ||
| 421 | + // 获取流程模型信息 | ||
| 422 | + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId); | ||
| 423 | + // 获取当前任务节点元素 | ||
| 424 | + FlowElement source = ModelUtils.getFlowElementById(bpmnModel, sourceKey); | ||
| 425 | + // 获取跳转的节点元素 | ||
| 426 | + FlowElement target = ModelUtils.getFlowElementById(bpmnModel, targetKey); | ||
| 427 | + if (null == target) { | ||
| 428 | + throw exception(TASK_TARGET_NODE_NOT_EXISTS); | ||
| 429 | + } | ||
| 430 | + // 从当前节点向前扫描,判断当前节点与目标节点是否属于串行,若目标节点是在并行网关上或非同一路线上,不可跳转 | ||
| 431 | + boolean isSequential = ModelUtils.isSequentialReachable(source, target, new HashSet<>()); | ||
| 432 | + if (!isSequential) { | ||
| 433 | + throw exception(TASK_SOURCE_TARGET_ERROR); | ||
| 434 | + } | ||
| 435 | + return target; | ||
| 436 | + } | ||
| 437 | + | ||
| 438 | + /** | ||
| 439 | + * 处理回退逻辑 | ||
| 440 | + * | ||
| 441 | + * @param task 当前回退的任务 | ||
| 442 | + * @param targetElement 需要回退到的目标任务 | ||
| 443 | + * @param reqVO 前端参数封装 | ||
| 444 | + */ | ||
| 445 | + public void handlerRollback(Task task, FlowElement targetElement, BpmTaskRollbackReqVO reqVO) { | ||
| 446 | + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 | ||
| 447 | + List<Task> runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); | ||
| 448 | + List<String> runTaskKeyList = convertList(runTaskList, Task::getTaskDefinitionKey); | ||
| 449 | + // 通过 targetElement 的出口连线,结合 runTaskList 比对,获取需要撤回的任务 key | ||
| 450 | + List<UserTask> rollbackUserTaskList = ModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); | ||
| 451 | + // 需退回任务 key | ||
| 452 | + List<String> rollbackTaskDefinitionKeyList = convertList(rollbackUserTaskList, UserTask::getId); | ||
| 453 | + | ||
| 454 | + // 通过 key 和 runTaskList 中的key对比,拿到任务ID,设置设置回退意见 | ||
| 455 | + List<String> currentTaskIds = new ArrayList<>(); | ||
| 456 | + rollbackTaskDefinitionKeyList.forEach(currentId -> runTaskList.forEach(runTask -> { | ||
| 457 | + if (currentId.equals(runTask.getTaskDefinitionKey())) { | ||
| 458 | + currentTaskIds.add(runTask.getId()); | ||
| 459 | + } | ||
| 460 | + })); | ||
| 461 | + if (CollUtil.isEmpty(currentTaskIds)) { | ||
| 462 | + throw exception(TASK_ROLLBACK_FAIL); | ||
| 463 | + } | ||
| 464 | + // 设置回退意见 | ||
| 465 | + for (String currentTaskId : currentTaskIds) { | ||
| 466 | + taskService.addComment(currentTaskId, task.getProcessInstanceId(), BpmProcessInstanceResultEnum.BACK.getResult().toString(), reqVO.getReason()); | ||
| 467 | + } | ||
| 468 | + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) | ||
| 469 | + runtimeService.createChangeActivityStateBuilder() | ||
| 470 | + .processInstanceId(task.getProcessInstanceId()) | ||
| 471 | + .moveActivityIdsToSingleActivityId(rollbackTaskDefinitionKeyList, reqVO.getTargetDefinitionKey()) | ||
| 472 | + .changeState(); | ||
| 473 | + } | ||
| 474 | + | ||
| 319 | } | 475 | } |