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; /** * 分页查询正在运行的流程实例 * * @param flowInstanceBo 流程实例 * @param pageQuery 分页 */ @Override public TableDataInfo selectRunningInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(flowInstanceBo); queryWrapper.in("fi.flow_status", BusinessStatusEnum.runningStatus()); Page page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper); return TableDataInfo.build(page); } /** * 分页查询已结束的流程实例 * * @param flowInstanceBo 流程实例 * @param pageQuery 分页 */ @Override public TableDataInfo selectFinishInstanceList(FlowInstanceBo flowInstanceBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(flowInstanceBo); queryWrapper.in("fi.flow_status", BusinessStatusEnum.finishStatus()); Page 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 buildQueryWrapper(FlowInstanceBo flowInstanceBo) { QueryWrapper 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 categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowInstanceBo.getCategory())); queryWrapper.in("fd.category", categoryIds); } 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().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 selectInstListByIdList(List instanceIds) { return flowInstanceMapper.selectByIds(instanceIds); } /** * 按照业务id删除流程实例 * * @param businessIds 业务id */ @Override @Transactional(rollbackFor = Exception.class) public boolean deleteByBusinessIds(List businessIds) { List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().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 instanceIds) { // 获取实例信息 List instances = insService.getByIds(instanceIds); if (CollUtil.isEmpty(instances)) { log.warn("未找到对应的流程实例信息,无法执行删除操作。"); return false; } // 获取定义信息 Map 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 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 selectCurrentInstanceList(FlowInstanceBo instanceBo, PageQuery pageQuery) { QueryWrapper queryWrapper = buildQueryWrapper(instanceBo); queryWrapper.eq("fi.create_by", LoginHelper.getUserIdStr()); Page page = flwInstanceMapper.selectInstanceList(pageQuery.build(), queryWrapper); return TableDataInfo.build(page); } /** * 获取流程图,流程记录 * * @param businessId 业务id */ @Override public Map flowImage(String businessId) { FlowInstance flowInstance = this.selectInstByBusinessId(businessId); if (ObjectUtil.isNull(flowInstance)) { throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); } Long instanceId = flowInstance.getId(); //运行中的任务 List list = new ArrayList<>(); List flowTaskList = flwTaskService.selectByInstId(instanceId); if (CollUtil.isNotEmpty(flowTaskList)) { List flowHisTaskVos = BeanUtil.copyToList(flowTaskList, FlowHisTaskVo.class); for (FlowHisTaskVo flowHisTaskVo : flowHisTaskVos) { flowHisTaskVo.setFlowStatus(TaskStatusEnum.WAITING.getStatus()); flowHisTaskVo.setUpdateTime(null); flowHisTaskVo.setRunDuration(null); List 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 wrapper = Wrappers.lambdaQuery(); wrapper.eq(FlowHisTask::getInstanceId, instanceId); wrapper.eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()); wrapper.orderByDesc(FlowHisTask::getCreateTime).orderByDesc(FlowHisTask::getUpdateTime); List 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 wrapper = new LambdaUpdateWrapper<>(); wrapper.set(FlowInstance::getFlowStatus, status); wrapper.eq(FlowInstance::getId, instanceId); flowInstanceMapper.update(wrapper); } /** * 获取流程变量 * * @param instanceId 实例id */ @Override public Map instanceVariable(Long instanceId) { Map map = new HashMap<>(); FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId); Map variableMap = flowInstance.getVariableMap(); List list = new ArrayList<>(); if (CollUtil.isNotEmpty(variableMap)) { for (Map.Entry 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 variable) { Instance instance = insService.getById(instanceId); if (instance != null) { taskService.mergeVariable(instance, variable); } } /** * 按任务id查询实例 * * @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; } /** * 按任务id查询实例 * * @param taskIdList 任务id */ @Override public List selectByTaskIdList(List taskIdList) { if (CollUtil.isEmpty(taskIdList)) { return Collections.emptyList(); } Set instanceIds = new HashSet<>(); List flowTaskList = flwTaskService.selectByIdList(taskIdList); for (FlowTask flowTask : flowTaskList) { instanceIds.add(flowTask.getInstanceId()); } List 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 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()); } } }