gssong
2024-03-17 5360ec6ec3bf356a554df624e884a0bb1090fa4b
add 添加节点审批记录,调整流程驳回
已添加5个文件
已修改7个文件
428 ■■■■ 文件已修改
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/sql/flowable.sql 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java
@@ -99,4 +99,14 @@
     * æ¨¡åž‹æ ‡è¯†key命名规范正则表达式
     */
    String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$";
    /**
     * ç”¨æˆ·ä»»åŠ¡
     */
    String USER_TASK = "userTask";
    /**
     * ä¼šç­¾
     */
    String MULTI_INSTANCE = "multiInstance";
}
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.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 java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * ä»»åŠ¡ç®¡ç† æŽ§åˆ¶å±‚
@@ -39,6 +41,8 @@
    private final IActTaskService actTaskService;
    private final TaskService taskService;
    private final IWfTaskBackNodeService iWfTaskBackNodeService;
    /**
@@ -220,7 +224,7 @@
    @Log(title = "任务管理", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping("/backProcess")
    public R<String> backProcess(@RequestBody BackProcessBo backProcessBo) {
    public R<String> backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo backProcessBo) {
        return R.ok(actTaskService.backProcess(backProcessBo));
    }
@@ -264,7 +268,7 @@
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @GetMapping("/getTaskNodeList/{processInstanceId}")
    public R<Set<TaskVo>> getNodeList(@PathVariable String processInstanceId) {
        return R.ok(actTaskService.getTaskNodeList(processInstanceId));
    public R<List<WfTaskBackNode>> getNodeList(@PathVariable String processInstanceId) {
        return R.ok(CollUtil.reverse(iWfTaskBackNodeService.getListByInstanceId(processInstanceId)));
    }
}
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;
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java
@@ -34,6 +34,7 @@
    /**
     * é©³å›žçš„节点id(目前未使用,直接驳回到申请人)
     */
    @NotBlank(message = "驳回的节点不能为空", groups = AddGroup.class)
    private String targetActivityId;
    /**
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<WfTaskBackNode, WfTaskBackNode> {
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java
@@ -8,7 +8,6 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * ä»»åŠ¡ æœåС层
@@ -143,12 +142,4 @@
     * @return ç»“æžœ
     */
    List<VariableVo> getInstanceVariable(String taskId);
    /**
     * èŽ·å–å¯é©³å›žå¾—ä»»åŠ¡èŠ‚ç‚¹
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     * @return ç»“æžœ
     */
    Set<TaskVo> getTaskNodeList(String processInstanceId);
}
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<WfTaskBackNode> 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<String> processInstanceIds);
}
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java
@@ -32,6 +32,7 @@
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 @@
    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 @@
            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
            }
            iWfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
@@ -534,6 +537,7 @@
            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
            }
            iWfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
@@ -551,6 +555,7 @@
    public boolean deleteFinishAndHisInstance(List<String> processInstanceIds) {
        try {
            historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
            iWfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java
@@ -19,6 +19,8 @@
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.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 @@
    private final ManagementService managementService;
    private final FlowEventStrategy flowEventStrategy;
    private final ActTaskMapper actTaskMapper;
    private final IWfTaskBackNodeService iWfTaskBackNodeService;
    private final ActHiTaskinstMapper actHiTaskinstMapper;
    /**
     * å¯åŠ¨ä»»åŠ¡
@@ -187,12 +193,14 @@
            //办理意见
            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 @@
                List<Task> 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<Task> taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list();
                    WorkflowUtils.createCopyTask(taskList, StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserId));
@@ -244,12 +251,7 @@
        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 @@
        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 @@
     */
    @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 @@
    @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 @@
            BusinessStatusEnum.checkBackStatus(processInstance.getBusinessStatus());
            //判断是否有多个任务
            List<Task> 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<Task> tasks = StreamUtils.filter(taskList, e -> !e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey()));
                actHiTaskinstMapper.deleteBatchIds(StreamUtils.toList(tasks, Task::getId));
            }
            List<HistoricTaskInstance> instanceList = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().desc().list();
            List<Task> 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 @@
                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());
        }
@@ -696,25 +716,5 @@
            }
        }
        return variableVoList;
    }
    /**
     * èŽ·å–å¯é©³å›žå¾—ä»»åŠ¡èŠ‚ç‚¹
     *
     * @param processInstanceId æµç¨‹å®žä¾‹id
     */
    @Override
    public Set<TaskVo> getTaskNodeList(String processInstanceId) {
        Set<TaskVo> list = new HashSet<>();
        List<HistoricTaskInstance> 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;
    }
}
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<WfTaskBackNode> 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<WfTaskBackNode> getListByInstanceId(String processInstanceId) {
        LambdaQueryWrapper<WfTaskBackNode> 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<WfTaskBackNode> 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<WfTaskBackNode> 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<WfTaskBackNode> taskNodeList = getListByInstanceId(processInstanceId);
                List<Long> 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<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId);
        List<WfTaskBackNode> list = wfTaskBackNodeMapper.selectList(wrapper);
        int delete = wfTaskBackNodeMapper.delete(wrapper);
        if (list.size() != delete) {
            throw new ServiceException("删除失败");
        }
        return true;
    }
    @Override
    public boolean deleteByInstanceIds(List<String> processInstanceIds) {
        LambdaQueryWrapper<WfTaskBackNode> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(WfTaskBackNode::getInstanceId, processInstanceIds);
        List<WfTaskBackNode> list = wfTaskBackNodeMapper.selectList(wrapper);
        int delete = wfTaskBackNodeMapper.delete(wrapper);
        if (list.size() != delete) {
            throw new ServiceException("删除失败");
        }
        return true;
    }
}
ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.workflow.mapper.WfTaskBackNodeMapper">
</mapper>
script/sql/flowable.sql
@@ -58,6 +58,26 @@
) 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, '');