Commit 1784aab186bbbba85c7be2bcb5064283a75501b3
Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm
Showing
12 changed files
with
101 additions
and
84 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| ... | ... | @@ -54,6 +54,7 @@ public interface ErrorCodeConstants { |
| 54 | 54 | ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); |
| 55 | 55 | ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); |
| 56 | 56 | ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); |
| 57 | + ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); | |
| 57 | 58 | |
| 58 | 59 | // ========== 动态表单模块 1-009-010-000 ========== |
| 59 | 60 | ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java
| ... | ... | @@ -33,7 +33,7 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { |
| 33 | 33 | CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), |
| 34 | 34 | PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"), |
| 35 | 35 | INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), |
| 36 | - ROUTE_BRANCH_NODE(54, "路由分支", "exclusiveGateway") | |
| 36 | + ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") | |
| 37 | 37 | ; |
| 38 | 38 | |
| 39 | 39 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray(); |
| ... | ... | @@ -51,7 +51,7 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable { |
| 51 | 51 | return Objects.equals(CONDITION_BRANCH_NODE.getType(), type) |
| 52 | 52 | || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type) |
| 53 | 53 | || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type) |
| 54 | - || Objects.equals(ROUTE_BRANCH_NODE.getType(), type); | |
| 54 | + || Objects.equals(ROUTER_BRANCH_NODE.getType(), type); | |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | public static BpmSimpleModelNodeType valueOf(Integer type) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
| ... | ... | @@ -117,11 +117,10 @@ public class BpmSimpleModelNodeVO { |
| 117 | 117 | private ConditionGroups conditionGroups; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE |
| 118 | 118 | |
| 119 | 119 | @Schema(description = "路由分支组", example = "[]") |
| 120 | - private List<RouteCondition> routerGroups; | |
| 120 | + private List<RouterCondition> routerGroups; | |
| 121 | 121 | |
| 122 | - // TODO @lesan:这个目前前端是随机生成的。可以后端来随机么? | |
| 123 | - @Schema(description = "默认分支 ID", example = "Flow_xxx") | |
| 124 | - private String defaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTE_BRANCH_NODE | |
| 122 | + @Schema(description = "默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成,所以 hidden = true | |
| 123 | + private String defaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE | |
| 125 | 124 | |
| 126 | 125 | @Schema(description = "任务监听器") |
| 127 | 126 | @Valid |
| ... | ... | @@ -290,11 +289,10 @@ public class BpmSimpleModelNodeVO { |
| 290 | 289 | |
| 291 | 290 | } |
| 292 | 291 | |
| 293 | - // TODO @lesan:还有相关的也要改下哈。route 到 router | |
| 294 | 292 | @Schema(description = "路由分支") |
| 295 | 293 | @Data |
| 296 | 294 | @Valid |
| 297 | - public static class RouteCondition { | |
| 295 | + public static class RouterCondition { | |
| 298 | 296 | |
| 299 | 297 | @Schema(description = "节点 Id", example = "Activity_xxx") // 跳转到该节点 |
| 300 | 298 | @NotEmpty(message = "节点 Id 不能为空") | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
| ... | ... | @@ -101,6 +101,10 @@ public class BpmApprovalDetailRespVO { |
| 101 | 101 | @Schema(description = "审批意见", example = "同意") |
| 102 | 102 | private String reason; |
| 103 | 103 | |
| 104 | + // TODO @lesan:改成 signPicUrl 会好点 | |
| 105 | + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") | |
| 106 | + private String sign; | |
| 107 | + | |
| 104 | 108 | } |
| 105 | 109 | |
| 106 | 110 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
| ... | ... | @@ -18,6 +18,10 @@ public class BpmTaskApproveReqVO { |
| 18 | 18 | @NotEmpty(message = "审批意见不能为空") |
| 19 | 19 | private String reason; |
| 20 | 20 | |
| 21 | + // TODO @lesan:改成 signPicUrl 会好点 | |
| 22 | + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") | |
| 23 | + private String sign; | |
| 24 | + | |
| 21 | 25 | @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) |
| 22 | 26 | private Map<String, Object> variables; |
| 23 | 27 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
| ... | ... | @@ -186,7 +186,8 @@ public interface BpmProcessInstanceConvert { |
| 186 | 186 | return null; |
| 187 | 187 | } |
| 188 | 188 | return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class) |
| 189 | - .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)); | |
| 189 | + .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) | |
| 190 | + .setSign(FlowableUtils.getTaskSign(task)); | |
| 190 | 191 | } |
| 191 | 192 | |
| 192 | 193 | default Set<Long> parseUserIds(HistoricProcessInstance processInstance, | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
| ... | ... | @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.convert.Convert; |
| 5 | +import cn.hutool.core.lang.Assert; | |
| 5 | 6 | import cn.hutool.core.map.MapUtil; |
| 6 | 7 | import cn.hutool.core.util.*; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 9 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 8 | 10 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 9 | 11 | import cn.iocoder.yudao.framework.common.util.string.StrUtils; |
| 10 | 12 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| ... | ... | @@ -21,6 +23,7 @@ import org.flowable.bpmn.model.Process; |
| 21 | 23 | import org.flowable.bpmn.model.*; |
| 22 | 24 | import org.flowable.common.engine.api.FlowableException; |
| 23 | 25 | import org.flowable.common.engine.impl.util.io.BytesStreamSource; |
| 26 | +import org.flowable.engine.impl.el.FixedValue; | |
| 24 | 27 | |
| 25 | 28 | import java.util.*; |
| 26 | 29 | |
| ... | ... | @@ -346,12 +349,8 @@ public class BpmnModelUtils { |
| 346 | 349 | } |
| 347 | 350 | |
| 348 | 351 | public static void addSignEnable(Boolean signEnable, FlowElement userTask) { |
| 349 | - // TODO @lesan:是不是改成表达式会好点 addExtensionElement(userTask, SIGN_ENABLE, ObjUtil.isNotNull(signEnable) ? ) | |
| 350 | - if (ObjUtil.isNotNull(signEnable)) { | |
| 351 | - addExtensionElement(userTask, SIGN_ENABLE, signEnable.toString()); | |
| 352 | - } else { | |
| 353 | - addExtensionElement(userTask, SIGN_ENABLE, "false"); | |
| 354 | - } | |
| 352 | + addExtensionElement(userTask, SIGN_ENABLE, | |
| 353 | + ObjUtil.isNotNull(signEnable) ? signEnable.toString() : Boolean.FALSE.toString()); | |
| 355 | 354 | } |
| 356 | 355 | |
| 357 | 356 | public static Boolean parseSignEnable(BpmnModel bpmnModel, String flowElementId) { |
| ... | ... | @@ -366,6 +365,19 @@ public class BpmnModelUtils { |
| 366 | 365 | return Convert.toBool(extensionElements.get(0).getElementText(), false); |
| 367 | 366 | } |
| 368 | 367 | |
| 368 | + public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) { | |
| 369 | + FieldExtension fieldExtension = new FieldExtension(); | |
| 370 | + fieldExtension.setFieldName("listenerConfig"); | |
| 371 | + fieldExtension.setStringValue(JsonUtils.toJsonString(handler)); | |
| 372 | + flowableListener.getFieldExtensions().add(fieldExtension); | |
| 373 | + } | |
| 374 | + | |
| 375 | + public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(FixedValue fixedValue) { | |
| 376 | + String expressionText = fixedValue.getExpressionText(); | |
| 377 | + Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); | |
| 378 | + return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); | |
| 379 | + } | |
| 380 | + | |
| 369 | 381 | // ========== BPM 简单查找相关的方法 ========== |
| 370 | 382 | |
| 371 | 383 | /** | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
| ... | ... | @@ -213,6 +213,11 @@ public class FlowableUtils { |
| 213 | 213 | return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON); |
| 214 | 214 | } |
| 215 | 215 | |
| 216 | + // TODO @lesan:这个方法名,也改咧 | |
| 217 | + public static String getTaskSign(TaskInfo task) { | |
| 218 | + return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_SIGN); | |
| 219 | + } | |
| 220 | + | |
| 216 | 221 | /** |
| 217 | 222 | * 获得任务的表单 |
| 218 | 223 | * | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| ... | ... | @@ -5,7 +5,6 @@ import cn.hutool.core.lang.Assert; |
| 5 | 5 | import cn.hutool.core.map.MapUtil; |
| 6 | 6 | import cn.hutool.core.util.*; |
| 7 | 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 8 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 9 | 8 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 10 | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; |
| 11 | 10 | import cn.iocoder.yudao.module.bpm.enums.definition.*; |
| ... | ... | @@ -188,7 +187,7 @@ public class SimpleModelUtils { |
| 188 | 187 | // 分支终点节点 ID |
| 189 | 188 | String branchEndNodeId = null; |
| 190 | 189 | if (nodeType == BpmSimpleModelNodeType.CONDITION_BRANCH_NODE |
| 191 | - || nodeType == BpmSimpleModelNodeType.ROUTE_BRANCH_NODE) { // 条件分支或路由分支 | |
| 190 | + || nodeType == BpmSimpleModelNodeType.ROUTER_BRANCH_NODE) { // 条件分支或路由分支 | |
| 192 | 191 | // 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点 ID |
| 193 | 192 | branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; |
| 194 | 193 | } else if (nodeType == BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE |
| ... | ... | @@ -199,10 +198,10 @@ public class SimpleModelUtils { |
| 199 | 198 | Assert.notEmpty(branchEndNodeId, "分支终点节点 Id 不能为空"); |
| 200 | 199 | |
| 201 | 200 | // 3. 遍历分支节点 |
| 202 | - if (nodeType == BpmSimpleModelNodeType.ROUTE_BRANCH_NODE) { | |
| 201 | + if (nodeType == BpmSimpleModelNodeType.ROUTER_BRANCH_NODE) { | |
| 203 | 202 | // 路由分支遍历 |
| 204 | - for (BpmSimpleModelNodeVO.RouteCondition route : node.getRouterGroups()) { | |
| 205 | - SequenceFlow sequenceFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), route); | |
| 203 | + for (BpmSimpleModelNodeVO.RouterCondition router : node.getRouterGroups()) { | |
| 204 | + SequenceFlow sequenceFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), router); | |
| 206 | 205 | process.addFlowElement(sequenceFlow); |
| 207 | 206 | } |
| 208 | 207 | } else { |
| ... | ... | @@ -233,7 +232,7 @@ public class SimpleModelUtils { |
| 233 | 232 | SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId); |
| 234 | 233 | process.addFlowElement(sequenceFlow); |
| 235 | 234 | // 4.2 如果是路由分支,需要连接后续节点为默认路由 |
| 236 | - } else if (nodeType == BpmSimpleModelNodeType.ROUTE_BRANCH_NODE) { | |
| 235 | + } else if (nodeType == BpmSimpleModelNodeType.ROUTER_BRANCH_NODE) { | |
| 237 | 236 | SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getDefaultFlowId(), |
| 238 | 237 | null, null); |
| 239 | 238 | process.addFlowElement(sequenceFlow); |
| ... | ... | @@ -453,10 +452,7 @@ public class SimpleModelUtils { |
| 453 | 452 | flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); |
| 454 | 453 | flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); |
| 455 | 454 | flowableListener.setImplementation(DELEGATE_EXPRESSION); |
| 456 | - FieldExtension fieldExtension = new FieldExtension(); | |
| 457 | - fieldExtension.setFieldName("listenerConfig"); | |
| 458 | - fieldExtension.setStringValue(JsonUtils.toJsonString(node.getTaskCreateListener())); | |
| 459 | - flowableListener.getFieldExtensions().add(fieldExtension); | |
| 455 | + addListenerConfig(flowableListener, node.getTaskCreateListener()); | |
| 460 | 456 | flowableListeners.add(flowableListener); |
| 461 | 457 | } |
| 462 | 458 | if (node.getTaskAssignListener() != null |
| ... | ... | @@ -465,11 +461,7 @@ public class SimpleModelUtils { |
| 465 | 461 | flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); |
| 466 | 462 | flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); |
| 467 | 463 | flowableListener.setImplementation(DELEGATE_EXPRESSION); |
| 468 | - // TODO @lesan:可以在 BpmnModelUtils 搞个方法,类似 public static Integer parseCandidateStrategy。这样,就收敛啦! | |
| 469 | - FieldExtension fieldExtension = new FieldExtension(); | |
| 470 | - fieldExtension.setFieldName("listenerConfig"); | |
| 471 | - fieldExtension.setStringValue(JsonUtils.toJsonString(node.getTaskAssignListener())); | |
| 472 | - flowableListener.getFieldExtensions().add(fieldExtension); | |
| 464 | + addListenerConfig(flowableListener, node.getTaskAssignListener()); | |
| 473 | 465 | flowableListeners.add(flowableListener); |
| 474 | 466 | } |
| 475 | 467 | if (node.getTaskCompleteListener() != null |
| ... | ... | @@ -478,10 +470,7 @@ public class SimpleModelUtils { |
| 478 | 470 | flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); |
| 479 | 471 | flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); |
| 480 | 472 | flowableListener.setImplementation(DELEGATE_EXPRESSION); |
| 481 | - FieldExtension fieldExtension = new FieldExtension(); | |
| 482 | - fieldExtension.setFieldName("listenerConfig"); | |
| 483 | - fieldExtension.setStringValue(JsonUtils.toJsonString(node.getTaskCompleteListener())); | |
| 484 | - flowableListener.getFieldExtensions().add(fieldExtension); | |
| 473 | + addListenerConfig(flowableListener, node.getTaskCompleteListener()); | |
| 485 | 474 | flowableListeners.add(flowableListener); |
| 486 | 475 | } |
| 487 | 476 | if (CollUtil.isNotEmpty(flowableListeners)) { |
| ... | ... | @@ -646,9 +635,9 @@ public class SimpleModelUtils { |
| 646 | 635 | node.getConditionGroups()); |
| 647 | 636 | } |
| 648 | 637 | |
| 649 | - public static String buildConditionExpression(BpmSimpleModelNodeVO.RouteCondition route) { | |
| 650 | - return buildConditionExpression(route.getConditionType(), route.getConditionExpression(), | |
| 651 | - route.getConditionGroups()); | |
| 638 | + public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterCondition router) { | |
| 639 | + return buildConditionExpression(router.getConditionType(), router.getConditionExpression(), | |
| 640 | + router.getConditionGroups()); | |
| 652 | 641 | } |
| 653 | 642 | |
| 654 | 643 | public static String buildConditionExpression(Integer conditionType, String conditionExpression, |
| ... | ... | @@ -728,18 +717,19 @@ public class SimpleModelUtils { |
| 728 | 717 | exclusiveGateway.setId(node.getId()); |
| 729 | 718 | |
| 730 | 719 | // 设置默认的序列流(条件) |
| 720 | + node.setDefaultFlowId("Flow_" + IdUtil.fastUUID()); | |
| 731 | 721 | exclusiveGateway.setDefaultFlow(node.getDefaultFlowId()); |
| 732 | 722 | return exclusiveGateway; |
| 733 | 723 | } |
| 734 | 724 | |
| 735 | 725 | @Override |
| 736 | 726 | public BpmSimpleModelNodeType getType() { |
| 737 | - return BpmSimpleModelNodeType.ROUTE_BRANCH_NODE; | |
| 727 | + return BpmSimpleModelNodeType.ROUTER_BRANCH_NODE; | |
| 738 | 728 | } |
| 739 | 729 | |
| 740 | - public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouteCondition route) { | |
| 741 | - String conditionExpression = ConditionNodeConvert.buildConditionExpression(route); | |
| 742 | - return buildBpmnSequenceFlow(nodeId, route.getNodeId(), null, null, conditionExpression); | |
| 730 | + public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouterCondition router) { | |
| 731 | + String conditionExpression = ConditionNodeConvert.buildConditionExpression(router); | |
| 732 | + return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression); | |
| 743 | 733 | } |
| 744 | 734 | |
| 745 | 735 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -482,6 +482,12 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 482 | 482 | if (instance == null) { |
| 483 | 483 | throw exception(PROCESS_INSTANCE_NOT_EXISTS); |
| 484 | 484 | } |
| 485 | + // 1.3 校验签名 | |
| 486 | + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | |
| 487 | + Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey()); | |
| 488 | + if (signEnable && StrUtil.isEmpty(reqVO.getSign())) { | |
| 489 | + throw exception(TASK_SIGNATURE_NOT_EXISTS); | |
| 490 | + } | |
| 485 | 491 | |
| 486 | 492 | // 情况一:被委派的任务,不调用 complete 去完成任务 |
| 487 | 493 | if (DelegationState.PENDING.equals(task.getDelegationState())) { |
| ... | ... | @@ -496,8 +502,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 496 | 502 | } |
| 497 | 503 | |
| 498 | 504 | // 情况三:审批普通的任务。大多数情况下,都是这样 |
| 499 | - // 2.1 更新 task 状态、原因 | |
| 505 | + // 2.1 更新 task 状态、原因、签字 | |
| 500 | 506 | updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason()); |
| 507 | + if (signEnable) { | |
| 508 | + taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_VARIABLE_SIGN, reqVO.getSign()); | |
| 509 | + } | |
| 501 | 510 | // 2.2 添加评论 |
| 502 | 511 | taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), |
| 503 | 512 | BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.service.task.listener; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | -import cn.hutool.core.lang.Assert; | |
| 5 | 4 | import cn.hutool.core.util.StrUtil; |
| 6 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 7 | 5 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 8 | 6 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmListenerParamTypeEnum; |
| 9 | -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; | |
| 10 | -import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; | |
| 11 | 7 | import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; |
| 12 | 8 | import jakarta.annotation.Resource; |
| 9 | +import lombok.Setter; | |
| 13 | 10 | import lombok.extern.slf4j.Slf4j; |
| 14 | -import org.flowable.bpmn.model.BpmnModel; | |
| 15 | -import org.flowable.bpmn.model.FieldExtension; | |
| 16 | -import org.flowable.bpmn.model.FlowableListener; | |
| 17 | -import org.flowable.bpmn.model.UserTask; | |
| 18 | 11 | import org.flowable.engine.delegate.TaskListener; |
| 19 | 12 | import org.flowable.engine.history.HistoricProcessInstance; |
| 13 | +import org.flowable.engine.impl.el.FixedValue; | |
| 20 | 14 | import org.flowable.task.service.delegate.DelegateTask; |
| 15 | +import org.springframework.context.annotation.Scope; | |
| 21 | 16 | import org.springframework.http.HttpEntity; |
| 22 | 17 | import org.springframework.http.HttpMethod; |
| 23 | 18 | import org.springframework.http.ResponseEntity; |
| 24 | 19 | import org.springframework.stereotype.Component; |
| 25 | 20 | import org.springframework.util.LinkedMultiValueMap; |
| 26 | 21 | import org.springframework.util.MultiValueMap; |
| 22 | +import org.springframework.web.client.RestClientException; | |
| 27 | 23 | import org.springframework.web.client.RestTemplate; |
| 28 | 24 | |
| 29 | 25 | import java.util.List; |
| 30 | 26 | import java.util.Map; |
| 31 | 27 | |
| 32 | 28 | import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; |
| 29 | +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; | |
| 33 | 30 | |
| 34 | 31 | /** |
| 35 | 32 | * BPM 用户任务通用监听器 |
| ... | ... | @@ -38,34 +35,32 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_ |
| 38 | 35 | */ |
| 39 | 36 | @Component |
| 40 | 37 | @Slf4j |
| 38 | +@Scope("prototype") | |
| 41 | 39 | public class BpmUserTaskListener implements TaskListener { |
| 42 | 40 | |
| 43 | 41 | public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}"; |
| 44 | 42 | |
| 45 | 43 | @Resource |
| 46 | - private BpmModelService modelService; | |
| 47 | - | |
| 48 | - @Resource | |
| 49 | 44 | private BpmProcessInstanceService processInstanceService; |
| 50 | 45 | |
| 51 | 46 | @Resource |
| 52 | 47 | private RestTemplate restTemplate; |
| 53 | 48 | |
| 49 | + @Setter | |
| 50 | + private FixedValue listenerConfig; | |
| 51 | + | |
| 54 | 52 | @Override |
| 55 | 53 | public void notify(DelegateTask delegateTask) { |
| 56 | 54 | // 1. 获取所需基础信息 |
| 57 | 55 | HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId()); |
| 58 | - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(delegateTask.getProcessDefinitionId()); | |
| 59 | - UserTask userTask = (UserTask) BpmnModelUtils.getFlowElementById(bpmnModel, delegateTask.getTaskDefinitionKey()); | |
| 60 | - BpmSimpleModelNodeVO.ListenerHandler listenerHandler = getListenerHandlerByEvent(delegateTask.getEventName(), | |
| 61 | - userTask.getTaskListeners()); | |
| 56 | + BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); | |
| 62 | 57 | |
| 63 | 58 | // 2. 获取请求头和请求体 |
| 64 | 59 | Map<String, Object> processVariables = processInstance.getProcessVariables(); |
| 65 | 60 | MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); |
| 66 | 61 | MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); |
| 67 | - parseListenerMap(listenerHandler.getHeader(), processVariables, headers); | |
| 68 | - parseListenerMap(listenerHandler.getBody(), processVariables, body); | |
| 62 | + parseListenerParam(listenerHandler.getHeader(), processVariables, headers); | |
| 63 | + parseListenerParam(listenerHandler.getBody(), processVariables, body); | |
| 69 | 64 | // 2.1 请求头默认参数 |
| 70 | 65 | if (StrUtil.isNotEmpty(delegateTask.getTenantId())) { |
| 71 | 66 | headers.add(HEADER_TENANT_ID, delegateTask.getTenantId()); |
| ... | ... | @@ -80,21 +75,29 @@ public class BpmUserTaskListener implements TaskListener { |
| 80 | 75 | // 3. 异步发起请求 |
| 81 | 76 | // TODO @芋艿:确认要同步,还是异步 |
| 82 | 77 | HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers); |
| 83 | - // TODO @lesan:可能需要 try catch 哇? RestClientException | |
| 84 | - ResponseEntity<String> responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST, | |
| 85 | - requestEntity, String.class); | |
| 86 | - log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]", | |
| 87 | - DELEGATE_EXPRESSION, | |
| 88 | - delegateTask.getEventName(), | |
| 89 | - headers, | |
| 90 | - body, | |
| 91 | - responseEntity); | |
| 78 | + try { | |
| 79 | + ResponseEntity<String> responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST, | |
| 80 | + requestEntity, String.class); | |
| 81 | + log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]", | |
| 82 | + DELEGATE_EXPRESSION, | |
| 83 | + delegateTask.getEventName(), | |
| 84 | + headers, | |
| 85 | + body, | |
| 86 | + responseEntity); | |
| 87 | + } catch (RestClientException e) { | |
| 88 | + log.error("[error][监听器:{},事件类型:{},请求头:{},请求体:{},请求出错:{}]", | |
| 89 | + DELEGATE_EXPRESSION, | |
| 90 | + delegateTask.getEventName(), | |
| 91 | + headers, | |
| 92 | + body, | |
| 93 | + e.getMessage()); | |
| 94 | + } | |
| 92 | 95 | // 4. 是否需要后续操作?TODO 芋艿:待定! |
| 93 | 96 | } |
| 94 | 97 | |
| 95 | - private void parseListenerMap(List<BpmSimpleModelNodeVO.ListenerHandler.ListenerParam> list, | |
| 96 | - Map<String, Object> processVariables, | |
| 97 | - MultiValueMap<String, String> to) { | |
| 98 | + private void parseListenerParam(List<BpmSimpleModelNodeVO.ListenerHandler.ListenerParam> list, | |
| 99 | + Map<String, Object> processVariables, | |
| 100 | + MultiValueMap<String, String> to) { | |
| 98 | 101 | if (CollUtil.isEmpty(list)) { |
| 99 | 102 | return; |
| 100 | 103 | } |
| ... | ... | @@ -107,17 +110,4 @@ public class BpmUserTaskListener implements TaskListener { |
| 107 | 110 | }); |
| 108 | 111 | } |
| 109 | 112 | |
| 110 | - private BpmSimpleModelNodeVO.ListenerHandler getListenerHandlerByEvent(String eventName, List<FlowableListener> node) { | |
| 111 | - FlowableListener flowableListener = node.stream() | |
| 112 | - .filter(item -> item.getEvent().equals(eventName)) | |
| 113 | - .findFirst().orElse(null); | |
| 114 | - Assert.notNull(flowableListener, "监听器({})不能为空", flowableListener); | |
| 115 | - // TODO @lesan:BpmnModelUtils 提供一个 BpmSimpleModelNodeVO.ListenerHandler 解析方法,尽量收敛掉。 | |
| 116 | - FieldExtension fieldExtension = flowableListener.getFieldExtensions().stream() | |
| 117 | - .filter(item -> item.getFieldName().equals("listenerConfig")) | |
| 118 | - .findFirst().orElse(null); | |
| 119 | - Assert.notNull(fieldExtension, "监听器扩展字段({})不能为空", fieldExtension); | |
| 120 | - return JsonUtils.parseObject(fieldExtension.getStringValue(), BpmSimpleModelNodeVO.ListenerHandler.class); | |
| 121 | - } | |
| 122 | - | |
| 123 | 113 | } |
| 124 | 114 | \ No newline at end of file | ... | ... |