Commit 4efa93b03b48b75e41b4c922cb3b9e2d96d977df
1 parent
921f99d3
feat: 【工作流】--委派
Showing
6 changed files
with
125 additions
and
14 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
| @@ -50,6 +50,8 @@ public interface ErrorCodeConstants { | @@ -50,6 +50,8 @@ public interface ErrorCodeConstants { | ||
| 50 | ErrorCode TASK_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_004, "目标节点是在并行网关上或非同一路线上,不可跳转"); | 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, " 目标节点不存在"); | 51 | ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_005, " 目标节点不存在"); |
| 52 | ErrorCode TASK_RETURN_FAIL = new ErrorCode(1_009_005_006, "回退任务失败,选择回退的节点没有需要回滚的任务!请重新选择其他任务节点"); | 52 | ErrorCode TASK_RETURN_FAIL = new ErrorCode(1_009_005_006, "回退任务失败,选择回退的节点没有需要回滚的任务!请重新选择其他任务节点"); |
| 53 | + ErrorCode TASK_DELEGATE_APPROVE_FAIL = new ErrorCode(1_009_005_007, "任务审批失败:委派任务找不到原审批人"); | ||
| 54 | + ErrorCode TASK_DELEGATE_USER_REPEAT = new ErrorCode(1_009_005_008, "任务委派失败,委派人和当前审批人为同一人"); | ||
| 53 | // ========== 流程任务分配规则 1-009-006-000 ========== | 55 | // ========== 流程任务分配规则 1-009-006-000 ========== |
| 54 | ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则"); | 56 | ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则"); |
| 55 | ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在"); | 57 | ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在"); |
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java
| @@ -20,7 +20,9 @@ public enum BpmProcessInstanceResultEnum { | @@ -20,7 +20,9 @@ public enum BpmProcessInstanceResultEnum { | ||
| 20 | 20 | ||
| 21 | // ========== 流程任务独有的状态 ========== | 21 | // ========== 流程任务独有的状态 ========== |
| 22 | 22 | ||
| 23 | - BACK(5, "退回/驳回"); | 23 | + BACK(5, "退回/驳回"), |
| 24 | + | ||
| 25 | + DELEGATE(6, "委派"); | ||
| 24 | 26 | ||
| 25 | /** | 27 | /** |
| 26 | * 结果 | 28 | * 结果 |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java
| @@ -91,4 +91,12 @@ public class BpmTaskController { | @@ -91,4 +91,12 @@ public class BpmTaskController { | ||
| 91 | return success(true); | 91 | return success(true); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | + @PutMapping("/delegate") | ||
| 95 | + @Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮,和向前加签有点像,和向前加签的唯一的区别是没有单独创立任务") | ||
| 96 | + @PreAuthorize("@ss.hasPermission('bpm:task:delegate')") | ||
| 97 | + public CommonResult<Boolean> delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) { | ||
| 98 | + taskService.delegateTask(reqVO); | ||
| 99 | + return success(true); | ||
| 100 | + } | ||
| 101 | + | ||
| 94 | } | 102 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.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 | +import javax.validation.constraints.NotNull; | ||
| 8 | + | ||
| 9 | +@Schema(description = "管理后台 - 委派流程任务的 Request VO") | ||
| 10 | +@Data | ||
| 11 | +public class BpmTaskDelegateReqVO { | ||
| 12 | + | ||
| 13 | + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") | ||
| 14 | + @NotEmpty(message = "任务编号不能为空") | ||
| 15 | + private String id; | ||
| 16 | + | ||
| 17 | + @Schema(description = "接收人ID", requiredMode = Schema.RequiredMode.REQUIRED,example = "1") | ||
| 18 | + @NotNull(message = "接收人ID不能为空") | ||
| 19 | + private Long receiveId; | ||
| 20 | + | ||
| 21 | + @Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED,example = "做不了决定,需要你先帮忙瞅瞅") | ||
| 22 | + @NotEmpty(message = "委派原因不能为空") | ||
| 23 | + private String reason; | ||
| 24 | +} |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
| @@ -137,4 +137,10 @@ public interface BpmTaskService { | @@ -137,4 +137,10 @@ public interface BpmTaskService { | ||
| 137 | */ | 137 | */ |
| 138 | void returnTask(BpmTaskReturnReqVO reqVO); | 138 | void returnTask(BpmTaskReturnReqVO reqVO); |
| 139 | 139 | ||
| 140 | + /** | ||
| 141 | + * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批 | ||
| 142 | + * | ||
| 143 | + * @param reqVO 接收人和被委派的任务编号理由参数 | ||
| 144 | + */ | ||
| 145 | + void delegateTask(BpmTaskDelegateReqVO reqVO); | ||
| 140 | } | 146 | } |
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
| @@ -2,16 +2,19 @@ package cn.iocoder.yudao.module.bpm.service.task; | @@ -2,16 +2,19 @@ package cn.iocoder.yudao.module.bpm.service.task; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.util.ArrayUtil; | 4 | import cn.hutool.core.util.ArrayUtil; |
| 5 | +import cn.hutool.core.util.ObjectUtil; | ||
| 5 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; | 8 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 8 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | 9 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 9 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; | 10 | import cn.iocoder.yudao.framework.common.util.object.PageUtils; |
| 10 | import cn.iocoder.yudao.framework.flowable.core.util.ModelUtils; | 11 | import cn.iocoder.yudao.framework.flowable.core.util.ModelUtils; |
| 12 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | ||
| 11 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; | 13 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; |
| 12 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; | 14 | import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; |
| 13 | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; | 15 | import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; |
| 14 | import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; | 16 | import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; |
| 17 | +import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; | ||
| 15 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; | 18 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; |
| 16 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; | 19 | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; |
| 17 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; | 20 | import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; |
| @@ -29,6 +32,7 @@ import org.flowable.engine.RuntimeService; | @@ -29,6 +32,7 @@ import org.flowable.engine.RuntimeService; | ||
| 29 | import org.flowable.engine.TaskService; | 32 | import org.flowable.engine.TaskService; |
| 30 | import org.flowable.engine.history.HistoricProcessInstance; | 33 | import org.flowable.engine.history.HistoricProcessInstance; |
| 31 | import org.flowable.engine.runtime.ProcessInstance; | 34 | import org.flowable.engine.runtime.ProcessInstance; |
| 35 | +import org.flowable.task.api.DelegationState; | ||
| 32 | import org.flowable.task.api.Task; | 36 | import org.flowable.task.api.Task; |
| 33 | import org.flowable.task.api.TaskQuery; | 37 | import org.flowable.task.api.TaskQuery; |
| 34 | import org.flowable.task.api.history.HistoricTaskInstance; | 38 | import org.flowable.task.api.history.HistoricTaskInstance; |
| @@ -192,13 +196,43 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -192,13 +196,43 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 192 | if (instance == null) { | 196 | if (instance == null) { |
| 193 | throw exception(PROCESS_INSTANCE_NOT_EXISTS); | 197 | throw exception(PROCESS_INSTANCE_NOT_EXISTS); |
| 194 | } | 198 | } |
| 199 | + //被委派的任务,不调用 complete 去完成任务 | ||
| 200 | + if (DelegationState.PENDING.equals(task.getDelegationState())) { | ||
| 201 | + this.approveDelegateTask(reqVO, task); | ||
| 202 | + } else { | ||
| 203 | + // 完成任务,审批通过 | ||
| 204 | + taskService.complete(task.getId(), instance.getProcessVariables()); | ||
| 205 | + | ||
| 206 | + // 更新任务拓展表为通过 | ||
| 207 | + taskExtMapper.updateByTaskId( | ||
| 208 | + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) | ||
| 209 | + .setReason(reqVO.getReason())); | ||
| 210 | + } | ||
| 211 | + } | ||
| 195 | 212 | ||
| 196 | - // 完成任务,审批通过 | ||
| 197 | - taskService.complete(task.getId(), instance.getProcessVariables()); | ||
| 198 | - | ||
| 199 | - // 更新任务拓展表为通过 | 213 | + /** |
| 214 | + * 审批被委派的任务 | ||
| 215 | + * | ||
| 216 | + * @param reqVO 前端请求参数,包含当前任务ID,审批意见等 | ||
| 217 | + * @param task 当前被审批的任务 | ||
| 218 | + */ | ||
| 219 | + private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) { | ||
| 220 | + // 添加审批意见 | ||
| 221 | + AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()); | ||
| 222 | + //原审批人 | ||
| 223 | + AdminUserRespDTO sourceApproveUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); | ||
| 224 | + if (sourceApproveUser == null) { | ||
| 225 | + throw exception(TASK_DELEGATE_APPROVE_FAIL); | ||
| 226 | + } | ||
| 227 | + String comment = String.format("[%s]完成委派任务,任务重新回到[%s]手中,审批意见为:%s", currentUser.getNickname(), | ||
| 228 | + sourceApproveUser.getNickname(), reqVO.getReason()); | ||
| 229 | + taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment); | ||
| 230 | + //调用 resolveTask 完成任务,底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()); | ||
| 231 | + //将 owner 设置为 assignee | ||
| 232 | + taskService.resolveTask(task.getId()); | ||
| 233 | + // 更新任务拓展表为【处理中】 | ||
| 200 | taskExtMapper.updateByTaskId( | 234 | taskExtMapper.updateByTaskId( |
| 201 | - new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) | 235 | + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()) |
| 202 | .setReason(reqVO.getReason())); | 236 | .setReason(reqVO.getReason())); |
| 203 | } | 237 | } |
| 204 | 238 | ||
| @@ -318,7 +352,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -318,7 +352,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 318 | } | 352 | } |
| 319 | 353 | ||
| 320 | private Task getTask(String id) { | 354 | private Task getTask(String id) { |
| 321 | - return taskService.createTaskQuery().taskId(id).singleResult(); | 355 | + Task task = taskService.createTaskQuery().taskId(id).singleResult(); |
| 356 | + if (null == task) { | ||
| 357 | + throw exception(TASK_NOT_EXISTS); | ||
| 358 | + } | ||
| 359 | + return task; | ||
| 322 | } | 360 | } |
| 323 | 361 | ||
| 324 | private HistoricTaskInstance getHistoricTask(String id) { | 362 | private HistoricTaskInstance getHistoricTask(String id) { |
| @@ -329,9 +367,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -329,9 +367,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 329 | public List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId) { | 367 | public List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId) { |
| 330 | // 当前任务 task | 368 | // 当前任务 task |
| 331 | Task task = getTask(taskId); | 369 | Task task = getTask(taskId); |
| 332 | - if (null == task) { | ||
| 333 | - throw exception(TASK_NOT_EXISTS); | ||
| 334 | - } | ||
| 335 | // 根据流程定义获取流程模型信息 | 370 | // 根据流程定义获取流程模型信息 |
| 336 | BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); | 371 | BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); |
| 337 | // 查询该任务的前置任务节点的key集合 | 372 | // 查询该任务的前置任务节点的key集合 |
| @@ -372,8 +407,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -372,8 +407,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 372 | return Collections.emptySet(); | 407 | return Collections.emptySet(); |
| 373 | } | 408 | } |
| 374 | 409 | ||
| 375 | - @Transactional(rollbackFor = Exception.class) | ||
| 376 | @Override | 410 | @Override |
| 411 | + @Transactional(rollbackFor = Exception.class) | ||
| 377 | public void returnTask(BpmTaskReturnReqVO reqVO) { | 412 | public void returnTask(BpmTaskReturnReqVO reqVO) { |
| 378 | // 当前任务 task | 413 | // 当前任务 task |
| 379 | Task task = validateReturnTask(reqVO.getId()); | 414 | Task task = validateReturnTask(reqVO.getId()); |
| @@ -396,9 +431,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -396,9 +431,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 396 | private Task validateReturnTask(String taskId) { | 431 | private Task validateReturnTask(String taskId) { |
| 397 | // 当前任务 task | 432 | // 当前任务 task |
| 398 | Task task = getTask(taskId); | 433 | Task task = getTask(taskId); |
| 399 | - if (null == task) { | ||
| 400 | - throw exception(TASK_NOT_EXISTS); | ||
| 401 | - } | ||
| 402 | if (task.isSuspended()) { | 434 | if (task.isSuspended()) { |
| 403 | throw exception(TASK_IS_PENDING); | 435 | throw exception(TASK_IS_PENDING); |
| 404 | } | 436 | } |
| @@ -468,4 +500,41 @@ public class BpmTaskServiceImpl implements BpmTaskService { | @@ -468,4 +500,41 @@ public class BpmTaskServiceImpl implements BpmTaskService { | ||
| 468 | .changeState(); | 500 | .changeState(); |
| 469 | } | 501 | } |
| 470 | 502 | ||
| 503 | + @Override | ||
| 504 | + @Transactional(rollbackFor = Exception.class) | ||
| 505 | + public void delegateTask(BpmTaskDelegateReqVO reqVO) { | ||
| 506 | + // 校验任务 | ||
| 507 | + Task task = this.validateTaskDelegate(reqVO); | ||
| 508 | + // 添加审批意见 | ||
| 509 | + AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()); | ||
| 510 | + AdminUserRespDTO receiveUser = adminUserApi.getUser(reqVO.getReceiveId()); | ||
| 511 | + String comment = String.format("[%s]将任务委派给[%s],委派理由为:%s", currentUser.getNickname(), | ||
| 512 | + receiveUser.getNickname(), reqVO.getReason()); | ||
| 513 | + String taskId = reqVO.getId(); | ||
| 514 | + taskService.addComment(taskId, task.getProcessInstanceId(), BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment); | ||
| 515 | + // 设置任务所有人 (owner) 为原任务的处理人 (assignee) | ||
| 516 | + taskService.setOwner(taskId, task.getAssignee()); | ||
| 517 | + // 执行委派,将任务委派给 receiveId | ||
| 518 | + taskService.delegateTask(taskId, reqVO.getReceiveId().toString()); | ||
| 519 | + // 更新任务拓展表为【委派】 | ||
| 520 | + taskExtMapper.updateByTaskId( | ||
| 521 | + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.DELEGATE.getResult()) | ||
| 522 | + .setReason(reqVO.getReason())); | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + /** | ||
| 526 | + * 校验任务委派参数 | ||
| 527 | + * | ||
| 528 | + * @param reqVO 任务编号,接收人ID | ||
| 529 | + * @return 当前任务信息 | ||
| 530 | + */ | ||
| 531 | + private Task validateTaskDelegate(BpmTaskDelegateReqVO reqVO) { | ||
| 532 | + // 校验任务 | ||
| 533 | + Task task = checkTask(WebFrameworkUtils.getLoginUserId(), reqVO.getId()); | ||
| 534 | + //校验当前审批人和被委派人不是同一人 | ||
| 535 | + if (task.getAssignee().equals(reqVO.getReceiveId().toString())) { | ||
| 536 | + throw exception(TASK_DELEGATE_USER_REPEAT); | ||
| 537 | + } | ||
| 538 | + return task; | ||
| 539 | + } | ||
| 471 | } | 540 | } |