Commit a53c7dafd3db965d8a1d6241b6e3274903b634cb
Merge branch 'feature/bpm' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17
Showing
20 changed files
with
238 additions
and
89 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| ... | ... | @@ -42,6 +42,7 @@ public interface ErrorCodeConstants { |
| 42 | 42 | ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); |
| 43 | 43 | ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); |
| 44 | 44 | ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); |
| 45 | + ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); | |
| 45 | 46 | |
| 46 | 47 | // ========== 流程任务 1-009-005-000 ========== |
| 47 | 48 | ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); | ... | ... |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmReasonEnum.java
| ... | ... | @@ -18,6 +18,7 @@ public enum BpmReasonEnum { |
| 18 | 18 | REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法 |
| 19 | 19 | CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程 |
| 20 | 20 | CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程 |
| 21 | + CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS("子流程自动取消,原因:主流程已取消"), | |
| 21 | 22 | |
| 22 | 23 | // ========== 流程任务的独有原因 ========== |
| 23 | 24 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.bpm.controller.admin.base.dept; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "部门精简信息 VO") | |
| 7 | +@Data | |
| 8 | +public class DeptSimpleBaseVO { | |
| 9 | + | |
| 10 | + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 11 | + private Long id; | |
| 12 | + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术部") | |
| 13 | + private String name; | |
| 14 | + | |
| 15 | +} | |
| 0 | 16 | \ No newline at end of file | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/BpmModelController.java
| ... | ... | @@ -12,6 +12,8 @@ import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; |
| 12 | 12 | import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; |
| 13 | 13 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; |
| 14 | 14 | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| 15 | +import cn.iocoder.yudao.module.system.api.dept.DeptApi; | |
| 16 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 15 | 17 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 16 | 18 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 17 | 19 | import io.swagger.v3.oas.annotations.Operation; |
| ... | ... | @@ -53,6 +55,8 @@ public class BpmModelController { |
| 53 | 55 | |
| 54 | 56 | @Resource |
| 55 | 57 | private AdminUserApi adminUserApi; |
| 58 | + @Resource | |
| 59 | + private DeptApi deptApi; | |
| 56 | 60 | |
| 57 | 61 | @GetMapping("/list") |
| 58 | 62 | @Operation(summary = "获得模型分页") |
| ... | ... | @@ -79,14 +83,19 @@ public class BpmModelController { |
| 79 | 83 | List<ProcessDefinition> processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds( |
| 80 | 84 | deploymentMap.keySet()); |
| 81 | 85 | Map<String, ProcessDefinition> processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId); |
| 82 | - // 获得 User Map | |
| 86 | + // 获得 User Map、Dept Map | |
| 83 | 87 | Set<Long> userIds = convertSetByFlatMap(list, model -> { |
| 84 | 88 | BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); |
| 85 | 89 | return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty(); |
| 86 | 90 | }); |
| 87 | 91 | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds); |
| 92 | + Set<Long> deptIds = convertSetByFlatMap(list, model -> { | |
| 93 | + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); | |
| 94 | + return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty(); | |
| 95 | + }); | |
| 96 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds); | |
| 88 | 97 | return success(BpmModelConvert.INSTANCE.buildModelList(list, |
| 89 | - formMap, categoryMap, deploymentMap, processDefinitionMap, userMap)); | |
| 98 | + formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap)); | |
| 90 | 99 | } |
| 91 | 100 | |
| 92 | 101 | @GetMapping("/get") | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
| ... | ... | @@ -59,6 +59,9 @@ public class BpmModelMetaInfoVO { |
| 59 | 59 | @Schema(description = "可发起用户编号数组", example = "[1,2,3]") |
| 60 | 60 | private List<Long> startUserIds; |
| 61 | 61 | |
| 62 | + @Schema(description = "可发起部门编号数组", example = "[2,4,6]") | |
| 63 | + private List<Long> startDeptIds; | |
| 64 | + | |
| 62 | 65 | @Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]") |
| 63 | 66 | @NotEmpty(message = "可管理用户编号数组不能为空") |
| 64 | 67 | private List<Long> managerUserIds; |
| ... | ... | @@ -88,6 +91,12 @@ public class BpmModelMetaInfoVO { |
| 88 | 91 | @Schema(description = "流程后置通知设置", example = "{}") |
| 89 | 92 | private HttpRequestSetting processAfterTriggerSetting; |
| 90 | 93 | |
| 94 | + @Schema(description = "任务前置通知设置", example = "{}") | |
| 95 | + private HttpRequestSetting taskBeforeTriggerSetting; | |
| 96 | + | |
| 97 | + @Schema(description = "任务后置通知设置", example = "{}") | |
| 98 | + private HttpRequestSetting taskAfterTriggerSetting; | |
| 99 | + | |
| 91 | 100 | @Schema(description = "流程 ID 规则") |
| 92 | 101 | @Data |
| 93 | 102 | @Valid | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java
| 1 | 1 | package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; | |
| 3 | 4 | import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; |
| 4 | 5 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 5 | 6 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; |
| ... | ... | @@ -39,6 +40,9 @@ public class BpmModelRespVO extends BpmModelMetaInfoVO { |
| 39 | 40 | @Schema(description = "可发起的用户数组") |
| 40 | 41 | private List<UserSimpleBaseVO> startUsers; |
| 41 | 42 | |
| 43 | + @Schema(description = "可发起的部门数组") | |
| 44 | + private List<DeptSimpleBaseVO> startDepts; | |
| 45 | + | |
| 42 | 46 | @Schema(description = "BPMN XML") |
| 43 | 47 | private String bpmnXml; |
| 44 | 48 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
| ... | ... | @@ -112,7 +112,7 @@ public class BpmSimpleModelNodeVO { |
| 112 | 112 | /** |
| 113 | 113 | * 条件节点设置 |
| 114 | 114 | */ |
| 115 | - private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE | |
| 115 | + private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE | |
| 116 | 116 | |
| 117 | 117 | @Schema(description = "路由分支组", example = "[]") |
| 118 | 118 | private List<RouterSetting> routerGroups; |
| ... | ... | @@ -241,7 +241,7 @@ public class BpmSimpleModelNodeVO { |
| 241 | 241 | @Schema(description = "条件设置") |
| 242 | 242 | @Data |
| 243 | 243 | @Valid |
| 244 | - // 仅用于条件节点 BpmSimpleModelNodeType.CONDITION_NODE | |
| 244 | + // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE | |
| 245 | 245 | public static class ConditionSetting { |
| 246 | 246 | |
| 247 | 247 | @Schema(description = "条件类型", example = "1") | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
| ... | ... | @@ -148,7 +148,6 @@ public class BpmProcessInstanceController { |
| 148 | 148 | processDefinition, processDefinitionInfo, startUser, dept)); |
| 149 | 149 | } |
| 150 | 150 | |
| 151 | - // TODO @lesan:【子流程】子流程如果取消,主流程应该是通过、还是不通过哈?还是禁用掉子流程的取消? | |
| 152 | 151 | @DeleteMapping("/cancel-by-start-user") |
| 153 | 152 | @Operation(summary = "用户取消流程实例", description = "取消发起的流程") |
| 154 | 153 | @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
| ... | ... | @@ -72,6 +72,9 @@ public class BpmApprovalDetailRespVO { |
| 72 | 72 | @Schema(description = "候选人用户列表") |
| 73 | 73 | private List<UserSimpleBaseVO> candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 |
| 74 | 74 | |
| 75 | + @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") | |
| 76 | + private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) | |
| 77 | + | |
| 75 | 78 | } |
| 76 | 79 | |
| 77 | 80 | @Schema(description = "活动节点的任务信息") | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.hutool.core.util.ArrayUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 7 | +import cn.iocoder.yudao.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; | |
| 7 | 8 | import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; |
| 8 | 9 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; |
| 9 | 10 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO; |
| ... | ... | @@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmPro |
| 13 | 14 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| 14 | 15 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| 15 | 16 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; |
| 17 | +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; | |
| 16 | 18 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| 17 | 19 | import org.flowable.common.engine.impl.db.SuspensionState; |
| 18 | 20 | import org.flowable.engine.repository.Deployment; |
| ... | ... | @@ -43,7 +45,8 @@ public interface BpmModelConvert { |
| 43 | 45 | Map<String, BpmCategoryDO> categoryMap, |
| 44 | 46 | Map<String, Deployment> deploymentMap, |
| 45 | 47 | Map<String, ProcessDefinition> processDefinitionMap, |
| 46 | - Map<Long, AdminUserRespDTO> userMap) { | |
| 48 | + Map<Long, AdminUserRespDTO> userMap, | |
| 49 | + Map<Long, DeptRespDTO> deptMap) { | |
| 47 | 50 | List<BpmModelRespVO> result = convertList(list, model -> { |
| 48 | 51 | BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); |
| 49 | 52 | BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; |
| ... | ... | @@ -52,7 +55,8 @@ public interface BpmModelConvert { |
| 52 | 55 | ProcessDefinition processDefinition = model.getDeploymentId() != null ? |
| 53 | 56 | processDefinitionMap.get(model.getDeploymentId()) : null; |
| 54 | 57 | List<AdminUserRespDTO> startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null; |
| 55 | - return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers); | |
| 58 | + List<DeptRespDTO> startDepts = metaInfo != null ? convertList(metaInfo.getStartDeptIds(), deptMap::get) : null; | |
| 59 | + return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers, startDepts); | |
| 56 | 60 | }); |
| 57 | 61 | // 排序 |
| 58 | 62 | result.sort(Comparator.comparing(BpmModelMetaInfoVO::getSort)); |
| ... | ... | @@ -61,7 +65,7 @@ public interface BpmModelConvert { |
| 61 | 65 | |
| 62 | 66 | default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes, BpmSimpleModelNodeVO simpleModel) { |
| 63 | 67 | BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); |
| 64 | - BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null); | |
| 68 | + BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null, null); | |
| 65 | 69 | if (ArrayUtil.isNotEmpty(bpmnBytes)) { |
| 66 | 70 | modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes)); |
| 67 | 71 | } |
| ... | ... | @@ -72,7 +76,7 @@ public interface BpmModelConvert { |
| 72 | 76 | default BpmModelRespVO buildModel0(Model model, |
| 73 | 77 | BpmModelMetaInfoVO metaInfo, BpmFormDO form, BpmCategoryDO category, |
| 74 | 78 | Deployment deployment, ProcessDefinition processDefinition, |
| 75 | - List<AdminUserRespDTO> startUsers) { | |
| 79 | + List<AdminUserRespDTO> startUsers, List<DeptRespDTO> startDepts) { | |
| 76 | 80 | BpmModelRespVO modelRespVO = new BpmModelRespVO().setId(model.getId()).setName(model.getName()) |
| 77 | 81 | .setKey(model.getKey()).setCategory(model.getCategory()) |
| 78 | 82 | .setCreateTime(DateUtils.of(model.getCreateTime())); |
| ... | ... | @@ -94,8 +98,9 @@ public interface BpmModelConvert { |
| 94 | 98 | modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime())); |
| 95 | 99 | } |
| 96 | 100 | } |
| 97 | - // User | |
| 98 | - modelRespVO.setStartUsers(BeanUtils.toBean(startUsers, UserSimpleBaseVO.class)); | |
| 101 | + // User、Dept | |
| 102 | + modelRespVO.setStartUsers(BeanUtils.toBean(startUsers, UserSimpleBaseVO.class)) | |
| 103 | + .setStartDepts(BeanUtils.toBean(startDepts, DeptSimpleBaseVO.class)); | |
| 99 | 104 | return modelRespVO; |
| 100 | 105 | } |
| 101 | 106 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
| ... | ... | @@ -152,6 +152,14 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { |
| 152 | 152 | private List<Long> startUserIds; |
| 153 | 153 | |
| 154 | 154 | /** |
| 155 | + * 可发起部门编号数组 | |
| 156 | + * | |
| 157 | + * 关联 {@link AdminUserRespDTO#getDeptId()} 字段的数组 | |
| 158 | + */ | |
| 159 | + @TableField(typeHandler = LongListTypeHandler.class) | |
| 160 | + private List<Long> startDeptIds; | |
| 161 | + | |
| 162 | + /** | |
| 155 | 163 | * 可管理用户编号数组 |
| 156 | 164 | * |
| 157 | 165 | * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 |
| ... | ... | @@ -199,4 +207,16 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { |
| 199 | 207 | @TableField(typeHandler = JacksonTypeHandler.class) |
| 200 | 208 | private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting; |
| 201 | 209 | |
| 210 | + /** | |
| 211 | + * 任务前置通知设置 | |
| 212 | + */ | |
| 213 | + @TableField(typeHandler = JacksonTypeHandler.class) | |
| 214 | + private BpmModelMetaInfoVO.HttpRequestSetting taskBeforeTriggerSetting; | |
| 215 | + | |
| 216 | + /** | |
| 217 | + * 任务后置通知设置 | |
| 218 | + */ | |
| 219 | + @TableField(typeHandler = JacksonTypeHandler.class) | |
| 220 | + private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting; | |
| 221 | + | |
| 202 | 222 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
| ... | ... | @@ -47,7 +47,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { |
| 47 | 47 | public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder() |
| 48 | 48 | .add(FlowableEngineEventType.TASK_CREATED) |
| 49 | 49 | .add(FlowableEngineEventType.TASK_ASSIGNED) |
| 50 | -// .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。 | |
| 50 | + .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,这里仅处理任务后置通知。 | |
| 51 | 51 | .add(FlowableEngineEventType.ACTIVITY_CANCELLED) |
| 52 | 52 | .add(FlowableEngineEventType.TIMER_FIRED) // 监听审批超时 |
| 53 | 53 | .build(); |
| ... | ... | @@ -67,6 +67,11 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | 69 | @Override |
| 70 | + protected void taskCompleted(FlowableEngineEntityEvent event) { | |
| 71 | + taskService.processTaskCompleted((Task) event.getEntity()); | |
| 72 | + } | |
| 73 | + | |
| 74 | + @Override | |
| 70 | 75 | protected void activityCancelled(FlowableActivityCancelledEvent event) { |
| 71 | 76 | List<HistoricActivityInstance> activityList = taskService.getHistoricActivityListByExecutionId(event.getExecutionId()); |
| 72 | 77 | if (CollUtil.isEmpty(activityList)) { | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
| ... | ... | @@ -187,7 +187,7 @@ public class SimpleModelUtils { |
| 187 | 187 | /** |
| 188 | 188 | * 构建有附加节点的连线 |
| 189 | 189 | * |
| 190 | - * @param nodeId 当前节点 ID | |
| 190 | + * @param nodeId 当前节点 ID | |
| 191 | 191 | * @param attachNodeId 附属节点 ID |
| 192 | 192 | * @param targetNodeId 目标节点 ID |
| 193 | 193 | */ |
| ... | ... | @@ -662,6 +662,10 @@ public class SimpleModelUtils { |
| 662 | 662 | * 构造条件表达式 |
| 663 | 663 | */ |
| 664 | 664 | public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { |
| 665 | + // 并行网关不需要设置条件 | |
| 666 | + if (conditionSetting == null) { | |
| 667 | + return null; | |
| 668 | + } | |
| 665 | 669 | return buildConditionExpression(conditionSetting.getConditionType(), conditionSetting.getConditionExpression(), |
| 666 | 670 | conditionSetting.getConditionGroups()); |
| 667 | 671 | } |
| ... | ... | @@ -813,7 +817,6 @@ public class SimpleModelUtils { |
| 813 | 817 | callActivity.setCalledElementType("key"); |
| 814 | 818 | // 1. 是否异步 |
| 815 | 819 | if (node.getChildProcessSetting().getAsync()) { |
| 816 | - // TODO @lesan: 这里目前测试没有跳过执行call activity 后面的节点 | |
| 817 | 820 | callActivity.setAsynchronous(true); |
| 818 | 821 | } |
| 819 | 822 | |
| ... | ... | @@ -959,8 +962,8 @@ public class SimpleModelUtils { |
| 959 | 962 | if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { |
| 960 | 963 | // 查找满足条件的 BpmSimpleModelNodeVO 节点 |
| 961 | 964 | BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), |
| 962 | - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) | |
| 963 | - && evalConditionExpress(variables, conditionNode.getConditionSetting())); | |
| 965 | + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) | |
| 966 | + && evalConditionExpress(variables, conditionNode.getConditionSetting())); | |
| 964 | 967 | if (matchConditionNode == null) { |
| 965 | 968 | matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), |
| 966 | 969 | conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); |
| ... | ... | @@ -974,8 +977,8 @@ public class SimpleModelUtils { |
| 974 | 977 | if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { |
| 975 | 978 | // 查找满足条件的 BpmSimpleModelNodeVO 节点 |
| 976 | 979 | Collection<BpmSimpleModelNodeVO> matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), |
| 977 | - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) | |
| 978 | - && evalConditionExpress(variables, conditionNode.getConditionSetting())); | |
| 980 | + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) | |
| 981 | + && evalConditionExpress(variables, conditionNode.getConditionSetting())); | |
| 979 | 982 | if (CollUtil.isEmpty(matchConditionNodes)) { |
| 980 | 983 | matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), |
| 981 | 984 | conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java
| ... | ... | @@ -12,6 +12,8 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitio |
| 12 | 12 | import cn.iocoder.yudao.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper; |
| 13 | 13 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 14 | 14 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| 15 | +import cn.iocoder.yudao.module.system.api.user.AdminUserApi; | |
| 16 | +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | |
| 15 | 17 | import jakarta.annotation.Resource; |
| 16 | 18 | import lombok.extern.slf4j.Slf4j; |
| 17 | 19 | import org.flowable.bpmn.model.BpmnModel; |
| ... | ... | @@ -50,6 +52,9 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ |
| 50 | 52 | @Resource |
| 51 | 53 | private BpmProcessDefinitionInfoMapper processDefinitionMapper; |
| 52 | 54 | |
| 55 | + @Resource | |
| 56 | + private AdminUserApi adminUserApi; | |
| 57 | + | |
| 53 | 58 | @Override |
| 54 | 59 | public ProcessDefinition getProcessDefinition(String id) { |
| 55 | 60 | return repositoryService.getProcessDefinition(id); |
| ... | ... | @@ -88,12 +93,22 @@ public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionServ |
| 88 | 93 | if (processDefinition == null) { |
| 89 | 94 | return false; |
| 90 | 95 | } |
| 91 | - // 为空,则所有人都可以发起 | |
| 92 | - if (CollUtil.isEmpty(processDefinition.getStartUserIds())) { | |
| 93 | - return true; | |
| 96 | + | |
| 97 | + // 校验用户是否在允许发起的用户列表中 | |
| 98 | + if (CollUtil.isNotEmpty(processDefinition.getStartUserIds())) { | |
| 99 | + return processDefinition.getStartUserIds().contains(userId); | |
| 100 | + } | |
| 101 | + | |
| 102 | + // 校验用户是否在允许发起的部门列表中 | |
| 103 | + if (CollUtil.isNotEmpty(processDefinition.getStartDeptIds())) { | |
| 104 | + AdminUserRespDTO user = adminUserApi.getUser(userId); | |
| 105 | + return user != null | |
| 106 | + && user.getDeptId() != null | |
| 107 | + && processDefinition.getStartDeptIds().contains(user.getDeptId()); | |
| 94 | 108 | } |
| 95 | - // 不为空,则需要存在里面 | |
| 96 | - return processDefinition.getStartUserIds().contains(userId); | |
| 109 | + | |
| 110 | + // 都为空,则所有人都可以发起 | |
| 111 | + return true; | |
| 97 | 112 | } |
| 98 | 113 | |
| 99 | 114 | @Override | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
| ... | ... | @@ -14,7 +14,6 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 14 | 14 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 15 | 15 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 16 | 16 | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| 17 | -import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO; | |
| 18 | 17 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; |
| 19 | 18 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 20 | 19 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; |
| ... | ... | @@ -63,7 +62,6 @@ import org.springframework.context.annotation.Lazy; |
| 63 | 62 | import org.springframework.stereotype.Service; |
| 64 | 63 | import org.springframework.transaction.annotation.Transactional; |
| 65 | 64 | import org.springframework.validation.annotation.Validated; |
| 66 | -import org.springframework.web.client.RestTemplate; | |
| 67 | 65 | |
| 68 | 66 | import java.util.*; |
| 69 | 67 | |
| ... | ... | @@ -263,30 +261,31 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 263 | 261 | processVariables.putAll(reqVO.getProcessVariables()); |
| 264 | 262 | } |
| 265 | 263 | |
| 266 | - // 3 获取当前任务节点的信息 | |
| 267 | - // 3.1 获取下一个将要执行的节点集合 | |
| 264 | + // 3. 获取下一个将要执行的节点集合 | |
| 268 | 265 | FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); |
| 269 | 266 | List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); |
| 270 | - return convertList(nextFlowNodes, node -> { | |
| 271 | - List<Long> candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), | |
| 272 | - loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables); | |
| 273 | - // 3.2 获取节点的审批人信息 | |
| 274 | - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(candidateUserIds); | |
| 275 | - // 3.3 获取节点的审批人部门信息 | |
| 276 | - Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); | |
| 277 | - // 3.4 存在一个节点多人审批的情况,组装审批人信息 | |
| 278 | - List<UserSimpleBaseVO> candidateUsers = new ArrayList<>(); | |
| 279 | - userMap.forEach((key, value) -> candidateUsers.add(BpmProcessInstanceConvert.INSTANCE.buildUser(key, userMap, deptMap))); | |
| 280 | - return new ActivityNode().setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) | |
| 281 | - .setId(node.getId()) | |
| 282 | - .setName(node.getName()) | |
| 283 | - .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) | |
| 284 | - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) | |
| 285 | - // TODO @小北:先把 candidateUserIds 设置完,然后最后拼接 candidateUsers 信息。这样,如果有多个节点,就不用重复查询啦;类似 buildApprovalDetail 思路; | |
| 286 | - // TODO 先拼接处 List ActivityNode | |
| 287 | - // TODO 接着,再起一段,处理 adminUserApi.getUserMap(candidateUserIds)、deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)) | |
| 288 | - .setCandidateUsers(candidateUsers); | |
| 289 | - }); | |
| 267 | + List<ActivityNode> nextActivityNodes = convertList(nextFlowNodes, node -> new ActivityNode().setId(node.getId()) | |
| 268 | + .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) | |
| 269 | + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) | |
| 270 | + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) | |
| 271 | + .setCandidateUserIds(getTaskCandidateUserList(bpmnModel, node.getId(), | |
| 272 | + loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables))); | |
| 273 | + if (CollUtil.isNotEmpty(nextActivityNodes)) { | |
| 274 | + return nextActivityNodes; | |
| 275 | + } | |
| 276 | + | |
| 277 | + // 4. 拼接基础信息 | |
| 278 | + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( | |
| 279 | + convertSetByFlatMap(nextActivityNodes, ActivityNode::getCandidateUserIds, Collection::stream)); | |
| 280 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); | |
| 281 | + nextActivityNodes.forEach(node -> node.setCandidateUsers(convertList(node.getCandidateUserIds(), userId -> { | |
| 282 | + AdminUserRespDTO user = userMap.get(userId); | |
| 283 | + if (user != null) { | |
| 284 | + return BpmProcessInstanceConvert.INSTANCE.buildUser(userId, userMap, deptMap); | |
| 285 | + } | |
| 286 | + return null; | |
| 287 | + }))); | |
| 288 | + return nextActivityNodes; | |
| 290 | 289 | } |
| 291 | 290 | |
| 292 | 291 | @Override |
| ... | ... | @@ -388,8 +387,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 388 | 387 | List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) { |
| 389 | 388 | // 遍历 tasks 列表,只处理已结束的 UserTask |
| 390 | 389 | // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点 |
| 391 | - // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程; | |
| 392 | - // TODO @lesan:【子流程】基于 activities 查询出 usertask、callactivity,然后拼接?如果是子流程,就是可以点击过去? | |
| 393 | 390 | List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null); |
| 394 | 391 | List<ActivityNode> approvalNodes = convertList(endTasks, task -> { |
| 395 | 392 | FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| ... | ... | @@ -411,7 +408,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 411 | 408 | |
| 412 | 409 | // 遍历 activities,只处理已结束的 StartEvent、EndEvent |
| 413 | 410 | List<HistoricActivityInstance> endActivities = filterList(activities, activity -> activity.getEndTime() != null |
| 414 | - && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_EVENT_START, ELEMENT_EVENT_END))); | |
| 411 | + && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_EVENT_START, ELEMENT_CALL_ACTIVITY, ELEMENT_EVENT_END))); | |
| 415 | 412 | endActivities.forEach(activity -> { |
| 416 | 413 | // StartEvent:只处理 BPMN 的场景。因为,SIMPLE 情况下,已经有 START_USER_NODE 节点 |
| 417 | 414 | if (ELEMENT_EVENT_START.equals(activity.getActivityType()) |
| ... | ... | @@ -445,7 +442,20 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 445 | 442 | } |
| 446 | 443 | approvalNodes.add(endNode); |
| 447 | 444 | } |
| 445 | + // CallActivity | |
| 446 | + if (ELEMENT_CALL_ACTIVITY.equals(activity.getActivityType())) { | |
| 447 | + ActivityNode callActivity = new ActivityNode().setId(activity.getId()) | |
| 448 | + .setName(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getName()) | |
| 449 | + .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) | |
| 450 | + .setStartTime(DateUtils.of(activity.getStartTime())) | |
| 451 | + .setEndTime(DateUtils.of(activity.getEndTime())) | |
| 452 | + .setProcessInstanceId(activity.getProcessInstanceId()); | |
| 453 | + approvalNodes.add(callActivity); | |
| 454 | + } | |
| 448 | 455 | }); |
| 456 | + | |
| 457 | + // 按照时间排序 | |
| 458 | + approvalNodes.sort(Comparator.comparing(ActivityNode::getStartTime)); | |
| 449 | 459 | return approvalNodes; |
| 450 | 460 | } |
| 451 | 461 | |
| ... | ... | @@ -465,7 +475,6 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 465 | 475 | HistoricActivityInstance::getActivityId); |
| 466 | 476 | |
| 467 | 477 | // 按照 activityId 分组,构建 ApprovalNodeInfo 节点 |
| 468 | - // TODO @lesan:【子流程】在子流程进行审批的时候,HistoricActivityInstance 里面可以拿到 runActivities.get(0).getCalledProcessInstanceId()。要不要支持跳转??? | |
| 469 | 478 | Map<String, HistoricTaskInstance> taskMap = convertMap(tasks, HistoricTaskInstance::getId); |
| 470 | 479 | return convertList(runningTaskMap.entrySet(), entry -> { |
| 471 | 480 | String activityId = entry.getKey(); |
| ... | ... | @@ -511,6 +520,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 511 | 520 | approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner |
| 512 | 521 | activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); |
| 513 | 522 | } |
| 523 | + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { | |
| 524 | + activityNode.setProcessInstanceId(firstActivity.getProcessInstanceId()); | |
| 525 | + } | |
| 514 | 526 | return activityNode; |
| 515 | 527 | }); |
| 516 | 528 | } |
| ... | ... | @@ -824,6 +836,10 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 824 | 836 | && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) { |
| 825 | 837 | throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); |
| 826 | 838 | } |
| 839 | + // 1.4 子流程不允许取消 | |
| 840 | + if (StrUtil.isNotBlank(instance.getSuperExecutionId())) { | |
| 841 | + throw exception(PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW); | |
| 842 | + } | |
| 827 | 843 | |
| 828 | 844 | // 2. 取消流程 |
| 829 | 845 | updateProcessInstanceCancel(cancelReqVO.getId(), |
| ... | ... | @@ -850,7 +866,13 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 850 | 866 | BpmProcessInstanceStatusEnum.CANCEL.getStatus()); |
| 851 | 867 | runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason); |
| 852 | 868 | |
| 853 | - // 2. 结束流程 | |
| 869 | + // 2. 取消所有子流程 | |
| 870 | + List<ProcessInstance> childProcessInstances = runtimeService.createProcessInstanceQuery() | |
| 871 | + .superProcessInstanceId(id).list(); | |
| 872 | + childProcessInstances.forEach(processInstance -> updateProcessInstanceCancel( | |
| 873 | + processInstance.getProcessInstanceId(), BpmReasonEnum.CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS.getReason())); | |
| 874 | + | |
| 875 | + // 3. 结束流程 | |
| 854 | 876 | taskService.moveTaskToEnd(id, reason); |
| 855 | 877 | } |
| 856 | 878 | |
| ... | ... | @@ -915,10 +937,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 915 | 937 | BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); |
| 916 | 938 | |
| 917 | 939 | BpmHttpRequestUtils.executeBpmHttpRequest(instance, |
| 918 | - setting.getUrl(), | |
| 919 | - setting.getHeader(), | |
| 920 | - setting.getBody(), | |
| 921 | - true, setting.getResponse()); | |
| 940 | + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | |
| 922 | 941 | } |
| 923 | 942 | } |
| 924 | 943 | }); |
| ... | ... | @@ -937,10 +956,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService |
| 937 | 956 | } |
| 938 | 957 | BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting(); |
| 939 | 958 | BpmHttpRequestUtils.executeBpmHttpRequest(instance, |
| 940 | - setting.getUrl(), | |
| 941 | - setting.getHeader(), | |
| 942 | - setting.getBody(), | |
| 943 | - true, setting.getResponse()); | |
| 959 | + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | |
| 944 | 960 | }); |
| 945 | 961 | } |
| 946 | 962 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| ... | ... | @@ -279,6 +279,13 @@ public interface BpmTaskService { |
| 279 | 279 | void processTaskAssigned(Task task); |
| 280 | 280 | |
| 281 | 281 | /** |
| 282 | + * 处理 Task 完成事件,目前是发送任务后置通知 | |
| 283 | + * | |
| 284 | + * @param task 任务实体 | |
| 285 | + */ | |
| 286 | + void processTaskCompleted(Task task); | |
| 287 | + | |
| 288 | + /** | |
| 282 | 289 | * 处理 Task 审批超时事件,可能会处理多个当前审批中的任务 |
| 283 | 290 | * |
| 284 | 291 | * @param processInstanceId 流程示例编号 | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| ... | ... | @@ -6,12 +6,14 @@ import cn.hutool.core.lang.Assert; |
| 6 | 6 | import cn.hutool.core.util.*; |
| 7 | 7 | import cn.hutool.extra.spring.SpringUtil; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | |
| 9 | 10 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 10 | 11 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 11 | 12 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 12 | 13 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 13 | 14 | import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; |
| 14 | 15 | import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; |
| 16 | +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; | |
| 15 | 17 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| 16 | 18 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 17 | 19 | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; |
| ... | ... | @@ -23,6 +25,7 @@ import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum; |
| 23 | 25 | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| 24 | 26 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; |
| 25 | 27 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; |
| 28 | +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; | |
| 26 | 29 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; |
| 27 | 30 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; |
| 28 | 31 | import cn.iocoder.yudao.module.bpm.service.definition.BpmFormService; |
| ... | ... | @@ -597,47 +600,54 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 597 | 600 | */ |
| 598 | 601 | private Map<String, Object> validateAndSetNextAssignees(String taskDefinitionKey, Map<String, Object> variables, BpmnModel bpmnModel, |
| 599 | 602 | Map<String, List<Long>> nextAssignees, ProcessInstance processInstance) { |
| 603 | + // simple 设计器第一个节点默认为发起人节点,不校验是否存在审批人 | |
| 604 | + if (Objects.equals(taskDefinitionKey, START_USER_NODE_ID)) { | |
| 605 | + return variables; | |
| 606 | + } | |
| 600 | 607 | // 1. 获取下一个将要执行的节点集合 |
| 601 | 608 | FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); |
| 602 | 609 | List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); |
| 603 | 610 | |
| 604 | 611 | // 2. 校验选择的下一个节点的审批人,是否合法 |
| 605 | - Map<String, List<Long>> processVariables; | |
| 606 | 612 | for (FlowNode nextFlowNode : nextFlowNodes) { |
| 607 | 613 | Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); |
| 608 | 614 | // 2.1 情况一:如果节点中的审批人策略为 发起人自选 |
| 609 | 615 | if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { |
| 616 | + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 | |
| 617 | + Map<String, List<Long>> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); | |
| 618 | + if (startUserSelectAssignees != null && CollUtil.isNotEmpty(startUserSelectAssignees.get(nextFlowNode.getId()))) { | |
| 619 | + continue; | |
| 620 | + } | |
| 610 | 621 | // 如果节点存在,但未配置审批人 |
| 611 | 622 | List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; |
| 612 | 623 | if (CollUtil.isEmpty(assignees)) { |
| 613 | 624 | throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); |
| 614 | 625 | } |
| 615 | - processVariables = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); | |
| 616 | - // 特殊:如果当前节点已经存在审批人,则不允许覆盖 | |
| 617 | - if (processVariables != null && CollUtil.isNotEmpty(processVariables.get(nextFlowNode.getId()))) { | |
| 618 | - continue; | |
| 619 | - } | |
| 626 | + | |
| 620 | 627 | // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES |
| 621 | - if (processVariables == null) { | |
| 622 | - processVariables = new HashMap<>(); | |
| 628 | + if (startUserSelectAssignees == null) { | |
| 629 | + startUserSelectAssignees = new HashMap<>(); | |
| 623 | 630 | } |
| 624 | - processVariables.put(nextFlowNode.getId(), assignees); | |
| 625 | - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, processVariables); | |
| 631 | + startUserSelectAssignees.put(nextFlowNode.getId(), assignees); | |
| 632 | + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); | |
| 633 | + continue; | |
| 626 | 634 | } |
| 635 | + | |
| 627 | 636 | // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 |
| 628 | 637 | if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { |
| 629 | 638 | // 如果节点存在,但未配置审批人 |
| 639 | + Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); | |
| 630 | 640 | List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; |
| 631 | 641 | if (CollUtil.isEmpty(assignees)) { |
| 632 | 642 | throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); |
| 633 | 643 | } |
| 634 | - processVariables = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); | |
| 635 | - if (processVariables == null) { | |
| 636 | - processVariables = new HashMap<>(); | |
| 637 | - } | |
| 644 | + | |
| 638 | 645 | // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES |
| 639 | - processVariables.put(nextFlowNode.getId(), assignees); | |
| 640 | - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, processVariables); | |
| 646 | + if (approveUserSelectAssignees == null) { | |
| 647 | + approveUserSelectAssignees = new HashMap<>(); | |
| 648 | + } | |
| 649 | + approveUserSelectAssignees.put(nextFlowNode.getId(), assignees); | |
| 650 | + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, approveUserSelectAssignees); | |
| 641 | 651 | } |
| 642 | 652 | } |
| 643 | 653 | return variables; |
| ... | ... | @@ -1174,12 +1184,26 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1174 | 1184 | } |
| 1175 | 1185 | updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); |
| 1176 | 1186 | |
| 1177 | - // 2. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 | |
| 1178 | 1187 | ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); |
| 1179 | 1188 | if (processInstance == null) { |
| 1180 | 1189 | log.error("[processTaskCreated][taskId({}) 没有找到流程实例]", task.getId()); |
| 1181 | 1190 | return; |
| 1182 | 1191 | } |
| 1192 | + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. | |
| 1193 | + getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); | |
| 1194 | + if (processDefinitionInfo == null) { | |
| 1195 | + log.error("[processTaskCreated][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); | |
| 1196 | + return; | |
| 1197 | + } | |
| 1198 | + | |
| 1199 | + // 2. 任务前置通知 | |
| 1200 | + if (ObjUtil.isNotNull(processDefinitionInfo.getTaskBeforeTriggerSetting())){ | |
| 1201 | + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskBeforeTriggerSetting(); | |
| 1202 | + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, | |
| 1203 | + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | |
| 1204 | + } | |
| 1205 | + | |
| 1206 | + // 3. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 | |
| 1183 | 1207 | BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); |
| 1184 | 1208 | FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); |
| 1185 | 1209 | Integer approveType = BpmnModelUtils.parseApproveType(userTaskElement); |
| ... | ... | @@ -1392,6 +1416,28 @@ public class BpmTaskServiceImpl implements BpmTaskService { |
| 1392 | 1416 | } |
| 1393 | 1417 | |
| 1394 | 1418 | @Override |
| 1419 | + public void processTaskCompleted(Task task) { | |
| 1420 | + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); | |
| 1421 | + if (processInstance == null) { | |
| 1422 | + log.error("[processTaskCompleted][taskId({}) 没有找到流程实例]", task.getId()); | |
| 1423 | + return; | |
| 1424 | + } | |
| 1425 | + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. | |
| 1426 | + getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); | |
| 1427 | + if (processDefinitionInfo == null) { | |
| 1428 | + log.error("[processTaskCompleted][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); | |
| 1429 | + return; | |
| 1430 | + } | |
| 1431 | + | |
| 1432 | + // 任务后置通知 | |
| 1433 | + if (ObjUtil.isNotNull(processDefinitionInfo.getTaskAfterTriggerSetting())){ | |
| 1434 | + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskAfterTriggerSetting(); | |
| 1435 | + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, | |
| 1436 | + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | |
| 1437 | + } | |
| 1438 | + } | |
| 1439 | + | |
| 1440 | + @Override | |
| 1395 | 1441 | @Transactional(rollbackFor = Exception.class) |
| 1396 | 1442 | public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType) { |
| 1397 | 1443 | ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java
| ... | ... | @@ -53,10 +53,7 @@ public class BpmUserTaskListener implements TaskListener { |
| 53 | 53 | listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") |
| 54 | 54 | .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getId())); |
| 55 | 55 | BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, |
| 56 | - listenerHandler.getPath(), | |
| 57 | - listenerHandler.getHeader(), | |
| 58 | - listenerHandler.getBody(), | |
| 59 | - false, null); | |
| 56 | + listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), false, null); | |
| 60 | 57 | |
| 61 | 58 | // 3. 是否需要后续操作?TODO 芋艿:待定! |
| 62 | 59 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java
| ... | ... | @@ -45,10 +45,7 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { |
| 45 | 45 | .setKey("taskDefineKey") // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 |
| 46 | 46 | .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(setting.getCallbackTaskDefineKey())); |
| 47 | 47 | BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, |
| 48 | - setting.getUrl(), | |
| 49 | - setting.getHeader(), | |
| 50 | - setting.getBody(), | |
| 51 | - false, null); | |
| 48 | + setting.getUrl(), setting.getHeader(), setting.getBody(), false, null); | |
| 52 | 49 | } |
| 53 | 50 | |
| 54 | 51 | } | ... | ... |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java
| ... | ... | @@ -40,10 +40,7 @@ public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { |
| 40 | 40 | // 2. 发起请求 |
| 41 | 41 | ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); |
| 42 | 42 | BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, |
| 43 | - setting.getUrl(), | |
| 44 | - setting.getHeader(), | |
| 45 | - setting.getBody(), | |
| 46 | - true, setting.getResponse()); | |
| 43 | + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); | |
| 47 | 44 | } |
| 48 | 45 | |
| 49 | 46 | } | ... | ... |