feat(task): completeTask时同步更新主表单数据

- 扩展queryTask接口支持选择主表单数据源
-
- 优化已完成任务的数据获取逻辑
- 保持原有节点快照数据作为备选方案
This commit is contained in:
PIGCLOUD committed 2025-12-28 11:25:33 +08:00
1 parent ab97082cd8
commit d6c1a0aad1
3 files changed
+416 -309

No files matched your search

@@ -48,13 +48,15 @@ public class ProcessTaskController {
* </p>
* @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);
}
@@ -24,6 +24,18 @@ public interface ITaskService {
*/
R queryTask(String taskId, boolean view);
/**
* 查询任务详情(支持选择表单数据源)
* <p>
* 获取任务的详细信息,包括: - 任务基本信息(名称、创建时间、执行人等) - 所属流程信息 - 表单数据和权限 - 历史审批记录
* </p>
* @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);
/**
* 完成任务
* <p>
@@ -44,10 +44,10 @@ import java.util.*;
* <p>
* 该类是流程任务操作的核心服务,负责处理用户与流程任务的交互,主要功能包括: 1. 任务查询(待办任务详情、任务表单权限等) 2. 任务操作(完成、委托、转办、退回等) 3.
* 任务统计(首页数据看板) 4. 流程控制(终止流程、加签等高级操作)
*
* <p>
* 该服务整合了流程引擎的任务API,为前端提供统一的任务操作接口
* </p>
*
*
* @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;
/**
* 查询首页数据看板
* <p>
* 获取当前用户的任务统计数据,包括: 1. 待办任务数量 2. 已办任务数量 3. 我发起的流程数量 4. 抄送给我的数量
* </p>
* @return R 包含IndexPageStatistics对象,含有各项统计数据
*/
@Override
public R queryTaskData() {
/**
* 查询首页数据看板
* <p>
* 获取当前用户的任务统计数据,包括: 1. 待办任务数量 2. 已办任务数量 3. 我发起的流程数量 4. 抄送给我的数量
* </p>
*
* @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);
}
/**
* 查询任务
* <p>
* 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4.
* 任务是否可委托、是否是委托任务等状态
*
* 对于已完成的任务,会从历史记录中获取任务数据
* </p>
* @param taskId 任务ID
* @param view 是否是查看模式(true:只查看,false:可操作)
* @return R 包含任务详情的Dict对象
* @throws Exception 当解析JSON数据失败时抛出异常
*/
@SneakyThrows
@Override
public R queryTask(String taskId, boolean view) {
/**
* 查询任务
* <p>
* 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4.
* 任务是否可委托、是否是委托任务等状态
* <p>
* 对于已完成的任务,会从历史记录中获取任务数据
* </p>
*
* @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();
/**
* 查询任务(支持选择表单数据源)
* <p>
* 查询指定任务的详细信息,包括: 1. 任务的基本信息(节点名称、执行人等) 2. 流程表单定义和表单数据 3. 当前节点的表单权限配置 4.
* 任务是否可委托、是否是委托任务等状态
* <p>
* 对于已完成的任务,可选择从主表单数据或节点快照读取表单数据
* </p>
*
* @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> 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> 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<String, Object> variables = taskService.getVariables(taskId);
Map<String, Object> 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> historicTaskInstance = Optional
.ofNullable(historyService.createHistoricTaskInstanceQuery()
.taskId(taskId)
.taskAssignee(String.valueOf(userId))
.singleResult());
String flowId = NodeUtil.getFlowId(processDefinitionId);
Map<String, Object> variables = taskService.getVariables(taskId);
Map<String, Object> 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> 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<HistoricVariableInstance> variableInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(processInstanceId)
.list();
Map<String, Object> variableAll = new HashMap<>();
String flowId = NodeUtil.getFlowId(processDefinitionId);
if (CollUtil.isNotEmpty(variableInstanceList)) {
variableInstanceList.forEach(historicVariableInstance -> variableAll
.put(historicVariableInstance.getVariableName(), historicVariableInstance.getValue()));
}
List<HistoricVariableInstance> variableInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(processInstanceId)
.list();
Map<String, Object> 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<String, Object> 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<String, Object> collect = objectMapper.readValue(data, new TypeReference<>() {
});
paramMap.putAll(collect);
// 变量
Map<String, Object> 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<String, Object> mainFormData = objectMapper.readValue(processInstanceRecord.getFormData(),
new TypeReference<Map<String, Object>>() {
});
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<String, String> 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<String, Object> 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<String, String> formPerms = node.getFormPerms();
/**
* 完成任务
* <p>
* 执行任务的完成操作,将任务标记为已完成并流转到下一个节点。 该方法会将当前用户ID设置到任务参数中,确保任务由正确的用户完成
* </p>
* @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<String, Object> 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());
/**
* 完成任务
* <p>
* 执行任务的完成操作,将任务标记为已完成并流转到下一个节点。 该方法会将当前用户ID设置到任务参数中,确保任务由正确的用户完成
* </p>
*
* @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<String, Object> taskLocalParamMap = taskParamDto.getTaskLocalParamMap();
if (CollUtil.isNotEmpty(taskLocalParamMap)) {
taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap);
}
/**
* 前加签
* <p>
* 将任务委托给其他用户处理,委托后任务会分配给被委托人, 被委托人完成后任务会返回给原委托人
* </p>
* @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());
/**
* 加签完成任务
* <p>
* 被委托人完成委托任务后,通过此方法将任务返回给原委托人。 这是委托流程的第二步,用于结束委托状态
* </p>
* @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();
/**
* 设置执行人
* <p>
* 转办任务给其他用户,与委托不同,转办后任务直接由新执行人处理, 不会返回给原执行人
* </p>
* @param taskParamDto 任务参数,包含任务ID、新执行人ID等
* @return R 操作结果
*/
@Override
public R setAssignee(TaskParamDto taskParamDto) {
Map<String, Object> taskLocalParamMap = taskParamDto.getTaskLocalParamMap();
if (CollUtil.isNotEmpty(taskLocalParamMap)) {
taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap);
}
if (processInstanceRecord != null) {
try {
// 合并现有表单数据和新提交的数据
Map<String, Object> existingFormData = new HashMap<>();
if (StrUtil.isNotBlank(processInstanceRecord.getFormData())) {
existingFormData = objectMapper.readValue(processInstanceRecord.getFormData(),
new TypeReference<Map<String, Object>>() {
});
}
// 设置任务的 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);
// 不中断流程执行,仅记录错误
}
}
}
/**
* 结束流程
* <p>
* 强制终止流程实例的执行。该方法会: 1. 递归查找并终止所有子流程实例 2. 更新流程实例记录状态为已结束 3.
* 设置结束原因为强制终止(finishReason=9)
*
* 通常用于流程异常或需要强制结束的场景
* </p>
* @param taskParamDto 任务参数,包含流程实例ID
* @return R 操作结果
*/
@Override
public R stopProcessInstance(TaskParamDto taskParamDto) {
String reqProcessInstanceId = taskParamDto.getProcessInstanceId();
List<String> 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<String> 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();
}
/**
* 退回
* <p>
* 将任务退回到指定的历史节点重新处理。 常用于审批不通过需要重新修改的场景
* </p>
* @param taskParamDto 任务参数,包含任务ID、目标节点ID、退回原因等
* @return R 操作结果
*/
@Override
public R back(TaskParamDto taskParamDto) {
taskParamDto.setUserId(SecurityUtils.getUser().getId().toString());
return flowableEngineService.back(taskParamDto);
}
/**
* 前加签
* <p>
* 将任务委托给其他用户处理,委托后任务会分配给被委托人, 被委托人完成后任务会返回给原委托人
* </p>
*
* @param taskParamDto 任务参数,包含任务ID、被委托人ID列表等
* @return R 操作结果
*/
@Transactional
@Override
public R delegateTask(TaskParamDto taskParamDto) {
taskParamDto.setUserId(SecurityUtils.getUser().getId().toString());
return flowableEngineService.delegateTask(taskParamDto);
}
/**
* 递归获取所有需要停止的子流程实例ID列表
* <p>
* 当一个流程包含子流程时,终止主流程需要同时终止所有子流程。 该方法递归查找指定流程的所有子流程实例ID
* </p>
* @param processInstanceId 父流程实例ID
* @return 所有子流程实例ID列表
*/
private List<String> getAllStopProcessInstanceIdList(String processInstanceId) {
List<ProcessInstanceRecord> list = processInstanceRecordService.lambdaQuery()
.eq(ProcessInstanceRecord::getParentProcessInstanceId, processInstanceId)
.list();
/**
* 加签完成任务
* <p>
* 被委托人完成委托任务后,通过此方法将任务返回给原委托人。 这是委托流程的第二步,用于结束委托状态
* </p>
*
* @param taskParamDto 任务参数,包含任务ID、审批意见等
* @return R 操作结果
*/
@Override
public R resolveTask(TaskParamDto taskParamDto) {
return flowableEngineService.resolveTask(taskParamDto);
}
List<String> collect = new ArrayList<>(list.stream().map(ProcessInstanceRecord::getProcessInstanceId).toList());
/**
* 设置执行人
* <p>
* 转办任务给其他用户,与委托不同,转办后任务直接由新执行人处理, 不会返回给原执行人
* </p>
*
* @param taskParamDto 任务参数,包含任务ID、新执行人ID等
* @return R 操作结果
*/
@Override
public R setAssignee(TaskParamDto taskParamDto) {
Map<String, Object> taskLocalParamMap = taskParamDto.getTaskLocalParamMap();
if (CollUtil.isNotEmpty(taskLocalParamMap)) {
taskService.setVariablesLocal(taskParamDto.getTaskId(), taskLocalParamMap);
}
for (ProcessInstanceRecord processInstanceRecord : list) {
List<String> 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();
}
/**
* 结束流程
* <p>
* 强制终止流程实例的执行。该方法会: 1. 递归查找并终止所有子流程实例 2. 更新流程实例记录状态为已结束 3.
* 设置结束原因为强制终止(finishReason=9)
* <p>
* 通常用于流程异常或需要强制结束的场景
* </p>
*
* @param taskParamDto 任务参数,包含流程实例ID
* @return R 操作结果
*/
@Override
public R stopProcessInstance(TaskParamDto taskParamDto) {
String reqProcessInstanceId = taskParamDto.getProcessInstanceId();
List<String> 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<String> 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();
}
/**
* 退回
* <p>
* 将任务退回到指定的历史节点重新处理。 常用于审批不通过需要重新修改的场景
* </p>
*
* @param taskParamDto 任务参数,包含任务ID、目标节点ID、退回原因等
* @return R 操作结果
*/
@Override
public R back(TaskParamDto taskParamDto) {
taskParamDto.setUserId(SecurityUtils.getUser().getId().toString());
return flowableEngineService.back(taskParamDto);
}
/**
* 递归获取所有需要停止的子流程实例ID列表
* <p>
* 当一个流程包含子流程时,终止主流程需要同时终止所有子流程。 该方法递归查找指定流程的所有子流程实例ID
* </p>
*
* @param processInstanceId 父流程实例ID
* @return 所有子流程实例ID列表
*/
private List<String> getAllStopProcessInstanceIdList(String processInstanceId) {
List<ProcessInstanceRecord> list = processInstanceRecordService.lambdaQuery()
.eq(ProcessInstanceRecord::getParentProcessInstanceId, processInstanceId)
.list();
List<String> collect = new ArrayList<>(list.stream().map(ProcessInstanceRecord::getProcessInstanceId).toList());
for (ProcessInstanceRecord processInstanceRecord : list) {
List<String> allStopProcessInstanceIdList = getAllStopProcessInstanceIdList(
processInstanceRecord.getProcessInstanceId());
collect.addAll(allStopProcessInstanceIdList);
}
return collect;
}
}