From f1208474f771a1c233d7425c8ed13fbaa0d521ac Mon Sep 17 00:00:00 2001
From: baoshiwei <baoshiwei@shlanbao.cn>
Date: 星期三, 12 三月 2025 09:35:13 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/5.X' into 5.X

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

diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java
new file mode 100644
index 0000000..db8ab71
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java
@@ -0,0 +1,451 @@
+package org.dromara.workflow.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.domain.dto.UserDTO;
+import org.dromara.common.core.enums.BusinessStatusEnum;
+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.warm.flow.core.FlowEngine;
+import org.dromara.warm.flow.core.constant.ExceptionCons;
+import org.dromara.warm.flow.core.dto.FlowParams;
+import org.dromara.warm.flow.core.entity.Definition;
+import org.dromara.warm.flow.core.entity.Instance;
+import org.dromara.warm.flow.core.entity.Task;
+import org.dromara.warm.flow.core.enums.NodeType;
+import org.dromara.warm.flow.core.service.ChartService;
+import org.dromara.warm.flow.core.service.DefService;
+import org.dromara.warm.flow.core.service.InsService;
+import org.dromara.warm.flow.core.service.TaskService;
+import org.dromara.warm.flow.orm.entity.FlowHisTask;
+import org.dromara.warm.flow.orm.entity.FlowInstance;
+import org.dromara.warm.flow.orm.entity.FlowTask;
+import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper;
+import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper;
+import org.dromara.workflow.common.ConditionalOnEnable;
+import org.dromara.workflow.common.enums.TaskStatusEnum;
+import org.dromara.workflow.domain.bo.FlowCancelBo;
+import org.dromara.workflow.domain.bo.FlowInstanceBo;
+import org.dromara.workflow.domain.bo.FlowInvalidBo;
+import org.dromara.workflow.domain.vo.FlowHisTaskVo;
+import org.dromara.workflow.domain.vo.FlowInstanceVo;
+import org.dromara.workflow.domain.vo.FlowVariableVo;
+import org.dromara.workflow.handler.FlowProcessEventHandler;
+import org.dromara.workflow.mapper.FlwCategoryMapper;
+import org.dromara.workflow.mapper.FlwInstanceMapper;
+import org.dromara.workflow.service.IFlwInstanceService;
+import org.dromara.workflow.service.IFlwTaskService;
+import org.dromara.workflow.utils.WorkflowUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 娴佺▼瀹炰緥 鏈嶅姟灞傚疄鐜�
+ *
+ * @author may
+ */
+@ConditionalOnEnable
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class FlwInstanceServiceImpl implements IFlwInstanceService {
+
+    private final InsService insService;
+    private final DefService defService;
+    private final ChartService chartService;
+    private final TaskService taskService;
+    private final FlowHisTaskMapper flowHisTaskMapper;
+    private final FlowInstanceMapper flowInstanceMapper;
+    private final FlowProcessEventHandler flowProcessEventHandler;
+    private final IFlwTaskService flwTaskService;
+    private final FlwInstanceMapper flwInstanceMapper;
+    private final FlwCategoryMapper flwCategoryMapper;
+
+    /**
+     * 鍒嗛〉鏌ヨ姝e湪杩愯鐨勬祦绋嬪疄渚�
+     *
+     * @param flowInstanceBo 娴佺▼瀹炰緥
+     * @param pageQuery      鍒嗛〉
+     */
+    @Override
+    public TableDataInfo<FlowInstanceVo> selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
+        QueryWrapper<FlowInstanceBo> queryWrapper = buildQueryWrapper(flowInstanceBo);
+        queryWrapper.in("fi.flow_status", BusinessStatusEnum.runningStatus());
+        Page<FlowInstanceVo> page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper);
+        return TableDataInfo.build(page);
+    }
+
+    /**
+     * 鍒嗛〉鏌ヨ宸茬粨鏉熺殑娴佺▼瀹炰緥
+     *
+     * @param flowInstanceBo 娴佺▼瀹炰緥
+     * @param pageQuery      鍒嗛〉
+     */
+    @Override
+    public TableDataInfo<FlowInstanceVo> selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) {
+        QueryWrapper<FlowInstanceBo> queryWrapper = buildQueryWrapper(flowInstanceBo);
+        queryWrapper.in("fi.flow_status", BusinessStatusEnum.finishStatus());
+        Page<FlowInstanceVo> page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper);
+        return TableDataInfo.build(page);
+    }
+
+    /**
+     * 鏍规嵁涓氬姟id鏌ヨ娴佺▼瀹炰緥璇︾粏淇℃伅
+     *
+     * @param businessId 涓氬姟id
+     * @return 缁撴灉
+     */
+    @Override
+    public FlowInstanceVo queryByBusinessId(Long businessId) {
+        FlowInstance instance = this.selectInstByBusinessId(String.valueOf(businessId));
+        FlowInstanceVo instanceVo = BeanUtil.toBean(instance, FlowInstanceVo.class);
+        Definition definition = defService.getById(instanceVo.getDefinitionId());
+        instanceVo.setFlowName(definition.getFlowName());
+        instanceVo.setFlowCode(definition.getFlowCode());
+        instanceVo.setVersion(definition.getVersion());
+        instanceVo.setFormCustom(definition.getFormCustom());
+        instanceVo.setFormPath(definition.getFormPath());
+        instanceVo.setCategory(definition.getCategory());
+        return instanceVo;
+    }
+
+    /**
+     * 閫氱敤鏌ヨ鏉′欢
+     *
+     * @param flowInstanceBo 鏌ヨ鏉′欢
+     * @return 鏌ヨ鏉′欢鏋勯�犳柟娉�
+     */
+    private QueryWrapper<FlowInstanceBo> buildQueryWrapper(FlowInstanceBo flowInstanceBo) {
+        QueryWrapper<FlowInstanceBo> queryWrapper = Wrappers.query();
+        queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getNodeName()), "fi.node_name", flowInstanceBo.getNodeName());
+        queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowName()), "fd.flow_name", flowInstanceBo.getFlowName());
+        queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowCode()), "fd.flow_code", flowInstanceBo.getFlowCode());
+        if (StringUtils.isNotBlank(flowInstanceBo.getCategory())) {
+            List<Long> categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowInstanceBo.getCategory()));
+            queryWrapper.in("fd.category", StreamUtils.toList(categoryIds, Convert::toStr));
+        }
+        queryWrapper.eq(StringUtils.isNotBlank(flowInstanceBo.getBusinessId()), "fi.business_id", flowInstanceBo.getBusinessId());
+        queryWrapper.in(CollUtil.isNotEmpty(flowInstanceBo.getCreateByIds()), "fi.create_by", flowInstanceBo.getCreateByIds());
+        queryWrapper.eq("fi.del_flag", "0");
+        queryWrapper.orderByDesc("fi.create_time");
+        return queryWrapper;
+    }
+
+    /**
+     * 鏍规嵁涓氬姟id鏌ヨ娴佺▼瀹炰緥
+     *
+     * @param businessId 涓氬姟id
+     */
+    @Override
+    public FlowInstance selectInstByBusinessId(String businessId) {
+        return flowInstanceMapper.selectOne(new LambdaQueryWrapper<FlowInstance>().eq(FlowInstance::getBusinessId, businessId));
+    }
+
+    /**
+     * 鎸夌収瀹炰緥id鏌ヨ娴佺▼瀹炰緥
+     *
+     * @param instanceId 瀹炰緥id
+     */
+    @Override
+    public FlowInstance selectInstById(Long instanceId) {
+        return flowInstanceMapper.selectById(instanceId);
+    }
+
+    /**
+     * 鎸夌収瀹炰緥id鏌ヨ娴佺▼瀹炰緥
+     *
+     * @param instanceIds 瀹炰緥id
+     */
+    @Override
+    public List<FlowInstance> selectInstListByIdList(List<Long> instanceIds) {
+        return flowInstanceMapper.selectByIds(instanceIds);
+    }
+
+    /**
+     * 鎸夌収涓氬姟id鍒犻櫎娴佺▼瀹炰緥
+     *
+     * @param businessIds 涓氬姟id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteByBusinessIds(List<Long> businessIds) {
+        List<FlowInstance> flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper<FlowInstance>().in(FlowInstance::getBusinessId, businessIds));
+        if (CollUtil.isEmpty(flowInstances)) {
+            log.warn("鏈壘鍒板搴旂殑娴佺▼瀹炰緥淇℃伅锛屾棤娉曟墽琛屽垹闄ゆ搷浣溿��");
+            return false;
+        }
+        return insService.remove(StreamUtils.toList(flowInstances, FlowInstance::getId));
+    }
+
+    /**
+     * 鎸夌収瀹炰緥id鍒犻櫎娴佺▼瀹炰緥
+     *
+     * @param instanceIds 瀹炰緥id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteByInstanceIds(List<Long> instanceIds) {
+        // 鑾峰彇瀹炰緥淇℃伅
+        List<Instance> instances = insService.getByIds(instanceIds);
+        if (CollUtil.isEmpty(instances)) {
+            log.warn("鏈壘鍒板搴旂殑娴佺▼瀹炰緥淇℃伅锛屾棤娉曟墽琛屽垹闄ゆ搷浣溿��");
+            return false;
+        }
+        // 鑾峰彇瀹氫箟淇℃伅
+        Map<Long, Definition> definitionMap = defService.getByIds(
+            StreamUtils.toList(instances, Instance::getDefinitionId)
+        ).stream().collect(Collectors.toMap(Definition::getId, definition -> definition));
+
+        // 閫愪竴瑙﹀彂鍒犻櫎浜嬩欢
+        instances.forEach(instance -> {
+            Definition definition = definitionMap.get(instance.getDefinitionId());
+            if (ObjectUtil.isNull(definition)) {
+                log.warn("瀹炰緥 ID: {} 瀵瑰簲鐨勬祦绋嬪畾涔変俊鎭湭鎵惧埌锛岃烦杩囧垹闄や簨浠惰Е鍙戙��", instance.getId());
+                return;
+            }
+            flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId());
+        });
+
+        // 鍒犻櫎瀹炰緥
+        return insService.remove(instanceIds);
+    }
+
+    /**
+     * 鎾ら攢娴佺▼
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean cancelProcessApply(FlowCancelBo bo) {
+        try {
+            Instance instance = selectInstByBusinessId(bo.getBusinessId());
+            if (instance == null) {
+                throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
+            }
+            Definition definition = defService.getById(instance.getDefinitionId());
+            if (definition == null) {
+                throw new ServiceException(ExceptionCons.NOT_FOUNT_DEF);
+            }
+            String message = bo.getMessage();
+            BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus());
+            String applyNodeCode = WorkflowUtils.applyNodeCode(definition.getId());
+            //鎾ら攢
+            WorkflowUtils.backTask(message, instance.getId(), applyNodeCode, BusinessStatusEnum.CANCEL.getStatus(), BusinessStatusEnum.CANCEL.getStatus());
+            //鍒ゆ柇鎴栫鑺傜偣鏄惁鏈夊涓紝鍙繚鐣欎竴涓�
+            List<Task> currentTaskList = taskService.list(FlowEngine.newTask().setInstanceId(instance.getId()));
+            if (CollUtil.isNotEmpty(currentTaskList)) {
+                if (currentTaskList.size() > 1) {
+                    currentTaskList.remove(0);
+                    WorkflowUtils.deleteRunTask(StreamUtils.toList(currentTaskList, Task::getId));
+                }
+            }
+
+        } catch (Exception e) {
+            log.error("鎾ら攢澶辫触: {}", e.getMessage(), e);
+            throw new ServiceException(e.getMessage());
+        }
+        return true;
+    }
+
+    /**
+     * 鑾峰彇褰撳墠鐧婚檰浜哄彂璧风殑娴佺▼瀹炰緥
+     *
+     * @param instanceBo 娴佺▼瀹炰緥
+     * @param pageQuery  鍒嗛〉
+     */
+    @Override
+    public TableDataInfo<FlowInstanceVo> selectCurrentInstanceList(FlowInstanceBo instanceBo, PageQuery pageQuery) {
+        QueryWrapper<FlowInstanceBo> queryWrapper = buildQueryWrapper(instanceBo);
+        queryWrapper.eq("fi.create_by", LoginHelper.getUserIdStr());
+        Page<FlowInstanceVo> page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper);
+        return TableDataInfo.build(page);
+    }
+
+    /**
+     * 鑾峰彇娴佺▼鍥�,娴佺▼璁板綍
+     *
+     * @param businessId 涓氬姟id
+     */
+    @Override
+    public Map<String, Object> flowImage(String businessId) {
+        FlowInstance flowInstance = this.selectInstByBusinessId(businessId);
+        if (ObjectUtil.isNull(flowInstance)) {
+            throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE);
+        }
+        Long instanceId = flowInstance.getId();
+        //杩愯涓殑浠诲姟
+        List<FlowHisTaskVo> list = new ArrayList<>();
+        List<FlowTask> flowTaskList = flwTaskService.selectByInstId(instanceId);
+        if (CollUtil.isNotEmpty(flowTaskList)) {
+            List<FlowHisTaskVo> flowHisTaskVos = BeanUtil.copyToList(flowTaskList, FlowHisTaskVo.class);
+            for (FlowHisTaskVo flowHisTaskVo : flowHisTaskVos) {
+                flowHisTaskVo.setFlowStatus(TaskStatusEnum.WAITING.getStatus());
+                flowHisTaskVo.setUpdateTime(null);
+                flowHisTaskVo.setRunDuration(null);
+                List<UserDTO> allUser = flwTaskService.currentTaskAllUser(flowHisTaskVo.getId());
+                if (CollUtil.isNotEmpty(allUser)) {
+                    String join = StreamUtils.join(allUser, e -> String.valueOf(e.getUserId()));
+                    flowHisTaskVo.setApprover(join);
+                }
+                if (BusinessStatusEnum.isDraftOrCancelOrBack(flowInstance.getFlowStatus())) {
+                    flowHisTaskVo.setApprover(LoginHelper.getUserIdStr());
+                    flowHisTaskVo.setApproveName(LoginHelper.getLoginUser().getNickname());
+                }
+            }
+            list.addAll(flowHisTaskVos);
+        }
+        //鍘嗗彶浠诲姟
+        LambdaQueryWrapper<FlowHisTask> wrapper = Wrappers.lambdaQuery();
+        wrapper.eq(FlowHisTask::getInstanceId, instanceId);
+        wrapper.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey());
+        wrapper.orderByDesc(FlowHisTask::getCreateTime).orderByDesc(FlowHisTask::getUpdateTime);
+        List<FlowHisTask> flowHisTasks = flowHisTaskMapper.selectList(wrapper);
+        if (CollUtil.isNotEmpty(flowHisTasks)) {
+            list.addAll(BeanUtil.copyToList(flowHisTasks, FlowHisTaskVo.class));
+        }
+        String flowChart = chartService.chartIns(instanceId);
+        return Map.of("list", list, "image", flowChart);
+    }
+
+    /**
+     * 鎸夌収瀹炰緥id鏇存柊鐘舵��
+     *
+     * @param instanceId 瀹炰緥id
+     * @param status     鐘舵��
+     */
+    @Override
+    public void updateStatus(Long instanceId, String status) {
+        LambdaUpdateWrapper<FlowInstance> wrapper = new LambdaUpdateWrapper<>();
+        wrapper.set(FlowInstance::getFlowStatus, status);
+        wrapper.eq(FlowInstance::getId, instanceId);
+        flowInstanceMapper.update(wrapper);
+    }
+
+    /**
+     * 鑾峰彇娴佺▼鍙橀噺
+     *
+     * @param instanceId 瀹炰緥id
+     */
+    @Override
+    public Map<String, Object> instanceVariable(Long instanceId) {
+        Map<String, Object> map = new HashMap<>();
+        FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId);
+        Map<String, Object> variableMap = flowInstance.getVariableMap();
+        List<FlowVariableVo> list = new ArrayList<>();
+        if (CollUtil.isNotEmpty(variableMap)) {
+            for (Map.Entry<String, Object> entry : variableMap.entrySet()) {
+                FlowVariableVo flowVariableVo = new FlowVariableVo();
+                flowVariableVo.setKey(entry.getKey());
+                flowVariableVo.setValue(entry.getValue().toString());
+                list.add(flowVariableVo);
+            }
+        }
+        map.put("variableList", list);
+        map.put("variable", flowInstance.getVariable());
+        return map;
+    }
+
+    /**
+     * 璁剧疆娴佺▼鍙橀噺
+     *
+     * @param instanceId 瀹炰緥id
+     * @param variable   娴佺▼鍙橀噺
+     */
+    @Override
+    public void setVariable(Long instanceId, Map<String, Object> variable) {
+        Instance instance = insService.getById(instanceId);
+        if (instance != null) {
+            taskService.mergeVariable(instance, variable);
+        }
+    }
+
+    /**
+     * 鎸変换鍔d鏌ヨ瀹炰緥
+     *
+     * @param taskId 浠诲姟id
+     */
+    @Override
+    public FlowInstance selectByTaskId(Long taskId) {
+        Task task = taskService.getById(taskId);
+        if (task == null) {
+            FlowHisTask flowHisTask = flwTaskService.selectHisTaskById(taskId);
+            if (flowHisTask != null) {
+                return this.selectInstById(flowHisTask.getInstanceId());
+            }
+        } else {
+            return this.selectInstById(task.getInstanceId());
+        }
+        return null;
+    }
+
+    /**
+     * 鎸変换鍔d鏌ヨ瀹炰緥
+     *
+     * @param taskIdList 浠诲姟id
+     */
+    @Override
+    public List<FlowInstance> selectByTaskIdList(List<Long> taskIdList) {
+        if (CollUtil.isEmpty(taskIdList)) {
+            return Collections.emptyList();
+        }
+        Set<Long> instanceIds = new HashSet<>();
+        List<FlowTask> flowTaskList = flwTaskService.selectByIdList(taskIdList);
+        for (FlowTask flowTask : flowTaskList) {
+            instanceIds.add(flowTask.getInstanceId());
+        }
+        List<FlowHisTask> flowHisTaskList = flwTaskService.selectHisTaskByIdList(taskIdList);
+        for (FlowHisTask flowHisTask : flowHisTaskList) {
+            instanceIds.add(flowHisTask.getInstanceId());
+        }
+        if (!instanceIds.isEmpty()) {
+            return this.selectInstListByIdList(new ArrayList<>(instanceIds));
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * 浣滃簾娴佺▼
+     *
+     * @param bo 鍙傛暟
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean processInvalid(FlowInvalidBo bo) {
+        try {
+            Instance instance = insService.getById(bo.getId());
+            if (instance != null) {
+                BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus());
+            }
+            List<FlowTask> flowTaskList = flwTaskService.selectByInstId(bo.getId());
+            for (FlowTask flowTask : flowTaskList) {
+                FlowParams flowParams = new FlowParams();
+                flowParams.message(bo.getComment());
+                flowParams.flowStatus(BusinessStatusEnum.INVALID.getStatus())
+                    .hisStatus(TaskStatusEnum.INVALID.getStatus());
+                flowParams.ignore(true);
+                taskService.termination(flowTask.getId(), flowParams);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            throw new ServiceException(e.getMessage());
+        }
+    }
+}

--
Gitblit v1.9.3