diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java index 0bc027f6f..f5f16920c 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -83,4 +83,9 @@ public interface FlowConstant { */ String WF_TASK_STATUS = "wf_task_status"; + /** + * 自动通过 + */ + String AUTO_PASS = "autoPass"; + } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java index ea2168d56..c798dc5c2 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwInstanceController.java @@ -89,6 +89,16 @@ public class FlwInstanceController extends BaseController { return toAjax(flwInstanceService.deleteByInstanceIds(instanceIds)); } + /** + * 按照实例id删除已完成得流程实例 + * + * @param instanceIds 实例id + */ + @DeleteMapping("/deleteHisByInstanceIds/{instanceIds}") + public R deleteHisByInstanceIds(@PathVariable List instanceIds) { + return toAjax(flwInstanceService.deleteHisByInstanceIds(instanceIds)); + } + /** * 撤销流程 * diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java index 09fed0d63..54ffe977e 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwInstanceService.java @@ -85,6 +85,14 @@ public interface IFlwInstanceService { */ boolean deleteByInstanceIds(List instanceIds); + /** + * 按照实例id删除已完成得流程实例 + * + * @param instanceIds 删除的实例id + * @return 删除结果 + */ + boolean deleteHisByInstanceIds(List instanceIds); + /** * 撤销流程 * diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java index 67279f340..0b4d17450 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java @@ -165,6 +165,14 @@ public interface IFlwTaskService { */ List selectByInstId(Long instanceId); + /** + * 按照实例id查询任务 + * + * @param instanceIds 列表 + * @return 结果 + */ + List selectByInstIds(List instanceIds); + /** * 判断流程是否已结束(即该流程实例下是否还有未完成的任务) * 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 index bc3b38524..66fc609e7 100644 --- 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 @@ -211,18 +211,71 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { Function.identity() ); - // 逐一触发删除事件 - instances.forEach(instance -> { - Definition definition = definitionMap.get(instance.getDefinitionId()); - if (ObjectUtil.isNull(definition)) { - log.warn("实例 ID: {} 对应的流程定义信息未找到,跳过删除事件触发。", instance.getId()); - return; + try { + // 逐一触发删除事件 + 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()); + }); + // 删除实例 + boolean remove = insService.remove(instanceIds); + if (!remove) { + log.warn("删除流程实例失败!"); + throw new ServiceException("删除流程实例失败"); } - flowProcessEventHandler.processDeleteHandler(definition.getFlowCode(), instance.getBusinessId()); - }); + } catch (Exception e) { + log.warn("操作失败!{}", e.getMessage()); + throw new ServiceException(e.getMessage()); + } + return true; + } - // 删除实例 - 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() + ); + try { + // 逐一触发删除事件 + 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); + } catch (Exception e) { + log.warn("操作失败!{}", e.getMessage()); + throw new ServiceException(e.getMessage()); + } + return true; } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index b3069e535..99b78e421 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -3,6 +3,7 @@ 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.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -21,6 +22,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.satoken.utils.LoginHelper; @@ -33,10 +35,7 @@ import org.dromara.warm.flow.core.service.*; import org.dromara.warm.flow.core.utils.ExpressionUtil; import org.dromara.warm.flow.core.utils.MapUtil; import org.dromara.warm.flow.orm.entity.*; -import org.dromara.warm.flow.orm.mapper.FlowHisTaskMapper; -import org.dromara.warm.flow.orm.mapper.FlowInstanceMapper; -import org.dromara.warm.flow.orm.mapper.FlowNodeMapper; -import org.dromara.warm.flow.orm.mapper.FlowTaskMapper; +import org.dromara.warm.flow.orm.mapper.*; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskAssigneeType; @@ -85,6 +84,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { private final IFlwTaskAssigneeService flwTaskAssigneeService; private final IFlwCommonService flwCommonService; private final IFlwNodeExtService flwNodeExtService; + private final FlowDefinitionMapper flowDefinitionMapper; /** * 启动任务 @@ -167,6 +167,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { throw new ServiceException("流程任务不存在或任务已审批!"); } Instance ins = insService.getById(flowTask.getInstanceId()); + FlowDefinition flowDefinition = flowDefinitionMapper.selectOne(new LambdaQueryWrapper<>(FlowDefinition.class).eq(FlowDefinition::getId, ins.getDefinitionId())); // 检查流程状态是否为草稿、已撤销或已退回状态,若是则执行流程提交监听 if (BusinessStatusEnum.isDraftOrCancelOrBack(ins.getFlowStatus())) { completeTaskBo.getVariables().put(FlowConstant.SUBMIT, true); @@ -177,15 +178,8 @@ public class FlwTaskServiceImpl implements IFlwTaskService { completeTaskBo.getVariables().putAll(assigneeMap); } // 构建流程参数,包括变量、跳转类型、消息、处理人、权限等信息 - FlowParams flowParams = FlowParams.build() - .variable(completeTaskBo.getVariables()) - .skipType(SkipType.PASS.getKey()) - .message(completeTaskBo.getMessage()) - .flowStatus(BusinessStatusEnum.WAITING.getStatus()) - .hisStatus(TaskStatusEnum.PASS.getStatus()) - .hisTaskExt(completeTaskBo.getFileId()); - // 执行任务跳转,并根据返回的处理人设置下一步处理人 - taskService.skip(taskId, flowParams); + FlowParams flowParams = FlowParams.build().variable(completeTaskBo.getVariables()).skipType(SkipType.PASS.getKey()).message(completeTaskBo.getMessage()).flowStatus(BusinessStatusEnum.WAITING.getStatus()).hisStatus(TaskStatusEnum.PASS.getStatus()).hisTaskExt(completeTaskBo.getFileId()); + skipTask(taskId, flowParams, flowTask.getInstanceId(), flowDefinition); return true; } catch (Exception e) { log.error(e.getMessage(), e); @@ -193,6 +187,45 @@ public class FlwTaskServiceImpl implements IFlwTaskService { } } + /** + * 流程办理 + * + * @param taskId 任务ID + * @param flowParams 参数 + * @param instanceId 实例ID + * @param flowDefinition 流程定义 + */ + private void skipTask(Long taskId, FlowParams flowParams, Long instanceId, FlowDefinition flowDefinition) { + // 执行任务跳转,并根据返回的处理人设置下一步处理人 + taskService.skip(taskId, flowParams); + List flowTaskList = selectByInstId(instanceId); + if (CollUtil.isEmpty(flowTaskList)) { + return; + } + List userList = FlowEngine.userService().getByAssociateds(StreamUtils.toList(flowTaskList, FlowTask::getId)); + if (CollUtil.isEmpty(userList)) { + return; + } + Dict dict = JsonUtils.parseMap(flowDefinition.getExt()); + for (FlowTask task : flowTaskList) { + //自动审批 + boolean autoPass = !ObjectUtil.isNull(dict) && cn.hutool.core.map.MapUtil.getBool(dict, AUTO_PASS); + if (!task.getId().equals(taskId) && autoPass) { + List users = StreamUtils.filter(userList, e -> ObjectUtil.equals(task.getId(), e.getAssociated()) && ObjectUtil.equal(e.getProcessedBy(), LoginHelper.getUserIdStr())); + if (CollUtil.isEmpty(users)) { + continue; + } + flowParams. + message("流程引擎自动审批!"). + variable(Map.of( + FlowConstant.SUBMIT, false, + FlowConstant.FLOW_COPY_LIST, Collections.emptyList(), + FlowConstant.MESSAGE_NOTICE, StringUtils.EMPTY)); + skipTask(task.getId(), flowParams, instanceId, flowDefinition); + } + } + } + /** * 设置弹窗处理人 * @@ -245,20 +278,12 @@ public class FlwTaskServiceImpl implements IFlwTaskService { task.setId(taskId); task.setNodeName("【抄送】" + task.getNodeName()); Date updateTime = new Date(flowHisTask.getUpdateTime().getTime() - 1000); - FlowParams flowParams = FlowParams.build() - .skipType(SkipType.NONE.getKey()) - .hisStatus(TaskStatusEnum.COPY.getStatus()) - .message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName)); + FlowParams flowParams = FlowParams.build().skipType(SkipType.NONE.getKey()).hisStatus(TaskStatusEnum.COPY.getStatus()).message("【抄送给】" + StreamUtils.join(flowCopyList, FlowCopyBo::getUserName)); HisTask hisTask = hisTaskService.setSkipHisTask(task, flowNode, flowParams); hisTask.setCreateTime(updateTime); hisTask.setUpdateTime(updateTime); hisTaskService.save(hisTask); - List userList = StreamUtils.toList(flowCopyList, x -> - new FlowUser() - .setType(TaskAssigneeType.COPY.getCode()) - .setProcessedBy(String.valueOf(x.getUserId())) - .setAssociated(taskId) - ); + List userList = StreamUtils.toList(flowCopyList, x -> new FlowUser().setType(TaskAssigneeType.COPY.getCode()).setProcessedBy(String.valueOf(x.getUserId())).setAssociated(taskId)); // 批量保存抄送人员 FlowEngine.userService().saveBatch(userList); } @@ -320,8 +345,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (CollUtil.isEmpty(taskList)) { return; } - List associatedUsers = FlowEngine.userService() - .getByAssociateds(StreamUtils.toList(taskList, FlowTaskVo::getId)); + List associatedUsers = FlowEngine.userService().getByAssociateds(StreamUtils.toList(taskList, FlowTaskVo::getId)); Map> taskUserMap = StreamUtils.groupByKey(associatedUsers, User::getAssociated); // 组装用户数据回任务列表 for (FlowTaskVo task : taskList) { @@ -399,14 +423,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 消息通知 variable.put("notice", notice); - FlowParams flowParams = FlowParams.build() - .nodeCode(bo.getNodeCode()) - .variable(variable) - .message(message) - .skipType(SkipType.REJECT.getKey()) - .flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()) - .hisStatus(TaskStatusEnum.BACK.getStatus()) - .hisTaskExt(bo.getFileId()); + FlowParams flowParams = FlowParams.build().nodeCode(bo.getNodeCode()).variable(variable).message(message).skipType(SkipType.REJECT.getKey()).flowStatus(applyNodeCode.equals(bo.getNodeCode()) ? TaskStatusEnum.BACK.getStatus() : TaskStatusEnum.WAITING.getStatus()).hisStatus(TaskStatusEnum.BACK.getStatus()).hisTaskExt(bo.getFileId()); taskService.skip(task.getId(), flowParams); return true; } catch (Exception e) { @@ -458,10 +475,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (ObjectUtil.isNotNull(instance)) { BusinessStatusEnum.checkInvalidStatus(instance.getFlowStatus()); } - FlowParams flowParams = FlowParams.build() - .message(bo.getComment()) - .flowStatus(BusinessStatusEnum.TERMINATION.getStatus()) - .hisStatus(TaskStatusEnum.TERMINATION.getStatus()); + FlowParams flowParams = FlowParams.build().message(bo.getComment()).flowStatus(BusinessStatusEnum.TERMINATION.getStatus()).hisStatus(TaskStatusEnum.TERMINATION.getStatus()); taskService.termination(taskId, flowParams); return true; } catch (Exception e) { @@ -477,8 +491,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public List selectByIdList(List taskIdList) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) - .in(FlowTask::getId, taskIdList)); + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).in(FlowTask::getId, taskIdList)); } /** @@ -535,9 +548,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { //办理人变量替换 ExpressionUtil.evalVariable(buildNextTaskList, FlowParams.build().variable(mergeVariable)); for (FlowNode flowNode : nextFlowNodes) { - Optional first = buildNextTaskList.stream() - .filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())) - .findFirst(); + Optional first = buildNextTaskList.stream().filter(t -> t.getNodeCode().equals(flowNode.getNodeCode())).findFirst(); first.ifPresent(t -> { if (CollUtil.isNotEmpty(t.getPermissionList())) { List users = flwTaskAssigneeService.fetchUsersByStorageIds(String.join(StringUtils.SEPARATOR, t.getPermissionList())); @@ -559,8 +570,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public FlowHisTask selectHisTaskById(Long taskId) { - return flowHisTaskMapper.selectOne(new LambdaQueryWrapper<>(FlowHisTask.class) - .eq(FlowHisTask::getId, taskId)); + return flowHisTaskMapper.selectOne(new LambdaQueryWrapper<>(FlowHisTask.class).eq(FlowHisTask::getId, taskId)); } /** @@ -570,8 +580,17 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public List selectByInstId(Long instanceId) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class) - .eq(FlowTask::getInstanceId, instanceId)); + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).eq(FlowTask::getInstanceId, instanceId)); + } + + /** + * 按照实例id查询任务 + * + * @param instanceIds 流程实例id + */ + @Override + public List selectByInstIds(List instanceIds) { + return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).in(FlowTask::getInstanceId, instanceIds)); } /** @@ -582,8 +601,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public boolean isTaskEnd(Long instanceId) { - boolean exists = flowTaskMapper.exists(new LambdaQueryWrapper() - .eq(FlowTask::getInstanceId, instanceId)); + boolean exists = flowTaskMapper.exists(new LambdaQueryWrapper().eq(FlowTask::getInstanceId, instanceId)); return !exists; } @@ -596,8 +614,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { @Override @Transactional(rollbackFor = Exception.class) public boolean taskOperation(TaskOperationBo bo, String taskOperation) { - FlowParams flowParams = FlowParams.build() - .message(bo.getMessage()); + FlowParams flowParams = FlowParams.build().message(bo.getMessage()); if (LoginHelper.isSuperAdmin() || LoginHelper.isTenantAdmin()) { flowParams.ignore(true); } @@ -676,12 +693,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 批量删除现有任务的办理人记录 if (CollUtil.isNotEmpty(flowTasks)) { FlowEngine.userService().deleteByTaskIds(StreamUtils.toList(flowTasks, FlowTask::getId)); - List userList = StreamUtils.toList(flowTasks, flowTask -> - new FlowUser() - .setType(TaskAssigneeType.APPROVER.getCode()) - .setProcessedBy(userId) - .setAssociated(flowTask.getId()) - ); + List userList = StreamUtils.toList(flowTasks, flowTask -> new FlowUser().setType(TaskAssigneeType.APPROVER.getCode()).setProcessedBy(userId).setAssociated(flowTask.getId())); if (CollUtil.isNotEmpty(userList)) { FlowEngine.userService().saveBatch(userList); } @@ -716,9 +728,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public FlowNode getByNodeCode(String nodeCode, Long definitionId) { - return flowNodeMapper.selectOne(new LambdaQueryWrapper() - .eq(FlowNode::getNodeCode, nodeCode) - .eq(FlowNode::getDefinitionId, definitionId)); + return flowNodeMapper.selectOne(new LambdaQueryWrapper().eq(FlowNode::getNodeCode, nodeCode).eq(FlowNode::getDefinitionId, definitionId)); } /**