From 5360ec6ec3bf356a554df624e884a0bb1090fa4b Mon Sep 17 00:00:00 2001 From: gssong <1742057357@qq.com> Date: Sun, 17 Mar 2024 18:25:20 +0800 Subject: [PATCH] =?UTF-8?q?add=20=E6=B7=BB=E5=8A=A0=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E5=AE=A1=E6=89=B9=E8=AE=B0=E5=BD=95=EF=BC=8C=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E9=A9=B3=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/constant/FlowConstant.java | 10 ++ .../controller/ActTaskController.java | 12 +- .../workflow/domain/WfTaskBackNode.java | 61 ++++++++ .../workflow/domain/bo/BackProcessBo.java | 1 + .../workflow/mapper/WfTaskBackNodeMapper.java | 13 ++ .../workflow/service/IActTaskService.java | 9 -- .../service/IWfTaskBackNodeService.java | 65 ++++++++ .../impl/ActProcessInstanceServiceImpl.java | 5 + .../service/impl/ActTaskServiceImpl.java | 84 +++++------ .../impl/WfTaskBackNodeServiceImpl.java | 141 ++++++++++++++++++ .../mapper/workflow/WfTaskBackNodeMapper.xml | 7 + script/sql/flowable.sql | 20 +++ 12 files changed, 373 insertions(+), 55 deletions(-) create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java index 1e9d79b36..77996aeb5 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -99,4 +99,14 @@ public interface FlowConstant { * 模型标识key命名规范正则表达式 */ String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$"; + + /** + * 用户任务 + */ + String USER_TASK = "userTask"; + + /** + * 会签 + */ + String MULTI_INSTANCE = "multiInstance"; } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java index 9f50cdce4..d4949594b 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java @@ -1,5 +1,6 @@ package org.dromara.workflow.controller; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; @@ -12,10 +13,12 @@ import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.WfTaskBackNode; import org.dromara.workflow.domain.bo.*; import org.dromara.workflow.domain.vo.TaskVo; import org.dromara.workflow.domain.vo.VariableVo; import org.dromara.workflow.service.IActTaskService; +import org.dromara.workflow.service.IWfTaskBackNodeService; import org.dromara.workflow.utils.QueryUtils; import org.flowable.engine.TaskService; import org.springframework.validation.annotation.Validated; @@ -23,7 +26,6 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; -import java.util.Set; /** * 任务管理 控制层 @@ -40,6 +42,8 @@ public class ActTaskController extends BaseController { private final TaskService taskService; + private final IWfTaskBackNodeService iWfTaskBackNodeService; + /** * 启动任务 @@ -220,7 +224,7 @@ public class ActTaskController extends BaseController { @Log(title = "任务管理", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping("/backProcess") - public R backProcess(@RequestBody BackProcessBo backProcessBo) { + public R backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo backProcessBo) { return R.ok(actTaskService.backProcess(backProcessBo)); } @@ -264,7 +268,7 @@ public class ActTaskController extends BaseController { * @param processInstanceId 流程实例id */ @GetMapping("/getTaskNodeList/{processInstanceId}") - public R> getNodeList(@PathVariable String processInstanceId) { - return R.ok(actTaskService.getTaskNodeList(processInstanceId)); + public R> getNodeList(@PathVariable String processInstanceId) { + return R.ok(CollUtil.reverse(iWfTaskBackNodeService.getListByInstanceId(processInstanceId))); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java new file mode 100644 index 000000000..6f5972794 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java @@ -0,0 +1,61 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; + +/** + * 节点驳回记录 wf_task_back_node + * + * @author may + * @date 2024-03-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wf_task_back_node") +public class WfTaskBackNode extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 实例id + */ + private String instanceId; + + /** + * 节点id + */ + private String nodeId; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 排序 + */ + private Integer orderNo; + + /** + * 节点类型 + */ + private String taskType; + + /** + * 办理人 + */ + private String assignee; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java index 7ac6b38e5..d0f436925 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java @@ -34,6 +34,7 @@ public class BackProcessBo implements Serializable { /** * 驳回的节点id(目前未使用,直接驳回到申请人) */ + @NotBlank(message = "驳回的节点不能为空", groups = AddGroup.class) private String targetActivityId; /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java new file mode 100644 index 000000000..9b291feb4 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java @@ -0,0 +1,13 @@ +package org.dromara.workflow.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.domain.WfTaskBackNode; + +/** + * 节点驳回记录Mapper接口 + * + * @author may + * @date 2024-03-13 + */ +public interface WfTaskBackNodeMapper extends BaseMapperPlus { +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java index 72a5ea11e..45c3de230 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java @@ -8,7 +8,6 @@ import org.dromara.workflow.domain.vo.VariableVo; import java.util.List; import java.util.Map; -import java.util.Set; /** * 任务 服务层 @@ -143,12 +142,4 @@ public interface IActTaskService { * @return 结果 */ List getInstanceVariable(String taskId); - - /** - * 获取可驳回得任务节点 - * - * @param processInstanceId 流程实例id - * @return 结果 - */ - Set getTaskNodeList(String processInstanceId); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java new file mode 100644 index 000000000..97f940671 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java @@ -0,0 +1,65 @@ +package org.dromara.workflow.service; + + +import org.dromara.workflow.domain.WfTaskBackNode; +import org.flowable.task.api.Task; + +import java.util.List; + +/** + * 节点驳回记录Service接口 + * + * @author may + * @date 2024-03-13 + */ +public interface IWfTaskBackNodeService { + + /** + * 记录审批节点 + * + * @param task 任务 + */ + void recordExecuteNode(Task task); + + /** + * 按流程实例id查询 + * + * @param processInstanceId 流程实例id + * @return 结果 + */ + List getListByInstanceId(String processInstanceId); + + /** + * 按照流程实例id,节点id查询 + * + * @param processInstanceId 流程实例id + * @param nodeId 节点id + * @return 结果 + */ + WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId); + + /** + * 删除驳回后的节点 + * + * @param processInstanceId 流程实例id + * @param targetActivityId 节点id + * @return 结果 + */ + boolean deleteBackTaskNode(String processInstanceId, String targetActivityId); + + /** + * 按流程实例id删除 + * + * @param processInstanceId 流程实例id + * @return 结果 + */ + boolean deleteByInstanceId(String processInstanceId); + + /** + * 按流程实例id删除 + * + * @param processInstanceIds 流程实例id + * @return 结果 + */ + boolean deleteByInstanceIds(List processInstanceIds); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java index 3f8678a05..f4e8df26f 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java @@ -32,6 +32,7 @@ import org.dromara.workflow.flowable.strategy.FlowEventStrategy; import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler; import org.dromara.workflow.service.IActHiProcinstService; import org.dromara.workflow.service.IActProcessInstanceService; +import org.dromara.workflow.service.IWfTaskBackNodeService; import org.dromara.workflow.utils.QueryUtils; import org.dromara.workflow.utils.WorkflowUtils; import org.flowable.bpmn.model.*; @@ -77,6 +78,7 @@ public class ActProcessInstanceServiceImpl implements IActProcessInstanceService private final IActHiProcinstService actHiProcinstService; private final ManagementService managementService; private final FlowEventStrategy flowEventStrategy; + private final IWfTaskBackNodeService iWfTaskBackNodeService; @Value("${flowable.activity-font-name}") private String activityFontName; @@ -500,6 +502,7 @@ public class ActProcessInstanceServiceImpl implements IActProcessInstanceService if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) { historyService.bulkDeleteHistoricProcessInstances(processInstanceIds); } + iWfTaskBackNodeService.deleteByInstanceIds(processInstanceIds); return true; } catch (Exception e) { e.printStackTrace(); @@ -534,6 +537,7 @@ public class ActProcessInstanceServiceImpl implements IActProcessInstanceService if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) { historyService.bulkDeleteHistoricProcessInstances(processInstanceIds); } + iWfTaskBackNodeService.deleteByInstanceIds(processInstanceIds); return true; } catch (Exception e) { e.printStackTrace(); @@ -551,6 +555,7 @@ public class ActProcessInstanceServiceImpl implements IActProcessInstanceService public boolean deleteFinishAndHisInstance(List processInstanceIds) { try { historyService.bulkDeleteHistoricProcessInstances(processInstanceIds); + iWfTaskBackNodeService.deleteByInstanceIds(processInstanceIds); return true; } catch (Exception e) { e.printStackTrace(); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java index b623eab04..d35e930ca 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java @@ -19,6 +19,8 @@ import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.BusinessStatusEnum; import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.dromara.workflow.domain.ActHiTaskinst; +import org.dromara.workflow.domain.WfTaskBackNode; import org.dromara.workflow.domain.bo.*; import org.dromara.workflow.domain.vo.MultiInstanceVo; import org.dromara.workflow.domain.vo.TaskVo; @@ -28,8 +30,10 @@ import org.dromara.workflow.flowable.cmd.*; import org.dromara.workflow.flowable.strategy.FlowEventStrategy; import org.dromara.workflow.flowable.strategy.FlowProcessEventHandler; import org.dromara.workflow.flowable.strategy.FlowTaskEventHandler; +import org.dromara.workflow.mapper.ActHiTaskinstMapper; import org.dromara.workflow.mapper.ActTaskMapper; import org.dromara.workflow.service.IActTaskService; +import org.dromara.workflow.service.IWfTaskBackNodeService; import org.dromara.workflow.utils.QueryUtils; import org.dromara.workflow.utils.WorkflowUtils; import org.flowable.common.engine.api.FlowableObjectNotFoundException; @@ -71,6 +75,8 @@ public class ActTaskServiceImpl implements IActTaskService { private final ManagementService managementService; private final FlowEventStrategy flowEventStrategy; private final ActTaskMapper actTaskMapper; + private final IWfTaskBackNodeService iWfTaskBackNodeService; + private final ActHiTaskinstMapper actHiTaskinstMapper; /** * 启动任务 @@ -187,12 +193,14 @@ public class ActTaskServiceImpl implements IActTaskService { //办理意见 taskService.addComment(completeTaskBo.getTaskId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isBlank(completeTaskBo.getMessage()) ? "同意" : completeTaskBo.getMessage()); //办理任务 - taskService.setAssignee(task.getId(),userId); + taskService.setAssignee(task.getId(), userId); if (CollUtil.isNotEmpty(completeTaskBo.getVariables())) { taskService.complete(completeTaskBo.getTaskId(), completeTaskBo.getVariables()); } else { taskService.complete(completeTaskBo.getTaskId()); } + //记录执行过的流程任务节点 + iWfTaskBackNodeService.recordExecuteNode(task); ProcessInstance pi = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult(); if (pi == null) { UpdateBusinessStatusCmd updateBusinessStatusCmd = new UpdateBusinessStatusCmd(task.getProcessInstanceId(), BusinessStatusEnum.FINISH.getStatus()); @@ -204,8 +212,7 @@ public class ActTaskServiceImpl implements IActTaskService { List list = QueryUtils.taskQuery(task.getProcessInstanceId()).list(); if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(completeTaskBo.getWfCopyList())) { TaskEntity newTask = WorkflowUtils.createNewTask(task); - taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), - LoginHelper.getLoginUser().getNickname() + "【抄送】给" + String.join(",", StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserName))); + taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), LoginHelper.getLoginUser().getNickname() + "【抄送】给" + String.join(",", StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserName))); taskService.complete(newTask.getId()); List taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list(); WorkflowUtils.createCopyTask(taskList, StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserId)); @@ -244,12 +251,7 @@ public class ActTaskServiceImpl implements IActTaskService { String userId = String.valueOf(LoginHelper.getUserId()); queryWrapper.eq("t.business_status_", BusinessStatusEnum.WAITING.getStatus()); queryWrapper.eq(TenantHelper.isEnable(), "t.tenant_id_", TenantHelper.getTenantId()); - queryWrapper.and(w1 -> - w1.eq("t.assignee_", userId) - .or(w2 -> w2.isNull("t.assignee_") - .apply("exists ( select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = t.ID_ and LINK.TYPE_ = 'candidate' " + - "and (LINK.USER_ID_ = {0} or ( LINK.GROUP_ID_ IN " + getInParam(roleIds) + " ) ))", userId)) - ); + queryWrapper.and(w1 -> w1.eq("t.assignee_", userId).or(w2 -> w2.isNull("t.assignee_").apply("exists ( select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = t.ID_ and LINK.TYPE_ = 'candidate' " + "and (LINK.USER_ID_ = {0} or ( LINK.GROUP_ID_ IN " + getInParam(roleIds) + " ) ))", userId))); if (StringUtils.isNotBlank(taskBo.getName())) { queryWrapper.like("t.name_", taskBo.getName()); } @@ -444,8 +446,7 @@ public class ActTaskServiceImpl implements IActTaskService { if (task.isSuspended()) { throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); } - HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery() - .processInstanceId(task.getProcessInstanceId()).singleResult(); + HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus()); try { if (StringUtils.isBlank(terminationBo.getComment())) { @@ -480,8 +481,7 @@ public class ActTaskServiceImpl implements IActTaskService { */ @Override public boolean transferTask(TransmitBo transmitBo) { - Task task = QueryUtils.taskQuery().taskId(transmitBo.getTaskId()) - .taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())).singleResult(); + Task task = QueryUtils.taskQuery().taskId(transmitBo.getTaskId()).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())).singleResult(); if (ObjectUtil.isEmpty(task)) { throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); } @@ -607,7 +607,8 @@ public class ActTaskServiceImpl implements IActTaskService { @Transactional(rollbackFor = Exception.class) public String backProcess(BackProcessBo backProcessBo) { TaskQuery query = QueryUtils.taskQuery(); - Task task = query.taskId(backProcessBo.getTaskId()).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())).singleResult(); + String userId = String.valueOf(LoginHelper.getUserId()); + Task task = query.taskId(backProcessBo.getTaskId()).taskCandidateOrAssigned(userId).singleResult(); if (ObjectUtil.isEmpty(task)) { throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); } @@ -624,20 +625,33 @@ public class ActTaskServiceImpl implements IActTaskService { BusinessStatusEnum.checkBackStatus(processInstance.getBusinessStatus()); //判断是否有多个任务 List taskList = QueryUtils.taskQuery(processInstanceId).list(); - //申请人节点 - HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().asc().list().get(0); - String backTaskDefinitionKey = historicTaskInstance.getTaskDefinitionKey(); + String backTaskDefinitionKey = backProcessBo.getTargetActivityId(); taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.BACK.getStatus(), StringUtils.isNotBlank(backProcessBo.getMessage()) ? backProcessBo.getMessage() : "退回"); if (taskList.size() > 1) { //当前多个任务驳回到单个节点 runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdsToSingleActivityId(taskList.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList()), backTaskDefinitionKey).changeState(); + ActHiTaskinst actHiTaskinst = new ActHiTaskinst(); + actHiTaskinst.setAssignee(userId); + actHiTaskinst.setId(task.getId()); + actHiTaskinstMapper.updateById(actHiTaskinst); } else { //当前单个节点驳回单个节点 runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdTo(task.getTaskDefinitionKey(), backTaskDefinitionKey).changeState(); } + //删除并行环节未办理记录 + MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + if (multiInstance == null && taskList.size() > 1) { + List tasks = StreamUtils.filter(taskList, e -> !e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey())); + actHiTaskinstMapper.deleteBatchIds(StreamUtils.toList(tasks, Task::getId)); + } + + + List instanceList = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().desc().list(); List list = QueryUtils.taskQuery(processInstanceId).list(); for (Task t : list) { - taskService.setAssignee(t.getId(), historicTaskInstance.getAssignee()); + instanceList.stream().filter(e -> e.getTaskDefinitionKey().equals(t.getTaskDefinitionKey())).findFirst().ifPresent(e -> { + taskService.setAssignee(t.getId(), e.getAssignee()); + }); } //发送消息 String message = "您的【" + processInstance.getName() + "】单据已经被驳回,请您注意查收。"; @@ -647,11 +661,17 @@ public class ActTaskServiceImpl implements IActTaskService { DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId()); managementService.executeCommand(deleteExecutionCmd); } - runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.BACK.getStatus()); - FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey()); - if (processHandler != null) { - processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.BACK.getStatus(), false); + + WfTaskBackNode wfTaskBackNode = iWfTaskBackNodeService.getListByInstanceIdAndNodeId(task.getProcessInstanceId(), backProcessBo.getTargetActivityId()); + if (ObjectUtil.isNotNull(wfTaskBackNode) && wfTaskBackNode.getOrderNo() == 0) { + runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.BACK.getStatus()); + FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey()); + if (processHandler != null) { + processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.BACK.getStatus(), false); + } } + //删除驳回后的流程节点 + iWfTaskBackNodeService.deleteBackTaskNode(processInstanceId, backProcessBo.getTargetActivityId()); } catch (Exception e) { throw new ServiceException(e.getMessage()); } @@ -697,24 +717,4 @@ public class ActTaskServiceImpl implements IActTaskService { } return variableVoList; } - - /** - * 获取可驳回得任务节点 - * - * @param processInstanceId 流程实例id - */ - @Override - public Set getTaskNodeList(String processInstanceId) { - Set list = new HashSet<>(); - List historicTaskInstances = QueryUtils.hisTaskInstanceQuery(processInstanceId).orderByHistoricTaskInstanceEndTime().desc().list(); - for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) { - if (historicTaskInstance.getEndTime() != null) { - TaskVo taskVo = new TaskVo(); - taskVo.setName(historicTaskInstance.getName()); - taskVo.setTaskDefinitionKey(historicTaskInstance.getTaskDefinitionKey()); - list.add(taskVo); - } - } - return list; - } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java new file mode 100644 index 000000000..b65642939 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java @@ -0,0 +1,141 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.workflow.domain.WfTaskBackNode; +import org.dromara.workflow.domain.vo.MultiInstanceVo; +import org.dromara.workflow.mapper.WfTaskBackNodeMapper; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.WorkflowUtils; +import org.flowable.task.api.Task; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +import static org.dromara.workflow.common.constant.FlowConstant.MULTI_INSTANCE; +import static org.dromara.workflow.common.constant.FlowConstant.USER_TASK; + + +/** + * 节点驳回记录Service业务层处理 + * + * @author may + * @date 2024-03-13 + */ +@RequiredArgsConstructor +@Service +public class WfTaskBackNodeServiceImpl implements IWfTaskBackNodeService { + + private final WfTaskBackNodeMapper wfTaskBackNodeMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void recordExecuteNode(Task task) { + List list = getListByInstanceId(task.getProcessInstanceId()); + WfTaskBackNode wfTaskBackNode = new WfTaskBackNode(); + wfTaskBackNode.setNodeId(task.getTaskDefinitionKey()); + wfTaskBackNode.setNodeName(task.getName()); + wfTaskBackNode.setInstanceId(task.getProcessInstanceId()); + wfTaskBackNode.setAssignee(String.valueOf(LoginHelper.getUserId())); + MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + if (ObjectUtil.isNotEmpty(multiInstance)) { + wfTaskBackNode.setTaskType(MULTI_INSTANCE); + } else { + wfTaskBackNode.setTaskType(USER_TASK); + } + if (CollectionUtil.isEmpty(list)) { + wfTaskBackNode.setOrderNo(0); + wfTaskBackNodeMapper.insert(wfTaskBackNode); + } else { + WfTaskBackNode taskNode = list.stream().filter(e -> e.getNodeId().equals(wfTaskBackNode.getNodeId()) && e.getOrderNo() == 0).findFirst().orElse(null); + if (ObjectUtil.isEmpty(taskNode)) { + wfTaskBackNode.setOrderNo(list.get(0).getOrderNo() + 1); + WfTaskBackNode node = getListByInstanceIdAndNodeId(wfTaskBackNode.getInstanceId(), wfTaskBackNode.getNodeId()); + if (ObjectUtil.isNotEmpty(node)) { + node.setAssignee(node.getAssignee() + StringUtils.SEPARATOR + LoginHelper.getUserId()); + wfTaskBackNodeMapper.updateById(node); + } else { + wfTaskBackNodeMapper.insert(wfTaskBackNode); + } + } + } + } + + @Override + public List getListByInstanceId(String processInstanceId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + wrapper.orderByDesc(WfTaskBackNode::getOrderNo); + return wfTaskBackNodeMapper.selectList(wrapper); + } + + @Override + public WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + queryWrapper.eq(WfTaskBackNode::getNodeId, nodeId); + return wfTaskBackNodeMapper.selectOne(queryWrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteBackTaskNode(String processInstanceId, String targetActivityId) { + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + queryWrapper.eq(WfTaskBackNode::getNodeId, targetActivityId); + WfTaskBackNode actTaskNode = wfTaskBackNodeMapper.selectOne(queryWrapper); + if (ObjectUtil.isNotNull(actTaskNode)) { + Integer orderNo = actTaskNode.getOrderNo(); + List taskNodeList = getListByInstanceId(processInstanceId); + List ids = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(taskNodeList)) { + for (WfTaskBackNode taskNode : taskNodeList) { + if (taskNode.getOrderNo() >= orderNo) { + ids.add(taskNode.getId()); + } + } + } + if (CollectionUtil.isNotEmpty(ids)) { + wfTaskBackNodeMapper.deleteBatchIds(ids); + } + } + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException("删除失败"); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteByInstanceId(String processInstanceId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + List list = wfTaskBackNodeMapper.selectList(wrapper); + int delete = wfTaskBackNodeMapper.delete(wrapper); + if (list.size() != delete) { + throw new ServiceException("删除失败"); + } + return true; + } + + @Override + public boolean deleteByInstanceIds(List processInstanceIds) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.in(WfTaskBackNode::getInstanceId, processInstanceIds); + List list = wfTaskBackNodeMapper.selectList(wrapper); + int delete = wfTaskBackNodeMapper.delete(wrapper); + if (list.size() != delete) { + throw new ServiceException("删除失败"); + } + return true; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml new file mode 100644 index 000000000..4a9179b62 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/script/sql/flowable.sql b/script/sql/flowable.sql index b7149e572..7eb8d6b21 100644 --- a/script/sql/flowable.sql +++ b/script/sql/flowable.sql @@ -58,6 +58,26 @@ create table wf_category ) engine=innodb comment= '流程分类'; INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, sysdate(), 1, sysdate()); +DROP TABLE if EXISTS wf_task_back_node; +create table wf_task_back_node +( + id varchar(255) not null + primary key, + node_id varchar(255) not null comment '节点id', + node_name varchar(255) not null comment '节点名称', + order_no int not null comment '排序', + instance_id varchar(255) null comment '流程实例id', + task_type varchar(255) not null comment '节点类型', + assignee varchar(2000) not null comment '审批人', + tenant_id varchar(20) default '000000' null comment '租户编号', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间' +) + comment '节点审批记录'; + INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'demo:leave:list', '#', 103, 1, sysdate(), NULL, NULL, '请假申请菜单'); INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'demo:leave:query', '#', 103, 1, sysdate(), NULL, NULL, '');