diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java index 79bd539ac..63f374822 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java +++ b/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 { ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在"); ErrorCode TASK_RETURN_FAIL_NO_RETURN_TASK = new ErrorCode(1_009_005_005, "回退任务失败,选择回退的节点没有需要回滚的任务!请重新选择其他任务节点"); ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "回退任务失败,目标节点是在并行网关上或非同一路线上,不可跳转"); + ErrorCode TASK_DELEGATE_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人"); + ErrorCode TASK_DELEGATE_USER_NULL = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在"); // ========== 流程任务分配规则 1-009-006-000 ========== ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则"); diff --git a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java b/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java index 3b57554cf..519c7bc4e 100644 --- a/yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmProcessInstanceResultEnum.java +++ b/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 { // ========== 流程任务独有的状态 ========== - BACK(5, "退回/驳回"); + BACK(5, "退回/驳回"), + + DELEGATE(6, "委派"); /** * 结果 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java index f1ac67579..bf849df0a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmTaskController.java +++ b/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 { return success(true); } + @PutMapping("/delegate") + @Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮,和向前加签有点像,和向前加签的唯一的区别是没有单独创立任务") + @PreAuthorize("@ss.hasPermission('bpm:task:delegate')") + public CommonResult delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) { + taskService.delegateTask(reqVO,getLoginUserId()); + return success(true); + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java new file mode 100644 index 000000000..295980345 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 委派流程任务的 Request VO") +@Data +public class BpmTaskDelegateReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "被委派人ID", requiredMode = Schema.RequiredMode.REQUIRED,example = "1") + @NotNull(message = "被委派人ID不能为空") + private Long delegateUserId; + + @Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED,example = "做不了决定,需要你先帮忙瞅瞅") + @NotEmpty(message = "委派原因不能为空") + private String reason; +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java index 23c4ed3bc..65645e9ea 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java @@ -137,4 +137,11 @@ public interface BpmTaskService { */ void returnTask(BpmTaskReturnReqVO reqVO); + /** + * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批 + * + * @param reqVO 被委派人和被委派的任务编号理由参数 + * @param userId 委派人ID + */ + void delegateTask(BpmTaskDelegateReqVO reqVO, Long userId); } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index 585295772..39c21046d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.framework.flowable.core.util.BpmnModelUtils; +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; @@ -29,6 +30,7 @@ import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.DelegationState; import org.flowable.task.api.Task; import org.flowable.task.api.TaskQuery; import org.flowable.task.api.history.HistoricTaskInstance; @@ -37,6 +39,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.util.Assert; import javax.annotation.Resource; import javax.validation.Valid; @@ -193,13 +196,42 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (instance == null) { throw exception(PROCESS_INSTANCE_NOT_EXISTS); } + //被委派的任务,不调用 complete 去完成任务 + if (DelegationState.PENDING.equals(task.getDelegationState())) { + this.approveDelegateTask(reqVO, task); + } else { + // 完成任务,审批通过 + taskService.complete(task.getId(), instance.getProcessVariables()); - // 完成任务,审批通过 - taskService.complete(task.getId(), instance.getProcessVariables()); + // 更新任务拓展表为通过 + taskExtMapper.updateByTaskId( + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) + .setReason(reqVO.getReason())); + } + } - // 更新任务拓展表为通过 + /** + * 审批被委派的任务 + * + * @param reqVO 前端请求参数,包含当前任务ID,审批意见等 + * @param task 当前被审批的任务 + */ + private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) { + // 添加审批意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()); + //原审批人 + AdminUserRespDTO sourceApproveUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); + Assert.notNull(sourceApproveUser, "委派任务找不到原审批人,需要检查数据"); + //添加审批意见 + String comment = StrUtil.format("[{}]完成委派任务,任务重新回到[{}]手中,审批意见为:{}", currentUser.getNickname(), + sourceApproveUser.getNickname(), reqVO.getReason()); + taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment); + //调用 resolveTask 完成任务,底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()); + //将 owner 设置为 assignee + taskService.resolveTask(task.getId()); + // 更新任务拓展表为【处理中】 taskExtMapper.updateByTaskId( - new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()) .setReason(reqVO.getReason())); } @@ -319,7 +351,11 @@ public class BpmTaskServiceImpl implements BpmTaskService { } private Task getTask(String id) { - return taskService.createTaskQuery().taskId(id).singleResult(); + Task task = taskService.createTaskQuery().taskId(id).singleResult(); + if (null == task) { + throw exception(TASK_NOT_EXISTS); + } + return task; } /** @@ -351,6 +387,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { if (task == null) { throw exception(TASK_NOT_EXISTS); } + // 根据流程定义获取流程模型信息 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); if (source == null) { @@ -445,4 +482,44 @@ public class BpmTaskServiceImpl implements BpmTaskService { .changeState(); } + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(BpmTaskDelegateReqVO reqVO,Long userId) { + // 校验任务 + Task task = this.validateTaskDelegate(reqVO); + // 添加审批意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId); + AdminUserRespDTO delegateUser = adminUserApi.getUser(reqVO.getDelegateUserId()); + if (delegateUser == null) { + throw exception(TASK_DELEGATE_USER_NULL); + } + String comment = StrUtil.format("[{}]将任务委派给[{}],委派理由为:{}", currentUser.getNickname(), + delegateUser.getNickname(), reqVO.getReason()); + String taskId = reqVO.getId(); + taskService.addComment(taskId, task.getProcessInstanceId(), BpmProcessInstanceResultEnum.DELEGATE.getResult().toString(), comment); + // 设置任务所有人 (owner) 为原任务的处理人 (assignee) + taskService.setOwner(taskId, task.getAssignee()); + // 执行委派,将任务委派给 receiveId + taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); + // 更新任务拓展表为【委派】 + taskExtMapper.updateByTaskId( + new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.DELEGATE.getResult()) + .setReason(reqVO.getReason())); + } + + /** + * 校验任务委派参数 + * + * @param reqVO 任务编号,接收人ID + * @return 当前任务信息 + */ + private Task validateTaskDelegate(BpmTaskDelegateReqVO reqVO) { + // 校验任务 + Task task = checkTask(WebFrameworkUtils.getLoginUserId(), reqVO.getId()); + //校验当前审批人和被委派人不是同一人 + if (task.getAssignee().equals(reqVO.getDelegateUserId().toString())) { + throw exception(TASK_DELEGATE_USER_REPEAT); + } + return task; + } }