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 55a2330c2..76c91a255 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 @@ -53,6 +53,7 @@ public interface ErrorCodeConstants { ErrorCode TASK_ADD_SIGN_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在"); ErrorCode TASK_ADD_SIGN_TYPE_ERROR = new ErrorCode(1_009_005_010, "任务加签:当前任务已经{},不能{}"); ErrorCode TASK_ADD_SIGN_USER_REPEAT = new ErrorCode(1_009_005_011, "任务加签失败,加签人与现有审批人[{}]重复"); + ErrorCode TASK_SUB_SIGN_NO_PARENT = new ErrorCode(1_009_005_011, "任务减签失败,被减签的任务必须是通过加签生成的任务"); // ========== 流程任务分配规则 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-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 e442d7aad..2f0e2db2a 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 @@ -103,8 +103,23 @@ public class BpmTaskController { @PutMapping("/add-sign") @Operation(summary = "加签", description = "before, after为前加签后加签") @PreAuthorize("@ss.hasPermission('bpm:task:add-sign')") - public CommonResult addSign(@RequestBody @Valid BpmTaskAddSignReqVO reqVO) { + public CommonResult addSign(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) { taskService.addSign(reqVO,getLoginUserId()); return success(true); } + + @PutMapping("/sub-sign") + @Operation(summary = "减签", description = "") + @PreAuthorize("@ss.hasPermission('bpm:task:sub-sign')") + public CommonResult subSign(@Valid @RequestBody BpmTaskSubSignReqVO bpmTaskSubSignReqVO) { + taskService.subSign(bpmTaskSubSignReqVO,getLoginUserId()); + return success(true); + } + @GetMapping("/get-sub-sign") + @Operation(summary = "获取能被减签的任务", description = "") + @PreAuthorize("@ss.hasPermission('bpm:task:sub-sign')") + public CommonResult> getChildrenTaskList(@RequestParam("taskId") String taskId) { + return success(taskService.getChildrenTaskList(taskId)); + } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index 3c404678f..198727dd6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -19,6 +19,11 @@ public class BpmTaskRespVO extends BpmTaskDonePageItemRespVO { */ private User assigneeUser; + /** + * 父任务ID + */ + private String parentTaskId; + @Schema(description = "用户信息") @Data public static class User { diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java new file mode 100644 index 000000000..665a9aec8 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.java @@ -0,0 +1,19 @@ +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; + +@Schema(description = "管理后台 - 减签流程任务的 Request VO") +@Data +public class BpmTaskSubSignReqVO { + + @Schema(description = "被减签的任务 ID") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "加签原因") + @NotEmpty(message = "加签原因不能为空") + private String reason; +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignRespVO.java new file mode 100644 index 000000000..efecbf582 --- /dev/null +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignRespVO.java @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 减签流程任务的 Response VO") +@Data +public class BpmTaskSubSignRespVO { + @Schema(description = "审核的用户信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "小李") + private BpmTaskRespVO.User assigneeUser; + @Schema(description = "任务 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "12312") + private String id; + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批") + private String name; +} diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java index ebbcf11fc..e61061c61 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java @@ -4,10 +4,7 @@ import cn.hutool.core.date.LocalDateTimeUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskSimpleRespVO; -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO; +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO; import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; @@ -162,4 +159,17 @@ public interface BpmTaskConvert { task.setCreateTime(new Date()); return task; } + + default List convertList(List bpmTaskExtDOList, Map userMap){ + return CollectionUtils.convertList(bpmTaskExtDOList, task->{ + BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO(); + bpmTaskSubSignRespVO.setName(task.getName()); + bpmTaskSubSignRespVO.setId(task.getTaskId()); + AdminUserRespDTO assignUser = userMap.get(task.getAssigneeUserId()); + if (assignUser != null) { + bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser)); + } + return bpmTaskSubSignRespVO; + }); + } } 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 2eb222b7c..3a7f84c07 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 @@ -163,4 +163,17 @@ public interface BpmTaskService { */ void addSign(BpmTaskAddSignReqVO reqVO, Long userId); + /** + * 减签 + * @param bpmTaskSubSignReqVO 被减签的任务ID,理由 + * @param loginUserId 当前用户ID + */ + void subSign(BpmTaskSubSignReqVO bpmTaskSubSignReqVO, Long loginUserId); + + /** + * 获取指定任务的子任务和审批人信息 + * @param taskId 指定任务ID + * @return 子任务列表 + */ + List getChildrenTaskList(String taskId); } 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 6e57fcff4..3dd806697 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 @@ -15,6 +15,7 @@ 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.*; import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; @@ -264,7 +265,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { long subTaskCount = getSubTaskCount(parentTaskId); if (subTaskCount == 0) { //2. 获取父任务 - Task parentTask = getTask(parentTaskId); + Task parentTask = validateTaskExist(parentTaskId); String scopeType = parentTask.getScopeType(); //3. 处理向前加签 if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) { @@ -319,7 +320,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { //此时,D 任务完成,要一直往上找到祖先任务 A ,调用 complete 方法完成 A 任务 boolean allChildrenTaskFinish = true; while (StrUtil.isNotBlank(parentTask.getParentTaskId())) { - parentTask = getTask(parentTask.getParentTaskId()); + parentTask = validateTaskExist(parentTask.getParentTaskId()); BpmTaskExtDO bpmTaskExtDO = taskExtMapper.selectByTaskId(parentTask.getId()); if (bpmTaskExtDO == null) { break; @@ -375,7 +376,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { String comment = StrUtil.format("[{}]完成委派任务,任务重新回到[{}]手中,审批意见为:{}", currentUser.getNickname(), sourceApproveUser.getNickname(), reqVO.getReason()); taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), - BpmCommentTypeEnum.DELEGATE.getDesc(), comment); + BpmCommentTypeEnum.DELEGATE.getResult().toString(), comment); // 2.1 调用 resolveTask 完成任务。 // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee @@ -425,10 +426,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { * @param taskId task id */ private Task validateTask(Long userId, String taskId) { - Task task = getTask(taskId); - if (task == null) { - throw exception(TASK_NOT_EXISTS); - } + Task task = validateTaskExist(taskId); if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) { throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); } @@ -507,6 +505,14 @@ public class BpmTaskServiceImpl implements BpmTaskService { }); } + private Task validateTaskExist(String id){ + Task task = taskService.createTaskQuery().taskId(id).singleResult(); + if(task == null){ + throw exception(TASK_NOT_EXISTS); + } + return task; + } + private Task getTask(String id) { return taskService.createTaskQuery().taskId(id).singleResult(); } @@ -518,10 +524,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @Override public List getReturnTaskList(String taskId) { // 1. 校验当前任务 task 存在 - Task task = getTask(taskId); - if (task == null) { - throw exception(TASK_NOT_EXISTS); - } + Task task = validateTaskExist(taskId); // 根据流程定义获取流程模型信息 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); @@ -609,7 +612,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { return; } taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), - BpmCommentTypeEnum.BACK.getDesc(), reqVO.getReason()); + BpmCommentTypeEnum.BACK.getResult().toString(), reqVO.getReason()); }); // 3. 执行驳回 @@ -637,7 +640,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { delegateUser.getNickname(), reqVO.getReason()); String taskId = reqVO.getId(); taskService.addComment(taskId, task.getProcessInstanceId(), - BpmCommentTypeEnum.DELEGATE.getDesc(), comment); + BpmCommentTypeEnum.DELEGATE.getResult().toString(), comment); // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) taskService.setOwner(taskId, task.getAssignee()); @@ -702,7 +705,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { String comment = StrUtil.format("[{}]{}给了[{}],理由为:{}", currentUser.getNickname(), BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()), String.join(SymbolConstant.D, convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), - BpmCommentTypeEnum.ADD_SIGN.getDesc(), comment); + BpmCommentTypeEnum.ADD_SIGN.getResult().toString(), comment); } @@ -776,6 +779,86 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskService.saveTask(task); } + @Override + @Transactional(rollbackFor = Exception.class) + public void subSign(BpmTaskSubSignReqVO reqVO,Long userId) { + Task task = validateSubSign(reqVO.getId()); + AdminUserRespDTO user = adminUserApi.getUser(userId); + AdminUserRespDTO cancelUser = null; + if(StrUtil.isNotBlank(task.getAssignee())){ + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())); + } + if(cancelUser == null && StrUtil.isNotBlank(task.getOwner())){ + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())); + } + Assert.notNull(cancelUser,"任务中没有所有者和审批人,数据错误"); + //1. 获取所有需要删除的任务 ID ,包含当前任务和所有子任务 + List allTaskIdList = getAllChildTaskIds(task.getId()); + //2. 删除任务和所有子任务 + taskService.deleteTasks(allTaskIdList); + //3. 修改扩展表状态为取消 + taskExtMapper.updateBatchByTaskIdList(allTaskIdList,new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()) + .setReason(StrUtil.format("由于{}操作[减签],任务被取消",user.getNickname()))); + //4. 判断当前任务的父任务是否还有子任务 + Task parentTask = validateTaskExist(task.getParentTaskId()); + Long subTaskCount = getSubTaskCount(task.getParentTaskId()); + if(subTaskCount == 0){ + if(BpmTaskAddSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())){ + //4.1 父任务是往前加签的,则进入判断,将当前任务的状态设置为进行中,并将 owner 设置回 assignee + taskExtMapper.updateByTaskId(new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()).setTaskId(task.getParentTaskId())); + parentTask.setAssignee(parentTask.getOwner()); + parentTask.setOwner(null); + } + //4.2 清空 scopeType 字段,修改task + clearTaskScopeTypeAndSave(parentTask); + } + //5.记录日志到父任务中 + String comment = StrUtil.format("{}操作了【减签】,审批人{}的任务被取消",user.getNickname(),cancelUser.getNickname()); + taskService.addComment(parentTask.getId(),parentTask.getProcessInstanceId(), + BpmCommentTypeEnum.SUB_SIGN.getResult().toString(),comment); + } + + /** + * 校验任务是否能被减签 + * @param id 任务ID + * @return 任务信息 + */ + private Task validateSubSign(String id) { + Task task = validateTaskExist(id); + //必须有parentId + if(StrUtil.isEmpty(task.getParentTaskId())){ + throw exception(TASK_SUB_SIGN_NO_PARENT); + } + return task; + } + + /** + * 获取所有要被取消的删除的任务 ID 集合 + * @param parentTaskId 父级任务ID + * @return 所有任务ID + */ + public List getAllChildTaskIds(String parentTaskId) { + List allChildTaskIds = new ArrayList<>(); + //1. 先将自己放入 + allChildTaskIds.add(parentTaskId); + //2. 递归获取子级 + recursiveGetChildTaskIds(parentTaskId, allChildTaskIds); + return allChildTaskIds; + } + + /** + * 递归处理子级任务 + * @param taskId 当前任务ID + * @param taskIds 结果 + */ + private void recursiveGetChildTaskIds(String taskId, List taskIds) { + List childrenTaskIdList = getChildrenTaskIdList(taskId); + for (String childTaskId : childrenTaskIdList) { + taskIds.add(childTaskId); // 将子任务的ID添加到集合中 + recursiveGetChildTaskIds(childTaskId, taskIds); // 递归获取子任务的子任务 + } + } + /** * 获取指定父级任务的所有子任务 ID 集合 * @param parentTaskId 父任务 ID @@ -787,4 +870,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { List childrenTaskList = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); return convertList(childrenTaskList, Task::getId); } + + @Override + public List getChildrenTaskList(String taskId){ + List childrenTaskIdList = getChildrenTaskIdList(taskId); + if(CollUtil.isEmpty(childrenTaskIdList)){ + return Collections.emptyList(); + } + List bpmTaskExtDOList = taskExtMapper.selectListByTaskIds(childrenTaskIdList); + Set assigneeUserIdSet = convertSet(bpmTaskExtDOList, BpmTaskExtDO::getAssigneeUserId); + Map userMap = adminUserApi.getUserMap(assigneeUserIdSet); + return BpmTaskConvert.INSTANCE.convertList(bpmTaskExtDOList,userMap); + } }