From 69e3afc7707d467b758858b52d3784947f7a502b Mon Sep 17 00:00:00 2001
From: 疯狂的狮子Li <15040126243@163.com>
Date: 星期一, 20 五月 2024 10:25:23 +0800
Subject: [PATCH] !538 ♥️发布 5.2.0-BETA 公测版本 Merge pull request !538 from 疯狂的狮子Li/dev

---
 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java |  683 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 683 insertions(+), 0 deletions(-)

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
new file mode 100644
index 0000000..b64c79e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java
@@ -0,0 +1,683 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.codec.Base64;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.ObjectUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.core.utils.StringUtils;
+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.workflow.common.constant.FlowConstant;
+import org.dromara.workflow.common.enums.BusinessStatusEnum;
+import org.dromara.workflow.common.enums.TaskStatusEnum;
+import org.dromara.workflow.domain.ActHiProcinst;
+import org.dromara.workflow.domain.bo.ProcessInstanceBo;
+import org.dromara.workflow.domain.bo.ProcessInvalidBo;
+import org.dromara.workflow.domain.bo.TaskUrgingBo;
+import org.dromara.workflow.domain.vo.*;
+import org.dromara.workflow.flowable.CustomDefaultProcessDiagramGenerator;
+import org.dromara.workflow.flowable.cmd.DeleteExecutionCmd;
+import org.dromara.workflow.flowable.cmd.ExecutionChildByExecutionIdCmd;
+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.IWfNodeConfigService;
+import org.dromara.workflow.service.IWfTaskBackNodeService;
+import org.dromara.workflow.utils.QueryUtils;
+import org.dromara.workflow.utils.WorkflowUtils;
+import org.flowable.bpmn.model.*;
+import org.flowable.engine.*;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.history.HistoricProcessInstanceQuery;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.runtime.ProcessInstanceQuery;
+import org.flowable.engine.task.Attachment;
+import org.flowable.engine.task.Comment;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.awt.*;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 娴佺▼瀹炰緥 鏈嶅姟灞傚疄鐜�
+ *
+ * @author may
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class ActProcessInstanceServiceImpl implements IActProcessInstanceService {
+
+    private final RepositoryService repositoryService;
+    private final RuntimeService runtimeService;
+    private final HistoryService historyService;
+    private final TaskService taskService;
+    private final IActHiProcinstService actHiProcinstService;
+    private final ManagementService managementService;
+    private final FlowEventStrategy flowEventStrategy;
+    private final IWfTaskBackNodeService wfTaskBackNodeService;
+    private final IWfNodeConfigService wfNodeConfigService;
+
+    @Value("${flowable.activity-font-name}")
+    private String activityFontName;
+
+    @Value("${flowable.label-font-name}")
+    private String labelFontName;
+
+    @Value("${flowable.annotation-font-name}")
+    private String annotationFontName;
+
+    /**
+     * 鍒嗛〉鏌ヨ姝e湪杩愯鐨勬祦绋嬪疄渚�
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<ProcessInstanceVo> getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) {
+        List<ProcessInstanceVo> list = new ArrayList<>();
+        ProcessInstanceQuery query = QueryUtils.instanceQuery();
+        if (StringUtils.isNotBlank(bo.getName())) {
+            query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(bo.getKey())) {
+            query.processDefinitionKey(bo.getKey());
+        }
+        if (StringUtils.isNotBlank(bo.getStartUserId())) {
+            query.startedBy(bo.getStartUserId());
+        }
+        if (StringUtils.isNotBlank(bo.getBusinessKey())) {
+            query.processInstanceBusinessKey(bo.getBusinessKey());
+        }
+        if (StringUtils.isNotBlank(bo.getCategoryCode())) {
+            query.processDefinitionCategory(bo.getCategoryCode());
+        }
+        query.orderByStartTime().desc();
+        List<ProcessInstance> processInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        for (ProcessInstance processInstance : processInstances) {
+            ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class);
+            processInstanceVo.setIsSuspended(processInstance.isSuspended());
+            processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus()));
+            list.add(processInstanceVo);
+        }
+        if (CollUtil.isNotEmpty(list)) {
+            List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (ProcessInstanceVo processInstanceVo : list) {
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
+                }
+            }
+        }
+        long count = query.count();
+        TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
+        build.setRows(list);
+        build.setTotal(count);
+        return build;
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ宸茬粨鏉熺殑娴佺▼瀹炰緥
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<ProcessInstanceVo> getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) {
+        List<ProcessInstanceVo> list = new ArrayList<>();
+        HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery()
+            .finished().orderByProcessInstanceEndTime().desc();
+        if (StringUtils.isNotEmpty(bo.getName())) {
+            query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(bo.getKey())) {
+            query.processDefinitionKey(bo.getKey());
+        }
+        if (StringUtils.isNotEmpty(bo.getStartUserId())) {
+            query.startedBy(bo.getStartUserId());
+        }
+        if (StringUtils.isNotBlank(bo.getBusinessKey())) {
+            query.processInstanceBusinessKey(bo.getBusinessKey());
+        }
+        if (StringUtils.isNotBlank(bo.getCategoryCode())) {
+            query.processDefinitionCategory(bo.getCategoryCode());
+        }
+        List<HistoricProcessInstance> historicProcessInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
+            ProcessInstanceVo processInstanceVo = BeanUtil.toBean(historicProcessInstance, ProcessInstanceVo.class);
+            processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(historicProcessInstance.getBusinessStatus()));
+            list.add(processInstanceVo);
+        }
+        if (CollUtil.isNotEmpty(list)) {
+            List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (ProcessInstanceVo processInstanceVo : list) {
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
+                }
+            }
+        }
+        long count = query.count();
+        TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
+        build.setRows(list);
+        build.setTotal(count);
+        return build;
+    }
+
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥�
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @SneakyThrows
+    @Override
+    public String getHistoryImage(String processInstanceId) {
+        String processDefinitionId;
+        // 鑾峰彇褰撳墠鐨勬祦绋嬪疄渚�
+        ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId).singleResult();
+        // 濡傛灉娴佺▼宸茬粡缁撴潫锛屽垯寰楀埌缁撴潫鑺傜偣
+        if (Objects.isNull(processInstance)) {
+            HistoricProcessInstance pi = QueryUtils.hisInstanceQuery(processInstanceId).singleResult();
+            processDefinitionId = pi.getProcessDefinitionId();
+        } else {
+            // 鏍规嵁娴佺▼瀹炰緥ID鑾峰緱褰撳墠澶勪簬娲诲姩鐘舵�佺殑ActivityId鍚堥泦
+            ProcessInstance pi = QueryUtils.instanceQuery(processInstanceId).singleResult();
+            processDefinitionId = pi.getProcessDefinitionId();
+        }
+
+        // 鑾峰緱娲诲姩鐨勮妭鐐�
+        List<HistoricActivityInstance> highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
+
+        List<String> highLightedFlows = new ArrayList<>();
+        List<String> highLightedNodes = new ArrayList<>();
+        //楂樹寒
+        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
+            if (FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType())) {
+                //楂樹寒绾�
+                highLightedFlows.add(tempActivity.getActivityId());
+            } else {
+                //楂樹寒鑺傜偣
+                if (tempActivity.getEndTime() == null) {
+                    highLightedNodes.add(Color.RED.toString() + tempActivity.getActivityId());
+                } else {
+                    highLightedNodes.add(tempActivity.getActivityId());
+                }
+            }
+        }
+        List<String> highLightedNodeList = new ArrayList<>();
+        //杩愯涓殑鑺傜偣
+        List<String> redNodeCollect = StreamUtils.filter(highLightedNodes, e -> e.contains(Color.RED.toString()));
+        //鎺掗櫎涓庤繍琛屼腑鐩稿悓鐨勮妭鐐�
+        for (String nodeId : highLightedNodes) {
+            if (!nodeId.contains(Color.RED.toString()) && !redNodeCollect.contains(Color.RED + nodeId)) {
+                highLightedNodeList.add(nodeId);
+            }
+        }
+        highLightedNodeList.addAll(redNodeCollect);
+        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
+        CustomDefaultProcessDiagramGenerator diagramGenerator = new CustomDefaultProcessDiagramGenerator();
+        InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodeList, highLightedFlows, activityFontName, labelFontName, annotationFontName, null, 1.0, true);
+        return Base64.encode(IoUtil.readBytes(inputStream));
+    }
+
+    /**
+     * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥捐繍琛屼腑锛屽巻鍙茬瓑鑺傜偣
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @Override
+    public Map<String, Object> getHistoryList(String processInstanceId) {
+        Map<String, Object> map = new HashMap<>();
+        List<Map<String, Object>> taskList = new ArrayList<>();
+        HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(processInstanceId).singleResult();
+        StringBuilder xml = new StringBuilder();
+        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());
+        // 鑾峰彇鑺傜偣
+        List<HistoricActivityInstance> highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
+        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
+            Map<String, Object> task = new HashMap<>();
+            if (!FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType()) &&
+                !FlowConstant.PARALLEL_GATEWAY.equals(tempActivity.getActivityType()) &&
+                !FlowConstant.EXCLUSIVE_GATEWAY.equals(tempActivity.getActivityType()) &&
+                !FlowConstant.INCLUSIVE_GATEWAY.equals(tempActivity.getActivityType())
+            ) {
+                task.put("key", tempActivity.getActivityId());
+                task.put("completed", tempActivity.getEndTime() != null);
+                task.put("activityType", tempActivity.getActivityType());
+                taskList.add(task);
+            }
+        }
+        ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId).singleResult();
+        if (processInstance != null) {
+            taskList = taskList.stream().filter(e -> !e.get("activityType").equals(FlowConstant.END_EVENT)).collect(Collectors.toList());
+        }
+        //鏌ヨ鍑鸿繍琛屼腑鑺傜偣
+        List<Map<String, Object>> runtimeNodeList = taskList.stream().filter(e -> !(Boolean) e.get("completed")).collect(Collectors.toList());
+        if (CollUtil.isNotEmpty(runtimeNodeList)) {
+            Iterator<Map<String, Object>> iterator = taskList.iterator();
+            while (iterator.hasNext()) {
+                Map<String, Object> next = iterator.next();
+                runtimeNodeList.stream().filter(t -> t.get("key").equals(next.get("key")) && (Boolean) next.get("completed")).findFirst().ifPresent(t -> iterator.remove());
+            }
+        }
+        map.put("taskList", taskList);
+        List<ActHistoryInfoVo> historyTaskList = getHistoryTaskList(processInstanceId);
+        map.put("historyList", historyTaskList);
+        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
+        xml.append(IoUtil.read(inputStream, StandardCharsets.UTF_8));
+        map.put("xml", xml.toString());
+        return map;
+    }
+
+    /**
+     * 鑾峰彇鍘嗗彶浠诲姟鑺傜偣淇℃伅
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    private List<ActHistoryInfoVo> getHistoryTaskList(String processInstanceId) {
+        //鏌ヨ浠诲姟鍔炵悊璁板綍
+        List<HistoricTaskInstance> list = QueryUtils.hisTaskInstanceQuery(processInstanceId).orderByHistoricTaskInstanceEndTime().desc().list();
+        list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed());
+        List<ActHistoryInfoVo> actHistoryInfoVoList = new ArrayList<>();
+        for (HistoricTaskInstance historicTaskInstance : list) {
+            ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo();
+            BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo);
+            actHistoryInfoVo.setStatus(actHistoryInfoVo.getEndTime() == null ? "寰呭鐞�" : "宸插鐞�");
+            if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) {
+                actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis()));
+            }
+            actHistoryInfoVoList.add(actHistoryInfoVo);
+        }
+        List<ActHistoryInfoVo> historyInfoVoList = new ArrayList<>();
+        Map<String, List<ActHistoryInfoVo>> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey);
+        for (Map.Entry<String, List<ActHistoryInfoVo>> entry : groupByKey.entrySet()) {
+            ActHistoryInfoVo historyInfoVo = new ActHistoryInfoVo();
+            BeanUtils.copyProperties(entry.getValue().get(0), historyInfoVo);
+            actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst()
+                .ifPresent(e -> {
+                    historyInfoVo.setStatus("寰呭鐞�");
+                    historyInfoVo.setStartTime(e.getStartTime());
+                    historyInfoVo.setEndTime(null);
+                    historyInfoVo.setRunDuration(null);
+                    if (ObjectUtil.isEmpty(e.getAssignee())) {
+                        ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(e.getId());
+                        if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) {
+                            historyInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr));
+                        }
+                    }
+                });
+            historyInfoVoList.add(historyInfoVo);
+        }
+        return historyInfoVoList;
+    }
+
+    /**
+     * 鑾峰彇瀹℃壒璁板綍
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @Override
+    public List<ActHistoryInfoVo> getHistoryRecord(String processInstanceId) {
+        // 鏌ヨ浠诲姟鍔炵悊璁板綍
+        List<HistoricTaskInstance> list = QueryUtils.hisTaskInstanceQuery(processInstanceId).orderByHistoricTaskInstanceEndTime().desc().list();
+        list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed());
+        List<ActHistoryInfoVo> actHistoryInfoVoList = new ArrayList<>();
+        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);
+        //闄勪欢
+        List<Attachment> attachmentList = taskService.getProcessInstanceAttachments(processInstanceId);
+        for (HistoricTaskInstance historicTaskInstance : list) {
+            ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo();
+            BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo);
+            if (actHistoryInfoVo.getEndTime() == null) {
+                actHistoryInfoVo.setStatus(TaskStatusEnum.WAITING.getStatus());
+                actHistoryInfoVo.setStatusName(TaskStatusEnum.WAITING.getDesc());
+            }
+            if (CollUtil.isNotEmpty(processInstanceComments)) {
+                processInstanceComments.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).findFirst().ifPresent(e -> {
+                    actHistoryInfoVo.setComment(e.getFullMessage());
+                    actHistoryInfoVo.setStatus(e.getType());
+                    actHistoryInfoVo.setStatusName(TaskStatusEnum.findByStatus(e.getType()));
+                });
+            }
+            if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) {
+                actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis()));
+            }
+            //闄勪欢
+            if (CollUtil.isNotEmpty(attachmentList)) {
+                List<Attachment> attachments = attachmentList.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).collect(Collectors.toList());
+                if (CollUtil.isNotEmpty(attachments)) {
+                    actHistoryInfoVo.setAttachmentList(attachments);
+                }
+            }
+            //璁剧疆浜哄憳id
+            if (ObjectUtil.isEmpty(historicTaskInstance.getAssignee())) {
+                ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(historicTaskInstance.getId());
+                if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) {
+                    actHistoryInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr));
+                }
+            }
+            actHistoryInfoVoList.add(actHistoryInfoVo);
+        }
+        // 瀹℃壒璁板綍
+        Map<String, List<ActHistoryInfoVo>> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey);
+        for (Map.Entry<String, List<ActHistoryInfoVo>> entry : groupByKey.entrySet()) {
+            ActHistoryInfoVo actHistoryInfoVo = BeanUtil.toBean(entry.getValue().get(0), ActHistoryInfoVo.class);
+            actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() != null).findFirst()
+                .ifPresent(e -> {
+                    actHistoryInfoVo.setStatus("宸插鐞�");
+                    actHistoryInfoVo.setStartTime(e.getStartTime());
+                });
+            actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst()
+                .ifPresent(e -> {
+                    actHistoryInfoVo.setStatus("寰呭鐞�");
+                    actHistoryInfoVo.setStartTime(e.getStartTime());
+                    actHistoryInfoVo.setEndTime(null);
+                    actHistoryInfoVo.setRunDuration(null);
+                });
+        }
+        List<ActHistoryInfoVo> recordList = new ArrayList<>();
+        // 寰呭姙鐞�
+        recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() == null));
+        // 宸插姙鐞�
+        recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() != null));
+
+        return recordList;
+    }
+
+    /**
+     * 浠诲姟瀹屾垚鏃堕棿澶勭悊
+     *
+     * @param time 鏃堕棿
+     */
+    private String getDuration(long time) {
+
+        long day = time / (24 * 60 * 60 * 1000);
+        long hour = (time / (60 * 60 * 1000) - day * 24);
+        long minute = ((time / (60 * 1000)) - day * 24 * 60 - hour * 60);
+        long second = (time / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60);
+
+        if (day > 0) {
+            return day + "澶�" + hour + "灏忔椂" + minute + "鍒嗛挓";
+        }
+        if (hour > 0) {
+            return hour + "灏忔椂" + minute + "鍒嗛挓";
+        }
+        if (minute > 0) {
+            return minute + "鍒嗛挓";
+        }
+        if (second > 0) {
+            return second + "绉�";
+        } else {
+            return 0 + "绉�";
+        }
+    }
+
+    /**
+     * 浣滃簾娴佺▼瀹炰緥锛屼笉浼氬垹闄ゅ巻鍙茶褰�(鍒犻櫎杩愯涓殑瀹炰緥)
+     *
+     * @param processInvalidBo 鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteRunInstance(ProcessInvalidBo processInvalidBo) {
+        try {
+            List<Task> list = QueryUtils.taskQuery(processInvalidBo.getProcessInstanceId()).list();
+            List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
+            if (CollUtil.isNotEmpty(subTasks)) {
+                subTasks.forEach(e -> taskService.deleteTask(e.getId()));
+            }
+            String deleteReason = LoginHelper.getLoginUser().getNickname() + "浣滃簾浜嗗綋鍓嶇敵璇凤紒";
+            if (StringUtils.isNotBlank(processInvalidBo.getDeleteReason())) {
+                deleteReason = LoginHelper.getLoginUser().getNickname() + "浣滃簾鐞嗙敱:" + processInvalidBo.getDeleteReason();
+            }
+            for (Task task : StreamUtils.filter(list, e -> StringUtils.isBlank(e.getParentTaskId()))) {
+                taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.INVALID.getStatus(), deleteReason);
+            }
+            HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(processInvalidBo.getProcessInstanceId()).singleResult();
+            BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus());
+            runtimeService.updateBusinessStatus(processInvalidBo.getProcessInstanceId(), BusinessStatusEnum.INVALID.getStatus());
+            runtimeService.deleteProcessInstance(processInvalidBo.getProcessInstanceId(), deleteReason);
+            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(historicProcessInstance.getProcessDefinitionKey());
+            if (processHandler != null) {
+                processHandler.handleProcess(historicProcessInstance.getBusinessKey(), BusinessStatusEnum.INVALID.getStatus(), false);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteRunAndHisInstance(List<String> processInstanceIds) {
+        try {
+            // 1.鍒犻櫎杩愯涓祦绋嬪疄渚�
+            List<Task> list = QueryUtils.taskQuery(processInstanceIds).list();
+            List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
+            if (CollUtil.isNotEmpty(subTasks)) {
+                subTasks.forEach(e -> taskService.deleteTask(e.getId()));
+            }
+            runtimeService.bulkDeleteProcessInstances(processInstanceIds, LoginHelper.getUserId() + "鍒犻櫎浜嗗綋鍓嶆祦绋嬬敵璇�");
+            // 2.鍒犻櫎鍘嗗彶璁板綍
+            List<HistoricProcessInstance> historicProcessInstanceList = QueryUtils.hisInstanceQuery(new HashSet<>(processInstanceIds)).list();
+            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
+                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
+            }
+            wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 鎸夌収涓氬姟id鍒犻櫎 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param businessKeys 涓氬姟id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteRunAndHisInstanceByBusinessKeys(List<String> businessKeys) {
+        try {
+            // 1.鍒犻櫎杩愯涓祦绋嬪疄渚�
+            List<ActHiProcinst> actHiProcinsts = actHiProcinstService.selectByBusinessKeyIn(businessKeys);
+            if (CollUtil.isEmpty(actHiProcinsts)) {
+                log.warn("褰撳墠涓氬姟ID:{}鏌ヨ鍒版祦绋嬪疄渚嬩负绌猴紒", businessKeys);
+                return false;
+            }
+            List<String> processInstanceIds = StreamUtils.toList(actHiProcinsts, ActHiProcinst::getId);
+            List<Task> list = QueryUtils.taskQuery(processInstanceIds).list();
+            List<Task> subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId()));
+            if (CollUtil.isNotEmpty(subTasks)) {
+                subTasks.forEach(e -> taskService.deleteTask(e.getId()));
+            }
+            runtimeService.bulkDeleteProcessInstances(processInstanceIds, LoginHelper.getUserId() + "鍒犻櫎浜嗗綋鍓嶆祦绋嬬敵璇�");
+            // 2.鍒犻櫎鍘嗗彶璁板綍
+            List<HistoricProcessInstance> historicProcessInstanceList = QueryUtils.hisInstanceQuery(new HashSet<>(processInstanceIds)).list();
+            if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) {
+                historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
+            }
+            wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 宸插畬鎴愮殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+     *
+     * @param processInstanceIds 娴佺▼瀹炰緥id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteFinishAndHisInstance(List<String> processInstanceIds) {
+        try {
+            historyService.bulkDeleteHistoricProcessInstances(processInstanceIds);
+            wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds);
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException(e.getMessage());
+        }
+    }
+
+    /**
+     * 鎾ら攢娴佺▼鐢宠
+     *
+     * @param processInstanceId 娴佺▼瀹炰緥id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean cancelProcessApply(String processInstanceId) {
+        try {
+            ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId)
+                .startedBy(String.valueOf(LoginHelper.getUserId())).singleResult();
+            if (ObjectUtil.isNull(processInstance)) {
+                throw new ServiceException("鎮ㄤ笉鏄祦绋嬪彂璧蜂汉,鎾ら攢澶辫触!");
+            }
+            if (processInstance.isSuspended()) {
+                throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED);
+            }
+            BusinessStatusEnum.checkCancelStatus(processInstance.getBusinessStatus());
+            List<Task> taskList = QueryUtils.taskQuery(processInstanceId).list();
+            for (Task task : taskList) {
+                taskService.setAssignee(task.getId(), null);
+                taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.CANCEL.getStatus(), LoginHelper.getLoginUser().getNickname() + "锛氭挙閿�鐢宠");
+            }
+            HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery().finished().orderByHistoricTaskInstanceEndTime().asc().list().get(0);
+            List<String> nodeIds = StreamUtils.toList(taskList, Task::getTaskDefinitionKey);
+            runtimeService.createChangeActivityStateBuilder()
+                .processInstanceId(processInstanceId)
+                .moveActivityIdsToSingleActivityId(nodeIds, historicTaskInstance.getTaskDefinitionKey()).changeState();
+            Task task = QueryUtils.taskQuery(processInstanceId).list().get(0);
+            taskService.setAssignee(task.getId(), historicTaskInstance.getAssignee());
+            //鑾峰彇骞惰缃戝叧鎵ц鍚庝繚鐣欑殑鎵ц瀹炰緥鏁版嵁
+            ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId());
+            List<ExecutionEntity> executionEntities = managementService.executeCommand(childByExecutionIdCmd);
+            //鍒犻櫎娴佺▼瀹炰緥鍨冨溇鏁版嵁
+            for (ExecutionEntity executionEntity : executionEntities) {
+                DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId());
+                managementService.executeCommand(deleteExecutionCmd);
+            }
+            runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.CANCEL.getStatus());
+            FlowProcessEventHandler processHandler = flowEventStrategy.getProcessHandler(processInstance.getProcessDefinitionKey());
+            if (processHandler != null) {
+                processHandler.handleProcess(processInstance.getBusinessKey(), BusinessStatusEnum.CANCEL.getStatus(), false);
+            }
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ServiceException("鎾ら攢澶辫触:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ褰撳墠鐧诲綍浜哄崟鎹�
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    public TableDataInfo<ProcessInstanceVo> getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) {
+        List<ProcessInstanceVo> list = new ArrayList<>();
+        HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery();
+        query.startedBy(String.valueOf(LoginHelper.getUserId()));
+        if (StringUtils.isNotBlank(bo.getName())) {
+            query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%");
+        }
+        if (StringUtils.isNotBlank(bo.getKey())) {
+            query.processDefinitionKey(bo.getKey());
+        }
+        if (StringUtils.isNotBlank(bo.getBusinessKey())) {
+            query.processInstanceBusinessKey(bo.getBusinessKey());
+        }
+        if (StringUtils.isNotBlank(bo.getCategoryCode())) {
+            query.processDefinitionCategory(bo.getCategoryCode());
+        }
+        query.orderByProcessInstanceStartTime().desc();
+        List<HistoricProcessInstance> historicProcessInstanceList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize());
+        List<TaskVo> taskVoList = new ArrayList<>();
+        if (CollUtil.isNotEmpty(historicProcessInstanceList)) {
+            List<String> processInstanceIds = StreamUtils.toList(historicProcessInstanceList, HistoricProcessInstance::getId);
+            List<Task> taskList = QueryUtils.taskQuery(processInstanceIds).list();
+            for (Task task : taskList) {
+                taskVoList.add(BeanUtil.toBean(task, TaskVo.class));
+            }
+        }
+        for (HistoricProcessInstance processInstance : historicProcessInstanceList) {
+            ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class);
+            processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus()));
+            if (CollUtil.isNotEmpty(taskVoList)) {
+                List<TaskVo> collect = StreamUtils.filter(taskVoList, e -> e.getProcessInstanceId().equals(processInstance.getId()));
+                processInstanceVo.setTaskVoList(CollUtil.isNotEmpty(collect) ? collect : Collections.emptyList());
+            }
+            list.add(processInstanceVo);
+        }
+        if (CollUtil.isNotEmpty(list)) {
+            List<String> processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId);
+            List<WfNodeConfigVo> wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds);
+            for (ProcessInstanceVo processInstanceVo : list) {
+                if (CollUtil.isNotEmpty(wfNodeConfigVoList)) {
+                    wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo);
+                }
+            }
+        }
+        long count = query.count();
+        TableDataInfo<ProcessInstanceVo> build = TableDataInfo.build();
+        build.setRows(list);
+        build.setTotal(count);
+        return build;
+    }
+
+    /**
+     * 浠诲姟鍌姙(缁欏綋鍓嶄换鍔″姙鐞嗕汉鍙戦�佺珯鍐呬俊锛岄偖浠讹紝鐭俊绛�)
+     *
+     * @param taskUrgingBo 浠诲姟鍌姙
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean taskUrging(TaskUrgingBo taskUrgingBo) {
+        try {
+            ProcessInstance processInstance = QueryUtils.instanceQuery(taskUrgingBo.getProcessInstanceId()).singleResult();
+            if (processInstance == null) {
+                throw new ServiceException("浠诲姟宸茬粨鏉燂紒");
+            }
+            String message = taskUrgingBo.getMessage();
+            if (StringUtils.isBlank(message)) {
+                message = "鎮ㄧ殑銆�" + processInstance.getName() + "銆戝崟鎹繕鏈鎵癸紝璇锋偍鍙婃椂澶勭悊銆�";
+            }
+            List<Task> list = QueryUtils.taskQuery(taskUrgingBo.getProcessInstanceId()).list();
+            WorkflowUtils.sendMessage(list, processInstance.getName(), taskUrgingBo.getMessageType(), message);
+        } catch (ServiceException e) {
+            throw new ServiceException(e.getMessage());
+        }
+        return true;
+    }
+}

--
Gitblit v1.9.3