feat: 【工作流】--减签

This commit is contained in:
kehaiyou 2023-09-29 15:51:00 +08:00
parent 7d497ce09b
commit 80d532b0f1
8 changed files with 192 additions and 19 deletions

View File

@ -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, "流程任务分配规则不存在");

View File

@ -103,8 +103,23 @@ public class BpmTaskController {
@PutMapping("/add-sign")
@Operation(summary = "加签", description = "before, after为前加签后加签")
@PreAuthorize("@ss.hasPermission('bpm:task:add-sign')")
public CommonResult<Boolean> addSign(@RequestBody @Valid BpmTaskAddSignReqVO reqVO) {
public CommonResult<Boolean> 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<Boolean> 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<List<BpmTaskSubSignRespVO>> getChildrenTaskList(@RequestParam("taskId") String taskId) {
return success(taskService.getChildrenTaskList(taskId));
}
}

View File

@ -19,6 +19,11 @@ public class BpmTaskRespVO extends BpmTaskDonePageItemRespVO {
*/
private User assigneeUser;
/**
* 父任务ID
*/
private String parentTaskId;
@Schema(description = "用户信息")
@Data
public static class User {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<BpmTaskSubSignRespVO> convertList(List<BpmTaskExtDO> bpmTaskExtDOList, Map<Long, AdminUserRespDTO> 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;
});
}
}

View File

@ -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<BpmTaskSubSignRespVO> getChildrenTaskList(String taskId);
}

View File

@ -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<BpmTaskSimpleRespVO> 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<String> 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<String> getAllChildTaskIds(String parentTaskId) {
List<String> allChildTaskIds = new ArrayList<>();
//1. 先将自己放入
allChildTaskIds.add(parentTaskId);
//2. 递归获取子级
recursiveGetChildTaskIds(parentTaskId, allChildTaskIds);
return allChildTaskIds;
}
/**
* 递归处理子级任务
* @param taskId 当前任务ID
* @param taskIds 结果
*/
private void recursiveGetChildTaskIds(String taskId, List<String> taskIds) {
List<String> 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<Task> childrenTaskList = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list();
return convertList(childrenTaskList, Task::getId);
}
@Override
public List<BpmTaskSubSignRespVO> getChildrenTaskList(String taskId){
List<String> childrenTaskIdList = getChildrenTaskIdList(taskId);
if(CollUtil.isEmpty(childrenTaskIdList)){
return Collections.emptyList();
}
List<BpmTaskExtDO> bpmTaskExtDOList = taskExtMapper.selectListByTaskIds(childrenTaskIdList);
Set<Long> assigneeUserIdSet = convertSet(bpmTaskExtDOList, BpmTaskExtDO::getAssigneeUserId);
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assigneeUserIdSet);
return BpmTaskConvert.INSTANCE.convertList(bpmTaskExtDOList,userMap);
}
}