Commit 80d532b0f159fb57d8be2c883aa1afd419a08acb

Authored by kehaiyou
1 parent 7d497ce0

feat: 【工作流】--减签

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 {
53 53 ErrorCode TASK_ADD_SIGN_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在");
54 54 ErrorCode TASK_ADD_SIGN_TYPE_ERROR = new ErrorCode(1_009_005_010, "任务加签:当前任务已经{},不能{}");
55 55 ErrorCode TASK_ADD_SIGN_USER_REPEAT = new ErrorCode(1_009_005_011, "任务加签失败,加签人与现有审批人[{}]重复");
  56 + ErrorCode TASK_SUB_SIGN_NO_PARENT = new ErrorCode(1_009_005_011, "任务减签失败,被减签的任务必须是通过加签生成的任务");
56 57 // ========== 流程任务分配规则 1-009-006-000 ==========
57 58 ErrorCode TASK_ASSIGN_RULE_EXISTS = new ErrorCode(1_009_006_000, "流程({}) 的任务({}) 已经存在分配规则");
58 59 ErrorCode TASK_ASSIGN_RULE_NOT_EXISTS = new ErrorCode(1_009_006_001, "流程任务分配规则不存在");
... ...
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 {
103 103 @PutMapping("/add-sign")
104 104 @Operation(summary = "加签", description = "before, after为前加签后加签")
105 105 @PreAuthorize("@ss.hasPermission('bpm:task:add-sign')")
106   - public CommonResult<Boolean> addSign(@RequestBody @Valid BpmTaskAddSignReqVO reqVO) {
  106 + public CommonResult<Boolean> addSign(@Valid @RequestBody BpmTaskAddSignReqVO reqVO) {
107 107 taskService.addSign(reqVO,getLoginUserId());
108 108 return success(true);
109 109 }
  110 +
  111 + @PutMapping("/sub-sign")
  112 + @Operation(summary = "减签", description = "")
  113 + @PreAuthorize("@ss.hasPermission('bpm:task:sub-sign')")
  114 + public CommonResult<Boolean> subSign(@Valid @RequestBody BpmTaskSubSignReqVO bpmTaskSubSignReqVO) {
  115 + taskService.subSign(bpmTaskSubSignReqVO,getLoginUserId());
  116 + return success(true);
  117 + }
  118 + @GetMapping("/get-sub-sign")
  119 + @Operation(summary = "获取能被减签的任务", description = "")
  120 + @PreAuthorize("@ss.hasPermission('bpm:task:sub-sign')")
  121 + public CommonResult<List<BpmTaskSubSignRespVO>> getChildrenTaskList(@RequestParam("taskId") String taskId) {
  122 + return success(taskService.getChildrenTaskList(taskId));
  123 + }
  124 +
110 125 }
... ...
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 {
19 19 */
20 20 private User assigneeUser;
21 21  
  22 + /**
  23 + * 父任务ID
  24 + */
  25 + private String parentTaskId;
  26 +
22 27 @Schema(description = "用户信息")
23 28 @Data
24 29 public static class User {
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignReqVO.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 +
  8 +@Schema(description = "管理后台 - 减签流程任务的 Request VO")
  9 +@Data
  10 +public class BpmTaskSubSignReqVO {
  11 +
  12 + @Schema(description = "被减签的任务 ID")
  13 + @NotEmpty(message = "任务编号不能为空")
  14 + private String id;
  15 +
  16 + @Schema(description = "加签原因")
  17 + @NotEmpty(message = "加签原因不能为空")
  18 + private String reason;
  19 +}
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskSubSignRespVO.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 +@Schema(description = "管理后台 - 减签流程任务的 Response VO")
  7 +@Data
  8 +public class BpmTaskSubSignRespVO {
  9 + @Schema(description = "审核的用户信息", requiredMode = Schema.RequiredMode.REQUIRED, example = "小李")
  10 + private BpmTaskRespVO.User assigneeUser;
  11 + @Schema(description = "任务 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "12312")
  12 + private String id;
  13 + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "经理审批")
  14 + private String name;
  15 +}
... ...
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;
4 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 5 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
6 6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
7   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO;
8   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
9   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskSimpleRespVO;
10   -import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskTodoPageItemRespVO;
  7 +import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
11 8 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
12 9 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO;
13 10 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
... ... @@ -162,4 +159,17 @@ public interface BpmTaskConvert {
162 159 task.setCreateTime(new Date());
163 160 return task;
164 161 }
  162 +
  163 + default List<BpmTaskSubSignRespVO> convertList(List<BpmTaskExtDO> bpmTaskExtDOList, Map<Long, AdminUserRespDTO> userMap){
  164 + return CollectionUtils.convertList(bpmTaskExtDOList, task->{
  165 + BpmTaskSubSignRespVO bpmTaskSubSignRespVO = new BpmTaskSubSignRespVO();
  166 + bpmTaskSubSignRespVO.setName(task.getName());
  167 + bpmTaskSubSignRespVO.setId(task.getTaskId());
  168 + AdminUserRespDTO assignUser = userMap.get(task.getAssigneeUserId());
  169 + if (assignUser != null) {
  170 + bpmTaskSubSignRespVO.setAssigneeUser(convert3(assignUser));
  171 + }
  172 + return bpmTaskSubSignRespVO;
  173 + });
  174 + }
165 175 }
... ...
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 {
163 163 */
164 164 void addSign(BpmTaskAddSignReqVO reqVO, Long userId);
165 165  
  166 + /**
  167 + * 减签
  168 + * @param bpmTaskSubSignReqVO 被减签的任务ID,理由
  169 + * @param loginUserId 当前用户ID
  170 + */
  171 + void subSign(BpmTaskSubSignReqVO bpmTaskSubSignReqVO, Long loginUserId);
  172 +
  173 + /**
  174 + * 获取指定任务的子任务和审批人信息
  175 + * @param taskId 指定任务ID
  176 + * @return 子任务列表
  177 + */
  178 + List<BpmTaskSubSignRespVO> getChildrenTaskList(String taskId);
166 179 }
... ...
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.*;
15 15 import cn.iocoder.yudao.module.bpm.convert.task.BpmTaskConvert;
16 16 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmTaskExtDO;
17 17 import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmTaskExtMapper;
  18 +import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
18 19 import cn.iocoder.yudao.module.bpm.enums.task.*;
19 20 import cn.iocoder.yudao.module.bpm.service.definition.BpmModelService;
20 21 import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService;
... ... @@ -264,7 +265,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
264 265 long subTaskCount = getSubTaskCount(parentTaskId);
265 266 if (subTaskCount == 0) {
266 267 //2. 获取父任务
267   - Task parentTask = getTask(parentTaskId);
  268 + Task parentTask = validateTaskExist(parentTaskId);
268 269 String scopeType = parentTask.getScopeType();
269 270 //3. 处理向前加签
270 271 if (BpmTaskAddSignTypeEnum.BEFORE.getType().equals(scopeType)) {
... ... @@ -319,7 +320,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
319 320 //此时,D 任务完成,要一直往上找到祖先任务 A ,调用 complete 方法完成 A 任务
320 321 boolean allChildrenTaskFinish = true;
321 322 while (StrUtil.isNotBlank(parentTask.getParentTaskId())) {
322   - parentTask = getTask(parentTask.getParentTaskId());
  323 + parentTask = validateTaskExist(parentTask.getParentTaskId());
323 324 BpmTaskExtDO bpmTaskExtDO = taskExtMapper.selectByTaskId(parentTask.getId());
324 325 if (bpmTaskExtDO == null) {
325 326 break;
... ... @@ -375,7 +376,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
375 376 String comment = StrUtil.format("[{}]完成委派任务,任务重新回到[{}]手中,审批意见为:{}", currentUser.getNickname(),
376 377 sourceApproveUser.getNickname(), reqVO.getReason());
377 378 taskService.addComment(reqVO.getId(), task.getProcessInstanceId(),
378   - BpmCommentTypeEnum.DELEGATE.getDesc(), comment);
  379 + BpmCommentTypeEnum.DELEGATE.getResult().toString(), comment);
379 380  
380 381 // 2.1 调用 resolveTask 完成任务。
381 382 // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee
... ... @@ -425,10 +426,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
425 426 * @param taskId task id
426 427 */
427 428 private Task validateTask(Long userId, String taskId) {
428   - Task task = getTask(taskId);
429   - if (task == null) {
430   - throw exception(TASK_NOT_EXISTS);
431   - }
  429 + Task task = validateTaskExist(taskId);
432 430 if (!Objects.equals(userId, NumberUtils.parseLong(task.getAssignee()))) {
433 431 throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF);
434 432 }
... ... @@ -507,6 +505,14 @@ public class BpmTaskServiceImpl implements BpmTaskService {
507 505 });
508 506 }
509 507  
  508 + private Task validateTaskExist(String id){
  509 + Task task = taskService.createTaskQuery().taskId(id).singleResult();
  510 + if(task == null){
  511 + throw exception(TASK_NOT_EXISTS);
  512 + }
  513 + return task;
  514 + }
  515 +
510 516 private Task getTask(String id) {
511 517 return taskService.createTaskQuery().taskId(id).singleResult();
512 518 }
... ... @@ -518,10 +524,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
518 524 @Override
519 525 public List<BpmTaskSimpleRespVO> getReturnTaskList(String taskId) {
520 526 // 1. 校验当前任务 task 存在
521   - Task task = getTask(taskId);
522   - if (task == null) {
523   - throw exception(TASK_NOT_EXISTS);
524   - }
  527 + Task task = validateTaskExist(taskId);
525 528 // 根据流程定义获取流程模型信息
526 529 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
527 530 FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
... ... @@ -609,7 +612,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
609 612 return;
610 613 }
611 614 taskService.addComment(task.getId(), currentTask.getProcessInstanceId(),
612   - BpmCommentTypeEnum.BACK.getDesc(), reqVO.getReason());
  615 + BpmCommentTypeEnum.BACK.getResult().toString(), reqVO.getReason());
613 616 });
614 617  
615 618 // 3. 执行驳回
... ... @@ -637,7 +640,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
637 640 delegateUser.getNickname(), reqVO.getReason());
638 641 String taskId = reqVO.getId();
639 642 taskService.addComment(taskId, task.getProcessInstanceId(),
640   - BpmCommentTypeEnum.DELEGATE.getDesc(), comment);
  643 + BpmCommentTypeEnum.DELEGATE.getResult().toString(), comment);
641 644  
642 645 // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee)
643 646 taskService.setOwner(taskId, task.getAssignee());
... ... @@ -702,7 +705,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
702 705 String comment = StrUtil.format("[{}]{}给了[{}],理由为:{}", currentUser.getNickname(), BpmTaskAddSignTypeEnum.formatDesc(reqVO.getType()),
703 706 String.join(SymbolConstant.D, convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason());
704 707 taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(),
705   - BpmCommentTypeEnum.ADD_SIGN.getDesc(), comment);
  708 + BpmCommentTypeEnum.ADD_SIGN.getResult().toString(), comment);
706 709 }
707 710  
708 711  
... ... @@ -776,6 +779,86 @@ public class BpmTaskServiceImpl implements BpmTaskService {
776 779 taskService.saveTask(task);
777 780 }
778 781  
  782 + @Override
  783 + @Transactional(rollbackFor = Exception.class)
  784 + public void subSign(BpmTaskSubSignReqVO reqVO,Long userId) {
  785 + Task task = validateSubSign(reqVO.getId());
  786 + AdminUserRespDTO user = adminUserApi.getUser(userId);
  787 + AdminUserRespDTO cancelUser = null;
  788 + if(StrUtil.isNotBlank(task.getAssignee())){
  789 + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee()));
  790 + }
  791 + if(cancelUser == null && StrUtil.isNotBlank(task.getOwner())){
  792 + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner()));
  793 + }
  794 + Assert.notNull(cancelUser,"任务中没有所有者和审批人,数据错误");
  795 + //1. 获取所有需要删除的任务 ID ,包含当前任务和所有子任务
  796 + List<String> allTaskIdList = getAllChildTaskIds(task.getId());
  797 + //2. 删除任务和所有子任务
  798 + taskService.deleteTasks(allTaskIdList);
  799 + //3. 修改扩展表状态为取消
  800 + taskExtMapper.updateBatchByTaskIdList(allTaskIdList,new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.CANCEL.getResult())
  801 + .setReason(StrUtil.format("由于{}操作[减签],任务被取消",user.getNickname())));
  802 + //4. 判断当前任务的父任务是否还有子任务
  803 + Task parentTask = validateTaskExist(task.getParentTaskId());
  804 + Long subTaskCount = getSubTaskCount(task.getParentTaskId());
  805 + if(subTaskCount == 0){
  806 + if(BpmTaskAddSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())){
  807 + //4.1 父任务是往前加签的,则进入判断,将当前任务的状态设置为进行中,并将 owner 设置回 assignee
  808 + taskExtMapper.updateByTaskId(new BpmTaskExtDO().setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()).setTaskId(task.getParentTaskId()));
  809 + parentTask.setAssignee(parentTask.getOwner());
  810 + parentTask.setOwner(null);
  811 + }
  812 + //4.2 清空 scopeType 字段,修改task
  813 + clearTaskScopeTypeAndSave(parentTask);
  814 + }
  815 + //5.记录日志到父任务中
  816 + String comment = StrUtil.format("{}操作了【减签】,审批人{}的任务被取消",user.getNickname(),cancelUser.getNickname());
  817 + taskService.addComment(parentTask.getId(),parentTask.getProcessInstanceId(),
  818 + BpmCommentTypeEnum.SUB_SIGN.getResult().toString(),comment);
  819 + }
  820 +
  821 + /**
  822 + * 校验任务是否能被减签
  823 + * @param id 任务ID
  824 + * @return 任务信息
  825 + */
  826 + private Task validateSubSign(String id) {
  827 + Task task = validateTaskExist(id);
  828 + //必须有parentId
  829 + if(StrUtil.isEmpty(task.getParentTaskId())){
  830 + throw exception(TASK_SUB_SIGN_NO_PARENT);
  831 + }
  832 + return task;
  833 + }
  834 +
  835 + /**
  836 + * 获取所有要被取消的删除的任务 ID 集合
  837 + * @param parentTaskId 父级任务ID
  838 + * @return 所有任务ID
  839 + */
  840 + public List<String> getAllChildTaskIds(String parentTaskId) {
  841 + List<String> allChildTaskIds = new ArrayList<>();
  842 + //1. 先将自己放入
  843 + allChildTaskIds.add(parentTaskId);
  844 + //2. 递归获取子级
  845 + recursiveGetChildTaskIds(parentTaskId, allChildTaskIds);
  846 + return allChildTaskIds;
  847 + }
  848 +
  849 + /**
  850 + * 递归处理子级任务
  851 + * @param taskId 当前任务ID
  852 + * @param taskIds 结果
  853 + */
  854 + private void recursiveGetChildTaskIds(String taskId, List<String> taskIds) {
  855 + List<String> childrenTaskIdList = getChildrenTaskIdList(taskId);
  856 + for (String childTaskId : childrenTaskIdList) {
  857 + taskIds.add(childTaskId); // 将子任务的ID添加到集合中
  858 + recursiveGetChildTaskIds(childTaskId, taskIds); // 递归获取子任务的子任务
  859 + }
  860 + }
  861 +
779 862 /**
780 863 * 获取指定父级任务的所有子任务 ID 集合
781 864 * @param parentTaskId 父任务 ID
... ... @@ -787,4 +870,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
787 870 List<Task> childrenTaskList = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list();
788 871 return convertList(childrenTaskList, Task::getId);
789 872 }
  873 +
  874 + @Override
  875 + public List<BpmTaskSubSignRespVO> getChildrenTaskList(String taskId){
  876 + List<String> childrenTaskIdList = getChildrenTaskIdList(taskId);
  877 + if(CollUtil.isEmpty(childrenTaskIdList)){
  878 + return Collections.emptyList();
  879 + }
  880 + List<BpmTaskExtDO> bpmTaskExtDOList = taskExtMapper.selectListByTaskIds(childrenTaskIdList);
  881 + Set<Long> assigneeUserIdSet = convertSet(bpmTaskExtDOList, BpmTaskExtDO::getAssigneeUserId);
  882 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIdSet);
  883 + return BpmTaskConvert.INSTANCE.convertList(bpmTaskExtDOList,userMap);
  884 + }
790 885 }
... ...