diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java index 04c07a41e..62d5be7df 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/controller/ProcessTaskController.java @@ -48,13 +48,15 @@ public class ProcessTaskController { *

* @param taskId 任务ID * @param view 是否为查看模式:true-仅查看,false-可编辑处理 + * @param useMainFormData 是否使用主表单数据(true=从process_instance_record.form_data读取最新数据,false=从节点快照读取,默认true) * @return R 统一响应对象,包含任务的完整信息和可操作项 */ @SneakyThrows @GetMapping("queryTask") - public R queryTask(String taskId, boolean view) { + public R queryTask(String taskId, boolean view, + @RequestParam(defaultValue = "true") boolean useMainFormData) { - return taskService.queryTask(taskId, view); + return taskService.queryTask(taskId, view, useMainFormData); } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java index f4edd92e2..bff9632bd 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/ITaskService.java @@ -24,6 +24,18 @@ public interface ITaskService { */ R queryTask(String taskId, boolean view); + /** + * 查询任务详情(支持选择表单数据源) + *

+ * 获取任务的详细信息,包括: - 任务基本信息(名称、创建时间、执行人等) - 所属流程信息 - 表单数据和权限 - 历史审批记录 + *

+ * @param taskId 任务ID + * @param view 是否为查看模式(true=只查看,false=处理模式) + * @param useMainFormData 是否使用主表单数据(true=从process_instance_record.form_data读取最新数据,false=从节点快照读取) + * @return 任务详情信息 + */ + R queryTask(String taskId, boolean view, boolean useMainFormData); + /** * 完成任务 *

diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java index aac8309aa..de2f5b541 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/TaskServiceImpl.java @@ -44,10 +44,10 @@ import java.util.*; *

* 该类是流程任务操作的核心服务,负责处理用户与流程任务的交互,主要功能包括: 1. 任务查询(待办任务详情、任务表单权限等) 2. 任务操作(完成、委托、转办、退回等) 3. * 任务统计(首页数据看板) 4. 流程控制(终止流程、加签等高级操作) - * + *

* 该服务整合了流程引擎的任务API,为前端提供统一的任务操作接口 *

- * + * * @author pigx code generator * @date 2023-10-01 */ @@ -56,364 +56,457 @@ import java.util.*; @RequiredArgsConstructor public class TaskServiceImpl implements ITaskService { - private final IProcessService processService; + private final IProcessService processService; - private final IProcessCopyService processCopyService; + private final IProcessCopyService processCopyService; - private final IProcessNodeDataService nodeDataService; + private final IProcessNodeDataService nodeDataService; - private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; + private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService; - private final IProcessInstanceRecordService processInstanceRecordService; + private final IProcessInstanceRecordService processInstanceRecordService; - private final IFlowableEngineService flowableEngineService; + private final IFlowableEngineService flowableEngineService; - private final TaskService taskService; + private final TaskService taskService; - private final HistoryService historyService; + private final HistoryService historyService; - private final RuntimeService runtimeService; + private final RuntimeService runtimeService; - private final ObjectMapper objectMapper; + private final ObjectMapper objectMapper; - /** - * 查询首页数据看板 - *

- * 获取当前用户的任务统计数据,包括: 1. 待办任务数量 2. 已办任务数量 3. 我发起的流程数量 4. 抄送给我的数量 - *

- * @return R 包含IndexPageStatistics对象,含有各项统计数据 - */ - @Override - public R queryTaskData() { + /** + * 查询首页数据看板 + *

+ * 获取当前用户的任务统计数据,包括: 1. 待办任务数量 2. 已办任务数量 3. 我发起的流程数量 4. 抄送给我的数量 + *

+ * + * @return R 包含IndexPageStatistics对象,含有各项统计数据 + */ + @Override + public R queryTaskData() { - TaskQuery taskQuery = taskService.createTaskQuery(); + TaskQuery taskQuery = taskService.createTaskQuery(); - // 待办数量 - long pendingNum = taskQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) - .taskTenantId(TenantContextHolder.getTenantId().toString()) - .count(); - // 已完成任务 - HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService - .createHistoricActivityInstanceQuery(); + // 待办数量 + long pendingNum = taskQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) + .taskTenantId(TenantContextHolder.getTenantId().toString()) + .count(); + // 已完成任务 + HistoricActivityInstanceQuery historicActivityInstanceQuery = historyService + .createHistoricActivityInstanceQuery(); - long completedNum = historicActivityInstanceQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) - .tenantIdIn(List.of(TenantContextHolder.getTenantId().toString())) - .finished() - .count(); + long completedNum = historicActivityInstanceQuery.taskAssignee(String.valueOf(SecurityUtils.getUser().getId())) + .tenantIdIn(List.of(TenantContextHolder.getTenantId().toString())) + .finished() + .count(); - IndexPageStatistics indexPageStatistics = IndexPageStatistics.builder() - .pendingNum(pendingNum) - .completedNum(completedNum) - .build(); + IndexPageStatistics indexPageStatistics = IndexPageStatistics.builder() + .pendingNum(pendingNum) + .completedNum(completedNum) + .build(); - // 获取抄送任务 - Long copyCount = processCopyService.lambdaQuery() - .eq(ProcessCopy::getUserId, SecurityUtils.getUser().getId()) - .count(); - indexPageStatistics.setCopyNum(copyCount); - return R.ok(indexPageStatistics); - } + // 获取抄送任务 + Long copyCount = processCopyService.lambdaQuery() + .eq(ProcessCopy::getUserId, SecurityUtils.getUser().getId()) + .count(); + indexPageStatistics.setCopyNum(copyCount); + return R.ok(indexPageStatistics); + } - /** - * 查询任务 - *

- * 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4. - * 任务是否可委托、是否是委托任务等状态 - * - * 对于已完成的任务,会从历史记录中获取任务数据 - *

- * @param taskId 任务ID - * @param view 是否是查看模式(true:只查看,false:可操作) - * @return R 包含任务详情的Dict对象 - * @throws Exception 当解析JSON数据失败时抛出异常 - */ - @SneakyThrows - @Override - public R queryTask(String taskId, boolean view) { + /** + * 查询任务 + *

+ * 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4. + * 任务是否可委托、是否是委托任务等状态 + *

+ * 对于已完成的任务,会从历史记录中获取任务数据 + *

+ * + * @param taskId 任务ID + * @param view 是否是查看模式(true:只查看,false:可操作) + * @return R 包含任务详情的Dict对象 + * @throws Exception 当解析JSON数据失败时抛出异常 + */ + @SneakyThrows + @Override + public R queryTask(String taskId, boolean view) { + // 默认使用主表单数据源 + return queryTask(taskId, view, true); + } - long userId = SecurityUtils.getUser().getId(); + /** + * 查询任务(支持选择表单数据源) + *

+ * 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4. + * 任务是否可委托、是否是委托任务等状态 + *

+ * 对于已完成的任务,可选择从主表单数据或节点快照读取表单数据 + *

+ * + * @param taskId 任务ID + * @param view 是否是查看模式(true:只查看,false:可操作) + * @param useMainFormData 是否使用主表单数据(true=从process_instance_record.form_data读取最新数据,false=从节点快照读取) + * @return R 包含任务详情的Dict对象 + * @throws Exception 当解析JSON数据失败时抛出异常 + */ + @SneakyThrows + @Override + public R queryTask(String taskId, boolean view, boolean useMainFormData) { - Optional task = Optional.ofNullable(taskService.createTaskQuery() - .taskId(taskId) - .taskTenantId(TenantContextHolder.getTenantId().toString()) - .taskAssignee(String.valueOf(userId)) - .singleResult()); - TaskResultDto taskResultDto = new TaskResultDto(); + long userId = SecurityUtils.getUser().getId(); - if (task.isPresent()) { - String processDefinitionId = task.get().getProcessDefinitionId(); - String taskDefinitionKey = task.get().getTaskDefinitionKey(); - DelegationState delegationState = task.get().getDelegationState(); - String processInstanceId = task.get().getProcessInstanceId(); - Object delegateVariable = taskService.getVariableLocal(taskId, "delegate"); + Optional task = Optional.ofNullable(taskService.createTaskQuery() + .taskId(taskId) + .taskTenantId(TenantContextHolder.getTenantId().toString()) + .taskAssignee(String.valueOf(userId)) + .singleResult()); + TaskResultDto taskResultDto = new TaskResultDto(); - String flowId = NodeUtil.getFlowId(processDefinitionId); - Map variables = taskService.getVariables(taskId); - Map variableAll = new HashMap<>(variables); + if (task.isPresent()) { + String processDefinitionId = task.get().getProcessDefinitionId(); + String taskDefinitionKey = task.get().getTaskDefinitionKey(); + DelegationState delegationState = task.get().getDelegationState(); + String processInstanceId = task.get().getProcessInstanceId(); + Object delegateVariable = taskService.getVariableLocal(taskId, "delegate"); - taskResultDto.setFlowId(flowId); - taskResultDto.setNodeId(taskDefinitionKey); - taskResultDto.setCurrentTask(true); - taskResultDto.setDelegate(Convert.toBool(delegateVariable, false)); - taskResultDto.setVariableAll(variableAll); - taskResultDto.setProcessInstanceId(processInstanceId); - taskResultDto.setDelegationState(delegationState == null ? null : delegationState.toString()); - } - else { - Optional historicTaskInstance = Optional - .ofNullable(historyService.createHistoricTaskInstanceQuery() - .taskId(taskId) - .taskAssignee(String.valueOf(userId)) - .singleResult()); + String flowId = NodeUtil.getFlowId(processDefinitionId); + Map variables = taskService.getVariables(taskId); + Map variableAll = new HashMap<>(variables); - String processDefinitionId = historicTaskInstance.get().getProcessDefinitionId(); - String taskDefinitionKey = historicTaskInstance.get().getTaskDefinitionKey(); - String processInstanceId = historicTaskInstance.get().getProcessInstanceId(); + taskResultDto.setFlowId(flowId); + taskResultDto.setNodeId(taskDefinitionKey); + taskResultDto.setCurrentTask(true); + taskResultDto.setDelegate(Convert.toBool(delegateVariable, false)); + taskResultDto.setVariableAll(variableAll); + taskResultDto.setProcessInstanceId(processInstanceId); + taskResultDto.setDelegationState(delegationState == null ? null : delegationState.toString()); + } else { + Optional historicTaskInstance = Optional + .ofNullable(historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(String.valueOf(userId)) + .singleResult()); - String flowId = NodeUtil.getFlowId(processDefinitionId); + String processDefinitionId = historicTaskInstance.get().getProcessDefinitionId(); + String taskDefinitionKey = historicTaskInstance.get().getTaskDefinitionKey(); + String processInstanceId = historicTaskInstance.get().getProcessInstanceId(); - List variableInstanceList = historyService.createHistoricVariableInstanceQuery() - .processInstanceId(processInstanceId) - .list(); - Map variableAll = new HashMap<>(); + String flowId = NodeUtil.getFlowId(processDefinitionId); - if (CollUtil.isNotEmpty(variableInstanceList)) { - variableInstanceList.forEach(historicVariableInstance -> variableAll - .put(historicVariableInstance.getVariableName(), historicVariableInstance.getValue())); - } + List variableInstanceList = historyService.createHistoricVariableInstanceQuery() + .processInstanceId(processInstanceId) + .list(); + Map variableAll = new HashMap<>(); - taskResultDto.setFlowId(flowId); - taskResultDto.setNodeId(taskDefinitionKey); - taskResultDto.setCurrentTask(false); - taskResultDto.setVariableAll(variableAll); - taskResultDto.setProcessInstanceId(processInstanceId); + if (CollUtil.isNotEmpty(variableInstanceList)) { + variableInstanceList.forEach(historicVariableInstance -> variableAll + .put(historicVariableInstance.getVariableName(), historicVariableInstance.getValue())); + } - } + taskResultDto.setFlowId(flowId); + taskResultDto.setNodeId(taskDefinitionKey); + taskResultDto.setCurrentTask(false); + taskResultDto.setVariableAll(variableAll); + taskResultDto.setProcessInstanceId(processInstanceId); - // 变量 - Map paramMap = taskResultDto.getVariableAll(); - // 是否是当前活动任务 - Boolean currentTask = taskResultDto.getCurrentTask(); - if (!currentTask) { - ProcessNodeRecordAssignUser processNodeRecordAssignUser = processNodeRecordAssignUserService.lambdaQuery() - .eq(ProcessNodeRecordAssignUser::getTaskId, taskId) - .eq(ProcessNodeRecordAssignUser::getUserId, userId) - .eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.YJS.getCode()) - .last("limit 1") - .orderByDesc(ProcessNodeRecordAssignUser::getEndTime) - .one(); + } - if (processNodeRecordAssignUser != null) { - String data = processNodeRecordAssignUser.getData(); - if (StrUtil.isNotBlank(data)) { - Map collect = objectMapper.readValue(data, new TypeReference<>() { - }); - paramMap.putAll(collect); + // 变量 + Map paramMap = taskResultDto.getVariableAll(); + // 是否是当前活动任务 + Boolean currentTask = taskResultDto.getCurrentTask(); + if (!currentTask) { + // 判断是否使用主表单数据源 + boolean shouldUseMainFormData = view && useMainFormData; - } - } + if (shouldUseMainFormData) { + // 新增: 从主流程实例记录读取最新表单数据 + String processInstanceId = taskResultDto.getProcessInstanceId(); + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); - } + if (processInstanceRecord != null && StrUtil.isNotBlank(processInstanceRecord.getFormData())) { + try { + Map mainFormData = objectMapper.readValue(processInstanceRecord.getFormData(), + new TypeReference>() { + }); + paramMap.putAll(mainFormData); + log.debug("Using main form data for completed task {}", taskId); + } catch (Exception e) { + log.warn("Failed to parse main form data, falling back to node snapshot: {}", e.getMessage()); + shouldUseMainFormData = false; + } + } else { + log.debug("Main form data empty, falling back to node snapshot for task {}", taskId); + shouldUseMainFormData = false; + } + } - // 当前节点数据 - Node node = nodeDataService.getNodeData(taskResultDto.getFlowId(), taskResultDto.getNodeId()).getData(); - Map formPerms = node.getFormPerms(); + // 回退: 使用节点级别快照(原有逻辑) + if (!shouldUseMainFormData) { + ProcessNodeRecordAssignUser processNodeRecordAssignUser = processNodeRecordAssignUserService.lambdaQuery() + .eq(ProcessNodeRecordAssignUser::getTaskId, taskId) + .eq(ProcessNodeRecordAssignUser::getUserId, userId) + .eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.YJS.getCode()) + .last("limit 1") + .orderByDesc(ProcessNodeRecordAssignUser::getEndTime) + .one(); - Process oaForms = processService.getByFlowId(taskResultDto.getFlowId()); - if (oaForms == null) { - return R.failed("流程不存在"); - } + if (processNodeRecordAssignUser != null) { + String data = processNodeRecordAssignUser.getData(); + if (StrUtil.isNotBlank(data)) { + Map collect = objectMapper.readValue(data, new TypeReference<>() { + }); + paramMap.putAll(collect); + log.debug("Using node-level snapshot for task {}", taskId); + } + } + } - Dict set = Dict.create() - .set("processInstanceId", taskResultDto.getProcessInstanceId()) - .set("node", taskResultDto.getTaskNode()) - .set("process", oaForms.getProcess()) - .set("delegateAgain", taskResultDto.getDelegate()) - .set("delegationTask", StrUtil.equals(taskResultDto.getDelegationState(), "PENDING")) - .set("formItems", oaForms.getFormItems()) - .set("formData", paramMap) - .set("formConfig", oaForms.getFormConfig()) - .set("formPerms", formPerms); + } - return R.ok(set); - } + // 当前节点数据 + Node node = nodeDataService.getNodeData(taskResultDto.getFlowId(), taskResultDto.getNodeId()).getData(); + Map formPerms = node.getFormPerms(); - /** - * 完成任务 - *

- * 执行任务的完成操作,将任务标记为已完成并流转到下一个节点。 该方法会将当前用户ID设置到任务参数中,确保任务由正确的用户完成 - *

- * @param taskParamDto 任务参数,包含任务ID、审批意见、流程变量等 - * @return R 操作结果 - */ - @Override - public R completeTask(TaskParamDto taskParamDto) { - long userId = SecurityUtils.getUser().getId(); - taskParamDto.setUserId(String.valueOf(userId)); + Process oaForms = processService.getByFlowId(taskResultDto.getFlowId()); + if (oaForms == null) { + return R.failed("流程不存在"); + } - Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); - if (CollUtil.isNotEmpty(taskLocalParamMap)) { - taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); - } + Dict set = Dict.create() + .set("processInstanceId", taskResultDto.getProcessInstanceId()) + .set("node", taskResultDto.getTaskNode()) + .set("process", oaForms.getProcess()) + .set("delegateAgain", taskResultDto.getDelegate()) + .set("delegationTask", StrUtil.equals(taskResultDto.getDelegationState(), "PENDING")) + .set("formItems", oaForms.getFormItems()) + .set("formData", paramMap) + .set("formConfig", oaForms.getFormConfig()) + .set("formPerms", formPerms); - if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { - // 获取当前任务 - Task task = taskService.createTaskQuery().taskId(taskParamDto.getTaskId()).singleResult(); - runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); - } + return R.ok(set); + } - taskService.complete(taskParamDto.getTaskId(), taskParamDto.getParamMap()); + /** + * 完成任务 + *

+ * 执行任务的完成操作,将任务标记为已完成并流转到下一个节点。 该方法会将当前用户ID设置到任务参数中,确保任务由正确的用户完成 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、审批意见、流程变量等 + * @return R 操作结果 + */ + @Override + public R completeTask(TaskParamDto taskParamDto) { + long userId = SecurityUtils.getUser().getId(); + taskParamDto.setUserId(String.valueOf(userId)); - return R.ok(); - } + Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); + if (CollUtil.isNotEmpty(taskLocalParamMap)) { + taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); + } - /** - * 前加签 - *

- * 将任务委托给其他用户处理,委托后任务会分配给被委托人, 被委托人完成后任务会返回给原委托人 - *

- * @param taskParamDto 任务参数,包含任务ID、被委托人ID列表等 - * @return R 操作结果 - */ - @Transactional - @Override - public R delegateTask(TaskParamDto taskParamDto) { - taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); - return flowableEngineService.delegateTask(taskParamDto); - } + if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { + // 获取当前任务 + Task task = taskService.createTaskQuery().taskId(taskParamDto.getTaskId()).singleResult(); + runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); - /** - * 加签完成任务 - *

- * 被委托人完成委托任务后,通过此方法将任务返回给原委托人。 这是委托流程的第二步,用于结束委托状态 - *

- * @param taskParamDto 任务参数,包含任务ID、审批意见等 - * @return R 操作结果 - */ - @Override - public R resolveTask(TaskParamDto taskParamDto) { - return flowableEngineService.resolveTask(taskParamDto); - } + // 新增: 同步更新流程实例记录的主表单数据 + String processInstanceId = task.getProcessInstanceId(); + ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .one(); - /** - * 设置执行人 - *

- * 转办任务给其他用户,与委托不同,转办后任务直接由新执行人处理, 不会返回给原执行人 - *

- * @param taskParamDto 任务参数,包含任务ID、新执行人ID等 - * @return R 操作结果 - */ - @Override - public R setAssignee(TaskParamDto taskParamDto) { - Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); - if (CollUtil.isNotEmpty(taskLocalParamMap)) { - taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); - } + if (processInstanceRecord != null) { + try { + // 合并现有表单数据和新提交的数据 + Map existingFormData = new HashMap<>(); + if (StrUtil.isNotBlank(processInstanceRecord.getFormData())) { + existingFormData = objectMapper.readValue(processInstanceRecord.getFormData(), + new TypeReference>() { + }); + } - // 设置任务的 owner 为当前任务的 assignee - Task task = taskService.createTaskQuery() - .taskId(taskParamDto.getTaskId()) - .includeTaskLocalVariables() - .singleResult(); - taskService.setOwner(taskParamDto.getTaskId(), task.getAssignee()); + // 用新数据覆盖现有数据 + existingFormData.putAll(taskParamDto.getFormData()); - // 设置任务的 assignee 为目标用户 (转办用户) - for (Long targetUserId : taskParamDto.getTargetUserIdList()) { - taskService.setAssignee(taskParamDto.getTaskId(), targetUserId.toString()); - } + // 更新到数据库 + String updatedFormData = objectMapper.writeValueAsString(existingFormData); + processInstanceRecordService.lambdaUpdate() + .set(ProcessInstanceRecord::getFormData, updatedFormData) + .eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId) + .update(); - // 更新表单 - if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { - runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); - } - return R.ok(); - } + log.debug("流程实例 {} 的表单数据已同步更新", processInstanceId); + } catch (Exception e) { + log.error("同步更新流程实例表单数据失败: {}", e.getMessage(), e); + // 不中断流程执行,仅记录错误 + } + } + } - /** - * 结束流程 - *

- * 强制终止流程实例的执行。该方法会: 1. 递归查找并终止所有子流程实例 2. 更新流程实例记录状态为已结束 3. - * 设置结束原因为强制终止(finishReason=9) - * - * 通常用于流程异常或需要强制结束的场景 - *

- * @param taskParamDto 任务参数,包含流程实例ID - * @return R 操作结果 - */ - @Override - public R stopProcessInstance(TaskParamDto taskParamDto) { - String reqProcessInstanceId = taskParamDto.getProcessInstanceId(); - List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList(reqProcessInstanceId); - CollUtil.reverse(allStopProcessInstanceIdList); - allStopProcessInstanceIdList.add(reqProcessInstanceId); - allStopProcessInstanceIdList.forEach(processInstanceId -> { - // 查询流程实例 - ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() - .processInstanceId(processInstanceId) - .processInstanceTenantId(TenantContextHolder.getTenantId().toString()) - .singleResult(); - if (Optional.ofNullable(processInstance).isPresent()) { - // 查询执行实例 - List executionIds = runtimeService.createExecutionQuery() - .parentId(processInstanceId) - .list() - .stream() - .map(Execution::getId) - .toList(); - // 更改活动状态为结束 - runtimeService.createChangeActivityStateBuilder() - .moveExecutionsToSingleActivityId(executionIds, "end") - .changeState(); - } - }); + taskService.complete(taskParamDto.getTaskId(), taskParamDto.getParamMap()); - // 停止流程实例后,更新流程实例记录状态 - processInstanceRecordService.lambdaUpdate() - .set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode()) - .set(ProcessInstanceRecord::getFinishReason, "9") - .set(ProcessInstanceRecord::getEndTime, new Date()) - .eq(ProcessInstanceRecord::getProcessInstanceId, reqProcessInstanceId) - .update(new ProcessInstanceRecord()); - return R.ok(); - } + return R.ok(); + } - /** - * 退回 - *

- * 将任务退回到指定的历史节点重新处理。 常用于审批不通过需要重新修改的场景 - *

- * @param taskParamDto 任务参数,包含任务ID、目标节点ID、退回原因等 - * @return R 操作结果 - */ - @Override - public R back(TaskParamDto taskParamDto) { - taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); - return flowableEngineService.back(taskParamDto); - } + /** + * 前加签 + *

+ * 将任务委托给其他用户处理,委托后任务会分配给被委托人, 被委托人完成后任务会返回给原委托人 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、被委托人ID列表等 + * @return R 操作结果 + */ + @Transactional + @Override + public R delegateTask(TaskParamDto taskParamDto) { + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + return flowableEngineService.delegateTask(taskParamDto); + } - /** - * 递归获取所有需要停止的子流程实例ID列表 - *

- * 当一个流程包含子流程时,终止主流程需要同时终止所有子流程。 该方法递归查找指定流程的所有子流程实例ID - *

- * @param processInstanceId 父流程实例ID - * @return 所有子流程实例ID列表 - */ - private List getAllStopProcessInstanceIdList(String processInstanceId) { - List list = processInstanceRecordService.lambdaQuery() - .eq(ProcessInstanceRecord::getParentProcessInstanceId, processInstanceId) - .list(); + /** + * 加签完成任务 + *

+ * 被委托人完成委托任务后,通过此方法将任务返回给原委托人。 这是委托流程的第二步,用于结束委托状态 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、审批意见等 + * @return R 操作结果 + */ + @Override + public R resolveTask(TaskParamDto taskParamDto) { + return flowableEngineService.resolveTask(taskParamDto); + } - List collect = new ArrayList<>(list.stream().map(ProcessInstanceRecord::getProcessInstanceId).toList()); + /** + * 设置执行人 + *

+ * 转办任务给其他用户,与委托不同,转办后任务直接由新执行人处理, 不会返回给原执行人 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、新执行人ID等 + * @return R 操作结果 + */ + @Override + public R setAssignee(TaskParamDto taskParamDto) { + Map taskLocalParamMap = taskParamDto.getTaskLocalParamMap(); + if (CollUtil.isNotEmpty(taskLocalParamMap)) { + taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap); + } - for (ProcessInstanceRecord processInstanceRecord : list) { - List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList( - processInstanceRecord.getProcessInstanceId()); + // 设置任务的 owner 为当前任务的 assignee + Task task = taskService.createTaskQuery() + .taskId(taskParamDto.getTaskId()) + .includeTaskLocalVariables() + .singleResult(); + taskService.setOwner(taskParamDto.getTaskId(), task.getAssignee()); - collect.addAll(allStopProcessInstanceIdList); + // 设置任务的 assignee 为目标用户 (转办用户) + for (Long targetUserId : taskParamDto.getTargetUserIdList()) { + taskService.setAssignee(taskParamDto.getTaskId(), targetUserId.toString()); + } - } - return collect; - } + // 更新表单 + if (CollUtil.isNotEmpty(taskParamDto.getFormData())) { + runtimeService.setVariables(task.getExecutionId(), taskParamDto.getFormData()); + } + return R.ok(); + } + + /** + * 结束流程 + *

+ * 强制终止流程实例的执行。该方法会: 1. 递归查找并终止所有子流程实例 2. 更新流程实例记录状态为已结束 3. + * 设置结束原因为强制终止(finishReason=9) + *

+ * 通常用于流程异常或需要强制结束的场景 + *

+ * + * @param taskParamDto 任务参数,包含流程实例ID + * @return R 操作结果 + */ + @Override + public R stopProcessInstance(TaskParamDto taskParamDto) { + String reqProcessInstanceId = taskParamDto.getProcessInstanceId(); + List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList(reqProcessInstanceId); + CollUtil.reverse(allStopProcessInstanceIdList); + allStopProcessInstanceIdList.add(reqProcessInstanceId); + allStopProcessInstanceIdList.forEach(processInstanceId -> { + // 查询流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(processInstanceId) + .processInstanceTenantId(TenantContextHolder.getTenantId().toString()) + .singleResult(); + if (Optional.ofNullable(processInstance).isPresent()) { + // 查询执行实例 + List executionIds = runtimeService.createExecutionQuery() + .parentId(processInstanceId) + .list() + .stream() + .map(Execution::getId) + .toList(); + // 更改活动状态为结束 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, "end") + .changeState(); + } + }); + + // 停止流程实例后,更新流程实例记录状态 + processInstanceRecordService.lambdaUpdate() + .set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode()) + .set(ProcessInstanceRecord::getFinishReason, "9") + .set(ProcessInstanceRecord::getEndTime, new Date()) + .eq(ProcessInstanceRecord::getProcessInstanceId, reqProcessInstanceId) + .update(new ProcessInstanceRecord()); + return R.ok(); + } + + /** + * 退回 + *

+ * 将任务退回到指定的历史节点重新处理。 常用于审批不通过需要重新修改的场景 + *

+ * + * @param taskParamDto 任务参数,包含任务ID、目标节点ID、退回原因等 + * @return R 操作结果 + */ + @Override + public R back(TaskParamDto taskParamDto) { + taskParamDto.setUserId(SecurityUtils.getUser().getId().toString()); + return flowableEngineService.back(taskParamDto); + } + + /** + * 递归获取所有需要停止的子流程实例ID列表 + *

+ * 当一个流程包含子流程时,终止主流程需要同时终止所有子流程。 该方法递归查找指定流程的所有子流程实例ID + *

+ * + * @param processInstanceId 父流程实例ID + * @return 所有子流程实例ID列表 + */ + private List getAllStopProcessInstanceIdList(String processInstanceId) { + List list = processInstanceRecordService.lambdaQuery() + .eq(ProcessInstanceRecord::getParentProcessInstanceId, processInstanceId) + .list(); + + List collect = new ArrayList<>(list.stream().map(ProcessInstanceRecord::getProcessInstanceId).toList()); + + for (ProcessInstanceRecord processInstanceRecord : list) { + List allStopProcessInstanceIdList = getAllStopProcessInstanceIdList( + processInstanceRecord.getProcessInstanceId()); + + collect.addAll(allStopProcessInstanceIdList); + + } + return collect; + } }