Commit 1f2222cf83934529cc5cd576ce8af5b417d668c6
Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm
Showing
9 changed files
with
75 additions
and
14 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java
| ... | ... | @@ -25,6 +25,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> { |
| 25 | 25 | START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定 |
| 26 | 26 | APPROVE_NODE(11, "审批人", "userTask"), |
| 27 | 27 | COPY_NODE(12, "抄送人", "serviceTask"), |
| 28 | + TRANSACTOR_NODE(13, "办理人", "userTask"), | |
| 28 | 29 | |
| 29 | 30 | DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), |
| 30 | 31 | TRIGGER_NODE(15, "触发器", "serviceTask"), | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.common.core.KeyValue; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 5 | 4 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum; |
| 6 | 5 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum; |
| ... | ... | @@ -27,8 +26,7 @@ import java.util.List; |
| 27 | 26 | @Data |
| 28 | 27 | public class BpmModelMetaInfoVO { |
| 29 | 28 | |
| 30 | - @Schema(description = "流程图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/yudao.jpg") | |
| 31 | - @NotEmpty(message = "流程图标不能为空") | |
| 29 | + @Schema(description = "流程图标", example = "https://www.iocoder.cn/yudao.jpg") | |
| 32 | 30 | @URL(message = "流程图标格式不正确") |
| 33 | 31 | private String icon; |
| 34 | 32 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java
| ... | ... | @@ -85,6 +85,9 @@ public class BpmTaskRespVO { |
| 85 | 85 | @Schema(description = "是否填写审批意见", example = "false") |
| 86 | 86 | private Boolean reasonRequire; |
| 87 | 87 | |
| 88 | + @Schema(description = "节点类型", example = "10") | |
| 89 | + private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。 | |
| 90 | + | |
| 88 | 91 | @Data |
| 89 | 92 | @Schema(description = "流程实例") |
| 90 | 93 | public static class ProcessInstance { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.framework.flowable.core.enums; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum; | |
| 4 | + | |
| 3 | 5 | /** |
| 4 | 6 | * BPMN XML 常量信息 |
| 5 | 7 | * |
| ... | ... | @@ -129,4 +131,11 @@ public interface BpmnModelConstants { |
| 129 | 131 | */ |
| 130 | 132 | String REASON_REQUIRE = "reasonRequire"; |
| 131 | 133 | |
| 134 | + /** | |
| 135 | + * 节点类型 | |
| 136 | + * | |
| 137 | + * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性,用于区分是审批节点、还是办理节点 | |
| 138 | + */ | |
| 139 | + String NODE_TYPE = "nodeType"; | |
| 140 | + | |
| 132 | 141 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| ... | ... | @@ -410,6 +410,26 @@ public class BpmnModelUtils { |
| 410 | 410 | return parseExtensionElement(flowElement, TRIGGER_PARAM); |
| 411 | 411 | } |
| 412 | 412 | |
| 413 | + /** | |
| 414 | + * 给节点添加节点类型 | |
| 415 | + * | |
| 416 | + * @param nodeType 节点类型 | |
| 417 | + * @param flowElement 节点 | |
| 418 | + */ | |
| 419 | + public static void addNodeType(Integer nodeType, FlowElement flowElement) { | |
| 420 | + addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType); | |
| 421 | + } | |
| 422 | + | |
| 423 | + /** | |
| 424 | + * 解析节点类型 | |
| 425 | + * | |
| 426 | + * @param flowElement 节点 | |
| 427 | + * @return 节点类型 | |
| 428 | + */ | |
| 429 | + public static Integer parseNodeType(FlowElement flowElement) { | |
| 430 | + return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE)); | |
| 431 | + } | |
| 432 | + | |
| 413 | 433 | // ========== BPM 简单查找相关的方法 ========== |
| 414 | 434 | |
| 415 | 435 | /** | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| ... | ... | @@ -40,7 +40,7 @@ public class SimpleModelUtils { |
| 40 | 40 | |
| 41 | 41 | static { |
| 42 | 42 | List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(), |
| 43 | - new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), | |
| 43 | + new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), | |
| 44 | 44 | new DelayTimerNodeConvert(), new TriggerNodeConvert(), |
| 45 | 45 | new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert()); |
| 46 | 46 | converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); |
| ... | ... | @@ -445,6 +445,8 @@ public class SimpleModelUtils { |
| 445 | 445 | addSignEnable(node.getSignEnable(), userTask); |
| 446 | 446 | // 审批意见 |
| 447 | 447 | addReasonRequire(node.getReasonRequire(), userTask); |
| 448 | + // 节点类型 | |
| 449 | + addNodeType(node.getType(), userTask); | |
| 448 | 450 | return userTask; |
| 449 | 451 | } |
| 450 | 452 | |
| ... | ... | @@ -514,6 +516,15 @@ public class SimpleModelUtils { |
| 514 | 516 | |
| 515 | 517 | } |
| 516 | 518 | |
| 519 | + private static class TransactorNodeConvert extends ApproveNodeConvert { | |
| 520 | + | |
| 521 | + @Override | |
| 522 | + public BpmSimpleModelNodeTypeEnum getType() { | |
| 523 | + return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; | |
| 524 | + } | |
| 525 | + | |
| 526 | + } | |
| 527 | + | |
| 517 | 528 | private static class CopyNodeConvert implements NodeConvert { |
| 518 | 529 | |
| 519 | 530 | @Override |
| ... | ... | @@ -785,10 +796,11 @@ public class SimpleModelUtils { |
| 785 | 796 | BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType()); |
| 786 | 797 | Assert.notNull(nodeType, "模型节点类型不支持"); |
| 787 | 798 | |
| 788 | - // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE | |
| 799 | + // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE | |
| 789 | 800 | if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE |
| 790 | 801 | || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE |
| 791 | 802 | || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE |
| 803 | + || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE | |
| 792 | 804 | || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE |
| 793 | 805 | || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { |
| 794 | 806 | // 添加元素 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
| ... | ... | @@ -28,8 +28,7 @@ import java.util.*; |
| 28 | 28 | |
| 29 | 29 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 30 | 30 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.addIfNotNull; |
| 31 | -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_KEY_NOT_MATCH; | |
| 32 | -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_DEFINITION_NAME_NOT_MATCH; | |
| 31 | +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; | |
| 33 | 32 | import static java.util.Collections.emptyList; |
| 34 | 33 | |
| 35 | 34 | /** |
| ... | ... | @@ -156,16 +155,25 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ |
| 156 | 155 | |
| 157 | 156 | @Override |
| 158 | 157 | public void updateProcessDefinitionState(String id, Integer state) { |
| 158 | + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id); | |
| 159 | + if (processDefinition == null) { | |
| 160 | + throw exception(PROCESS_DEFINITION_NOT_EXISTS); | |
| 161 | + } | |
| 162 | + | |
| 159 | 163 | // 激活 |
| 160 | 164 | if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { |
| 161 | - repositoryService.activateProcessDefinitionById(id, false, null); | |
| 165 | + if (processDefinition.isSuspended()) { | |
| 166 | + repositoryService.activateProcessDefinitionById(id, false, null); | |
| 167 | + } | |
| 162 | 168 | return; |
| 163 | 169 | } |
| 164 | 170 | // 挂起 |
| 165 | 171 | if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { |
| 166 | 172 | // suspendProcessInstances = false,进行中的任务,不进行挂起。 |
| 167 | 173 | // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 |
| 168 | - repositoryService.suspendProcessDefinitionById(id, false, null); | |
| 174 | + if (!processDefinition.isSuspended()) { | |
| 175 | + repositoryService.suspendProcessDefinitionById(id, false, null); | |
| 176 | + } | |
| 169 | 177 | return; |
| 170 | 178 | } |
| 171 | 179 | log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil; |
| 5 | 5 | import cn.hutool.core.date.DateUtil; |
| 6 | 6 | import cn.hutool.core.lang.Assert; |
| 7 | 7 | import cn.hutool.core.util.ArrayUtil; |
| 8 | +import cn.hutool.core.util.ObjUtil; | |
| 8 | 9 | import cn.hutool.core.util.ObjectUtil; |
| 9 | 10 | import cn.hutool.core.util.StrUtil; |
| 10 | 11 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| ... | ... | @@ -67,6 +68,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. |
| 67 | 68 | import static cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; |
| 68 | 69 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; |
| 69 | 70 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; |
| 71 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; | |
| 70 | 72 | import static java.util.Arrays.asList; |
| 71 | 73 | import static java.util.Collections.singletonList; |
| 72 | 74 | import static org.flowable.bpmn.constants.BpmnXMLConstants.*; |
| ... | ... | @@ -325,7 +327,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 325 | 327 | ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) |
| 326 | 328 | .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) |
| 327 | 329 | ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() |
| 328 | - : BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) | |
| 330 | + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 | |
| 331 | + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) | |
| 329 | 332 | .setStatus(FlowableUtils.getTaskStatus(task)) |
| 330 | 333 | .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) |
| 331 | 334 | .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) |
| ... | ... | @@ -402,7 +405,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 402 | 405 | HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 |
| 403 | 406 | ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) |
| 404 | 407 | .setName(firstActivity.getActivityName()) |
| 405 | - .setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) | |
| 408 | + .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 | |
| 409 | + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) | |
| 406 | 410 | .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) |
| 407 | 411 | .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) |
| 408 | 412 | .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) |
| ... | ... | @@ -479,7 +483,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 479 | 483 | // 1. 开始节点/审批节点 |
| 480 | 484 | if (ObjectUtils.equalsAny(node.getType(), |
| 481 | 485 | BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(), |
| 482 | - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) { | |
| 486 | + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(), | |
| 487 | + BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) { | |
| 483 | 488 | List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), |
| 484 | 489 | startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); |
| 485 | 490 | activityNode.setCandidateUserIds(candidateUserIds); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -61,6 +61,7 @@ import java.util.stream.Stream; |
| 61 | 61 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 62 | 62 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| 63 | 63 | import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; |
| 64 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; | |
| 64 | 65 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; |
| 65 | 66 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; |
| 66 | 67 | |
| ... | ... | @@ -162,6 +163,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 162 | 163 | bpmnModel, todoTask.getTaskDefinitionKey()); |
| 163 | 164 | Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); |
| 164 | 165 | Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey()); |
| 166 | + Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey())); | |
| 165 | 167 | |
| 166 | 168 | // 4. 任务表单 |
| 167 | 169 | BpmFormDO taskForm = null; |
| ... | ... | @@ -170,8 +172,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 170 | 172 | } |
| 171 | 173 | |
| 172 | 174 | return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) |
| 173 | - .setSignEnable(signEnable) | |
| 174 | - .setReasonRequire(reasonRequire); | |
| 175 | + .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); | |
| 175 | 176 | } |
| 176 | 177 | |
| 177 | 178 | @Override |
| ... | ... | @@ -194,6 +195,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 194 | 195 | return PageResult.empty(); |
| 195 | 196 | } |
| 196 | 197 | List<HistoricTaskInstance> tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); |
| 198 | + | |
| 199 | + // 特殊:强制移除自动完成的“发起人”节点 | |
| 200 | + // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 | |
| 201 | + tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); | |
| 197 | 202 | return new PageResult<>(tasks, count); |
| 198 | 203 | } |
| 199 | 204 | ... | ... |