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.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.entity.User; import org.dromara.warm.flow.core.enums.NodeType; 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.bo.FlowVariableBo; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowInstanceVo; 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.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.function.Function; /** * 流程实例 服务层实现 * * @author may */ @ConditionalOnEnable @Slf4j @RequiredArgsConstructor @Service public class FlwInstanceServiceImpl implements IFlwInstanceService { private final InsService insService; private final DefService defService; 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(Convert.toStr(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", 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().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 = StreamUtils.toMap( defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)), Definition::getId, Function.identity() ); // 逐一触发删除事件 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); } /** * 按照实例id删除已完成的流程实例 * * @param instanceIds 实例id */ @Override @Transactional(rollbackFor = Exception.class) public boolean deleteHisByInstanceIds(List instanceIds) { // 获取实例信息 List instances = insService.getByIds(instanceIds); if (CollUtil.isEmpty(instances)) { log.warn("未找到对应的流程实例信息,无法执行删除操作。"); return false; } // 获取定义信息 Map definitionMap = StreamUtils.toMap( defService.getByIds(StreamUtils.toList(instances, Instance::getDefinitionId)), Definition::getId, Function.identity() ); // 逐一触发删除事件 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()); }); List flowTaskList = flwTaskService.selectByInstIds(instanceIds); if (CollUtil.isNotEmpty(flowTaskList)) { FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTaskList, FlowTask::getId)); } FlowEngine.taskService().deleteByInsIds(instanceIds); FlowEngine.hisTaskService().deleteByInsIds(instanceIds); FlowEngine.insService().removeByIds(instanceIds); return true; } /** * 撤销流程 * * @param bo 参数 */ @Override @Transactional(rollbackFor = Exception.class) public boolean cancelProcessApply(FlowCancelBo bo) { 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(); String userIdStr = LoginHelper.getUserIdStr(); BusinessStatusEnum.checkCancelStatus(instance.getFlowStatus()); FlowParams flowParams = FlowParams.build() .message(message) .flowStatus(BusinessStatusEnum.CANCEL.getStatus()) .hisStatus(BusinessStatusEnum.CANCEL.getStatus()) .handler(userIdStr) .ignore(true); taskService.revoke(instance.getId(), flowParams); 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 flowHisTaskList(String businessId) { FlowInstance flowInstance = this.selectInstByBusinessId(businessId); if (ObjectUtil.isNull(flowInstance)) { throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); } Long instanceId = flowInstance.getId(); // 先组装待审批任务(运行中的任务) List runningTaskVos = new ArrayList<>(); List runningTasks = flwTaskService.selectByInstId(instanceId); if (CollUtil.isNotEmpty(runningTasks)) { runningTaskVos = BeanUtil.copyToList(runningTasks, FlowHisTaskVo.class); List associatedUsers = FlowEngine.userService() .getByAssociateds(StreamUtils.toList(runningTasks, FlowTask::getId)); Map> taskUserMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); for (FlowHisTaskVo vo : runningTaskVos) { vo.setFlowStatus(TaskStatusEnum.WAITING.getStatus()); vo.setUpdateTime(null); vo.setRunDuration(null); List users = taskUserMap.get(vo.getId()); if (CollUtil.isNotEmpty(users)) { vo.setApprover(StreamUtils.join(users, User::getProcessedBy)); } } } // 再组装历史任务(已处理任务) List hisTaskVos = new ArrayList<>(); List hisTasks = flowHisTaskMapper.selectList( new LambdaQueryWrapper() .eq(FlowHisTask::getInstanceId, instanceId) .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) .orderByDesc(FlowHisTask::getUpdateTime) ); if (CollUtil.isNotEmpty(hisTasks)) { hisTaskVos = BeanUtil.copyToList(hisTasks, FlowHisTaskVo.class); } // 结果列表,待审批任务在前,历史任务在后 List combinedList = new ArrayList<>(); combinedList.addAll(runningTaskVos); combinedList.addAll(hisTaskVos); return Map.of("list", combinedList, "instanceId", instanceId); } /** * 按照实例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) { FlowInstance flowInstance = flowInstanceMapper.selectById(instanceId); Map variableMap = Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap()); List> variableList = variableMap.entrySet().stream() .map(entry -> Map.of("key", entry.getKey(), "value", entry.getValue())) .toList(); return Map.of("variableList", variableList, "variable", flowInstance.getVariable()); } /** * 设置流程变量 * * @param bo 参数 */ @Override @Transactional(rollbackFor = Exception.class) public boolean updateVariable(FlowVariableBo bo) { FlowInstance flowInstance = flowInstanceMapper.selectById(bo.getInstanceId()); if (flowInstance == null) { throw new ServiceException(ExceptionCons.NOT_FOUNT_INSTANCE); } Map variableMap = new HashMap<>(Optional.ofNullable(flowInstance.getVariableMap()).orElse(Collections.emptyMap())); if (!variableMap.containsKey(bo.getKey())) { log.error("变量不存在: {}", bo.getKey()); return false; } variableMap.put(bo.getKey(), bo.getValue()); flowInstance.setVariable(FlowEngine.jsonConvert.objToStr(variableMap)); return flowInstanceMapper.updateById(flowInstance) > 0; } /** * 设置流程变量 * * @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); insService.updateById(instance); } } /** * 按任务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; } /** * 作废流程 * * @param bo 参数 */ @Override @Transactional(rollbackFor = Exception.class) public boolean processInvalid(FlowInvalidBo bo) { Instance instance = insService.getById(bo.getId()); if (instance != null) { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); } FlowParams flowParams = FlowParams.build() .message(bo.getComment()) .flowStatus(BusinessStatusEnum.INVALID.getStatus()) .hisStatus(TaskStatusEnum.INVALID.getStatus()) .ignore(true); taskService.terminationByInsId(bo.getId(), flowParams); return true; } }