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 48b71ef72..b411055f6 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_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_004, "目标节点是在并行网关上或非同一路线上,不可跳转"); ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_005, " 目标节点不存在"); ErrorCode TASK_RETURN_FAIL = new ErrorCode(1_009_005_006, "回退任务失败,选择回退的节点没有需要回滚的任务!请重新选择其他任务节点"); + ErrorCode TASK_DELEGATE_APPROVE_FAIL = new ErrorCode(1_009_005_007, "任务审批失败:委派任务找不到原审批人"); + ErrorCode TASK_DELEGATE_USER_REPEAT = new ErrorCode(1_009_005_008, "任务委派失败,委派人和当前审批人为同一人"); // ========== 流程任务分配规则 1-009-006-000 ========== ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则"); ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在"); 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 d7470495e..d9efd2b7b 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); + 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..444ac9e8b --- /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 receiveId; + + @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 1c327ca78..b953b2200 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,10 @@ public interface BpmTaskService { */ void returnTask(BpmTaskReturnReqVO reqVO); + /** + * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批 + * + * @param reqVO 接收人和被委派的任务编号理由参数 + */ + void delegateTask(BpmTaskDelegateReqVO reqVO); } 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 a7d1496a7..eed82fd4a 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 @@ -2,16 +2,19 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; 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.ModelUtils; +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; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper; +import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; @@ -29,6 +32,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; @@ -192,13 +196,43 @@ 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())); + if (sourceApproveUser == null) { + throw exception(TASK_DELEGATE_APPROVE_FAIL); + } + String comment = String.format("[%s]完成委派任务,任务重新回到[%s]手中,审批意见为:%s", 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())); } @@ -318,7 +352,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; } private HistoricTaskInstance getHistoricTask(String id) { @@ -329,9 +367,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { public List getReturnTaskList(String taskId) { // 当前任务 task Task task = getTask(taskId); - if (null == task) { - throw exception(TASK_NOT_EXISTS); - } // 根据流程定义获取流程模型信息 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); // 查询该任务的前置任务节点的key集合 @@ -372,8 +407,8 @@ public class BpmTaskServiceImpl implements BpmTaskService { return Collections.emptySet(); } - @Transactional(rollbackFor = Exception.class) @Override + @Transactional(rollbackFor = Exception.class) public void returnTask(BpmTaskReturnReqVO reqVO) { // 当前任务 task Task task = validateReturnTask(reqVO.getId()); @@ -396,9 +431,6 @@ public class BpmTaskServiceImpl implements BpmTaskService { private Task validateReturnTask(String taskId) { // 当前任务 task Task task = getTask(taskId); - if (null == task) { - throw exception(TASK_NOT_EXISTS); - } if (task.isSuspended()) { throw exception(TASK_IS_PENDING); } @@ -468,4 +500,41 @@ public class BpmTaskServiceImpl implements BpmTaskService { .changeState(); } + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(BpmTaskDelegateReqVO reqVO) { + // 校验任务 + Task task = this.validateTaskDelegate(reqVO); + // 添加审批意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()); + AdminUserRespDTO receiveUser = adminUserApi.getUser(reqVO.getReceiveId()); + String comment = String.format("[%s]将任务委派给[%s],委派理由为:%s", currentUser.getNickname(), + receiveUser.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.getReceiveId().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.getReceiveId().toString())) { + throw exception(TASK_DELEGATE_USER_REPEAT); + } + return task; + } }