feat(pigx-flow-biz): 支持分组ID为空时查询所有未隐藏流程

This commit is contained in:
PIGCLOUD committed 2026-05-11 17:33:12 +08:00
1 parent 26efa7acbf
commit 5f3bff90e1
11 files changed
+411 -118

No files matched your search

@@ -27,46 +27,46 @@ import java.util.List;
@FeignClient(contextId = "remoteFlowApiFlowService", value = ServiceNameConstants.FLOW_SERVER)
public interface RemoteFlowApiFlowService {
/**
* 查询当前用户可发起的流程列表
* <p>
* 根据当前登录用户的权限,查询该用户有权发起的所有流程。
* 返回结果按分组组织,方便前端展示。
* </p>
*
* @return 分组后的流程列表,包含分组信息和各分组下的流程
*/
@GetMapping("/combination/group/listCurrentUserStartGroup")
R<List<FormGroupVo>> listCurrentUserStartGroup();
/**
* 查询当前用户可发起的流程列表
* <p>
* 根据当前登录用户的权限,查询该用户有权发起的所有流程。
* 返回结果按分组组织,方便前端展示。
* </p>
*
* @return 分组后的流程列表,包含分组信息和各分组下的流程
*/
@GetMapping("/combination/group/listCurrentUserStartGroup")
R<List<FormGroupVo>> listCurrentUserStartGroup();
/**
* 获取流程详细信息
* <p>
* 根据流程ID获取流程的完整配置信息,包括:
* - 流程基本信息(名称、图标、描述等)
* - 表单配置(表单项定义、验证规则等)
* - 流程配置(节点定义、流转规则等)
* </p>
*
* @param flowId 流程定义ID
* @return 流程详细信息视图对象
*/
@GetMapping("/process/getDetail")
R<ProcessVO> getDetail(@RequestParam("flowId") String flowId);
/**
* 获取流程详细信息
* <p>
* 根据流程ID获取流程的完整配置信息,包括:
* - 流程基本信息(名称、图标、描述等)
* - 表单配置(表单项定义、验证规则等)
* - 流程配置(节点定义、流转规则等)
* </p>
*
* @param flowId 流程定义ID
* @return 流程详细信息视图对象
*/
@GetMapping("/process/getDetail")
R<ProcessVO> getDetail(@RequestParam("flowId") String flowId);
/**
* 启动流程实例
* <p>
* 根据流程定义创建新的流程实例。启动流程时需要提供:
* - 流程ID
* - 表单数据
* - 需要自选的节点执行人信息
* </p>
*
* @param paramDto 流程实例启动参数,包含流程ID、表单数据等
* @return 启动成功后的流程实例ID
*/
@PostMapping("/process-instance/startProcessInstance")
R<String> startProcessInstance(@RequestBody ProcessInstanceParamDto paramDto);
/**
* 启动流程实例
* <p>
* 根据流程定义创建新的流程实例。启动流程时需要提供:
* - 流程ID
* - 表单数据
* - 需要自选的节点执行人信息
* </p>
*
* @param paramDto 流程实例启动参数,包含流程ID、表单数据等
* @return 启动成功后的流程实例ID
*/
@PostMapping("/process-instance/startProcessInstance")
R<String> startProcessInstance(@RequestBody ProcessInstanceParamDto paramDto);
}
@@ -60,6 +60,36 @@ public class ProcessInstanceConstant {
*/
public static final String REJECT_TO_STARTER_VAR = "rejectToStarter";
/**
* 流程结束原因:拒绝
*/
public static final String FINISH_REASON_REFUSE = "0";
/**
* 流程结束原因:通过
*/
public static final String FINISH_REASON_PASS = "1";
/**
* 流程结束原因:撤回
*/
public static final String FINISH_REASON_WITHDRAW = "3";
/**
* 流程结束原因:终止
*/
public static final String FINISH_REASON_TERMINATE = "9";
/**
* 流程节点执行人状态:撤回
*/
public static final int ASSIGN_USER_STATUS_WITHDRAW = 3;
/**
* 流程节点执行人状态:终止
*/
public static final int ASSIGN_USER_STATUS_TERMINATE = 5;
/**
* 多人审批模式:会签 所有审批人都必须同意才能通过,任何一人拒绝则不通过
*/
@@ -1,6 +1,6 @@
package com.pig4cloud.pigx.flow.dto;
import cn.idev.excel.annotation.ExcelIgnore;
import org.apache.fesod.sheet.annotation.ExcelIgnore;
import com.baomidou.mybatisplus.annotation.TableField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -44,4 +44,14 @@ public class ProcessInstanceRecordVo extends ProcessInstanceRecord {
*/
private Boolean hasPrintTemplate;
/**
* 是否允许撤回
*/
private Boolean canWithdraw;
/**
* 不可撤回原因
*/
private String withdrawDisabledReason;
}
@@ -6,6 +6,7 @@ import com.pig4cloud.pigx.flow.service.ICombinationGroupService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
@@ -37,7 +38,7 @@ public class ProcessCombinationController {
* @return R 统一响应对象,包含分页后的流程组及流程列表数据
*/
@GetMapping("listGroupWithProcess")
public R listGroupWithProcess(Page page, Long groupId) {
public R listGroupWithProcess(Page page, @RequestParam(required = false) Long groupId) {
return combinationGroupService.listGroupWithProcess(page, groupId);
}
@@ -144,6 +144,17 @@ public class ProcessTaskController {
}
/**
* 撤回流程实例
*
* @param taskParamDto 撤回参数,包含流程实例ID
* @return R 统一响应对象,操作成功返回撤回信息
*/
@PostMapping("withdrawProcessInstance")
public R withdrawProcessInstance(@RequestBody TaskParamDto taskParamDto) {
return taskService.withdrawProcessInstance(taskParamDto);
}
/**
* 退回任务
* <p>
@@ -87,6 +87,14 @@ public interface ITaskService {
*/
R stopProcessInstance(TaskParamDto taskParamDto);
/**
* 撤回流程实例
*
* @param taskParamDto 任务参数,包含流程实例ID
* @return 操作结果
*/
R withdrawProcessInstance(TaskParamDto taskParamDto);
/**
* 退回任务
* <p>
@@ -48,13 +48,13 @@ public class CombinationGroupServiceImpl implements ICombinationGroupService {
* 根据流程分组ID查询该分组下所有未隐藏的流程,按照排序值升序和创建时间降序排列
* </p>
* @param page 分页参数,用于控制查询结果的分页
* @param groupId 流程分组ID
* @param groupId 流程分组ID,可为空;为空时返回所有分组下的未隐藏流程
* @return R 包含分页后的流程列表
*/
@Override
public R listGroupWithProcess(Page page, Long groupId) {
return R.ok(processService.lambdaQuery()
.eq(Process::getGroupId, groupId)
.eq(Objects.nonNull(groupId), Process::getGroupId, groupId)
.eq(Process::getHidden, YesNoEnum.NO.getCode())
.orderByAsc(Process::getSort)
.orderByDesc(Process::getCreateTime)
@@ -18,11 +18,10 @@ import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import com.pig4cloud.pigx.flow.constant.NodeStatusEnum;
import com.pig4cloud.pigx.flow.constant.NodeUserTypeEnum;
import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant;
import com.pig4cloud.pigx.flow.dto.*;
import com.pig4cloud.pigx.flow.entity.*;
import com.pig4cloud.pigx.flow.entity.Process;
import com.pig4cloud.pigx.flow.entity.ProcessCopy;
import com.pig4cloud.pigx.flow.entity.ProcessInstanceRecord;
import com.pig4cloud.pigx.flow.entity.ProcessNodeRecord;
import com.pig4cloud.pigx.flow.service.*;
import com.pig4cloud.pigx.flow.support.utils.NodeFormatUtil;
import com.pig4cloud.pigx.flow.support.utils.NodeUtil;
@@ -42,6 +41,7 @@ import org.flowable.engine.history.HistoricActivityInstanceQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Service;
import java.time.ZoneId;
@@ -82,6 +82,8 @@ public class ProcessInstanceServiceImpl implements IProcessInstanceService {
private final IProcessNodeRecordService processNodeRecordService;
private final IProcessNodeRecordAssignUserService processNodeRecordAssignUserService;
private final ObjectMapper objectMapper;
/**
@@ -374,36 +376,59 @@ public class ProcessInstanceServiceImpl implements IProcessInstanceService {
.collect(Collectors.toSet());
List<Process> processList = CollUtil.isEmpty(flowIds) ? Collections.emptyList()
: processService.lambdaQuery().in(Process::getFlowId, flowIds).list();
Set<String> processInstanceIds = voList.stream()
.map(ProcessInstanceRecordVo::getProcessInstanceId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
Map<String, List<ProcessNodeRecordAssignUser>> assignUserMap = queryAssignUserMap(processInstanceIds);
// 批量查询进行中流程的 rejectToStarter 变量,避免 N+1
Set<String> runningInstanceIds = voList.stream()
.filter(vo -> Objects.equals(vo.getStatus(), NodeStatusEnum.JXZ.getCode()))
.map(ProcessInstanceRecordVo::getProcessInstanceId)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toSet());
Set<String> rejectToStarterIds = batchQueryRejectToStarter(runningInstanceIds);
// 批量查询驳回待提交的 root 任务,避免 N+1
Map<String, String> resubmitTaskMap = Collections.emptyMap();
if (!rejectToStarterIds.isEmpty()) {
resubmitTaskMap = taskService.createTaskQuery()
.processInstanceIdIn(rejectToStarterIds)
.taskDefinitionKey("root")
.list()
.stream()
.collect(Collectors.toMap(Task::getProcessInstanceId, Task::getId, (a, b) -> a));
}
// 构建打印模板 Map,避免循环内 stream 遍历
Map<String, Process> processMap = processList.stream()
.collect(Collectors.toMap(p -> p.getFlowId().toLowerCase(), p -> p, (a, b) -> a));
for (ProcessInstanceRecordVo vo : voList) {
try {
// 从 Flowable 变量中获取 rejectToStarter 状态
Object rejectVar = runtimeService.getVariable(
vo.getProcessInstanceId(),
"rejectToStarter"
);
vo.setRejectToStarter(Boolean.TRUE.equals(rejectVar));
boolean rejectToStarter = false;
if (Objects.equals(vo.getStatus(), NodeStatusEnum.JXZ.getCode())) {
rejectToStarter = rejectToStarterIds.contains(vo.getProcessInstanceId());
vo.setRejectToStarter(rejectToStarter);
// 如果是驳回状态,获取 root 任务 ID 用于重新提交
if (Boolean.TRUE.equals(vo.getRejectToStarter())) {
Task task = taskService.createTaskQuery()
.processInstanceId(vo.getProcessInstanceId())
.taskDefinitionKey("root")
.singleResult();
if (task != null) {
vo.setResubmitTaskId(task.getId());
}
if (rejectToStarter) {
vo.setResubmitTaskId(resubmitTaskMap.get(vo.getProcessInstanceId()));
}
} catch (Exception e) {
// 流程可能已结束,设置为 false
} else {
vo.setRejectToStarter(false);
}
// 检查是否配置了打印模板(不依赖运行时变量,无需放在 try-catch 内)
processList.stream()
.filter(process -> process.getFlowId().equalsIgnoreCase(vo.getFlowId()))
.findFirst()
.ifPresent(process -> vo.setHasPrintTemplate(StrUtil.isNotBlank(process.getPrintTemplate())));
WithdrawState withdrawState = buildWithdrawState(vo, vo.getRejectToStarter(),
assignUserMap.getOrDefault(vo.getProcessInstanceId(), Collections.emptyList()));
vo.setCanWithdraw(withdrawState.canWithdraw);
vo.setWithdrawDisabledReason(withdrawState.disabledReason);
// 检查是否配置了打印模板
Process matchedProcess = processMap.get(vo.getFlowId().toLowerCase());
if (matchedProcess != null) {
vo.setHasPrintTemplate(StrUtil.isNotBlank(matchedProcess.getPrintTemplate()));
}
}
// 构建返回的分页对象
@@ -549,10 +574,128 @@ public class ProcessInstanceServiceImpl implements IProcessInstanceService {
Node nodeDto = objectMapper.readValue(process, Node.class);
Map<String, String> formPerms1 = nodeDto.getFormPerms();
boolean rejectToStarter = Objects.equals(processInstanceRecord.getStatus(), NodeStatusEnum.JXZ.getCode())
&& isRejectToStarter(processInstanceId);
WithdrawState withdrawState = buildWithdrawState(processInstanceRecord, rejectToStarter,
queryAssignUserMap(Collections.singleton(processInstanceId))
.getOrDefault(processInstanceId, Collections.emptyList()));
Dict set = Dict.create().set(ProcessInstanceRecord.Fields.processInstanceId, processInstanceId).set(ProcessInstanceRecord.Fields.status, processInstanceRecord.getStatus()).set("process", oaForms.getProcess()).set("formItems", oaForms.getFormItems()).set("formData", formData).set("formConfig", oaForms.getFormConfig()).set("formPerms", formPerms1);
Dict set = Dict.create()
.set(ProcessInstanceRecord.Fields.processInstanceId, processInstanceId)
.set(ProcessInstanceRecord.Fields.status, processInstanceRecord.getStatus())
.set("finishReason", processInstanceRecord.getFinishReason())
.set("rejectToStarter", rejectToStarter)
.set("canWithdraw", withdrawState.canWithdraw)
.set("withdrawDisabledReason", withdrawState.disabledReason)
.set("process", oaForms.getProcess())
.set("formItems", oaForms.getFormItems())
.set("formData", formData)
.set("formConfig", oaForms.getFormConfig())
.set("formPerms", formPerms1);
return R.ok(set);
}
private Map<String, List<ProcessNodeRecordAssignUser>> queryAssignUserMap(Set<String> processInstanceIds) {
if (CollUtil.isEmpty(processInstanceIds)) {
return Collections.emptyMap();
}
return processNodeRecordAssignUserService.lambdaQuery()
.in(ProcessNodeRecordAssignUser::getProcessInstanceId, processInstanceIds)
.ne(ProcessNodeRecordAssignUser::getNodeId, "root")
.list()
.stream()
.collect(Collectors.groupingBy(ProcessNodeRecordAssignUser::getProcessInstanceId));
}
private boolean isRejectToStarter(String processInstanceId) {
try {
Object rejectVar = runtimeService.getVariable(processInstanceId,
ProcessInstanceConstant.REJECT_TO_STARTER_VAR);
return Boolean.TRUE.equals(rejectVar);
} catch (Exception e) {
log.debug("查询驳回待提交状态失败: processInstanceId={}, error={}", processInstanceId, e.getMessage());
return false;
}
}
/**
* 批量查询流程实例的驳回到发起人状态
*
* @param processInstanceIds 流程实例ID集合
* @return 处于驳回待提交状态的流程实例ID集合
*/
private Set<String> batchQueryRejectToStarter(Set<String> processInstanceIds) {
if (CollUtil.isEmpty(processInstanceIds)) {
return Collections.emptySet();
}
try {
return historyService.createHistoricVariableInstanceQuery()
.processInstanceIds(processInstanceIds)
.variableName(ProcessInstanceConstant.REJECT_TO_STARTER_VAR)
.list()
.stream()
.filter(v -> Boolean.TRUE.equals(v.getValue()))
.map(HistoricVariableInstance::getProcessInstanceId)
.collect(Collectors.toSet());
} catch (Exception e) {
log.warn("批量查询驳回状态失败,降级为逐条查询: {}", e.getMessage());
return processInstanceIds.stream()
.filter(this::isRejectToStarter)
.collect(Collectors.toSet());
}
}
private WithdrawState buildWithdrawState(ProcessInstanceRecord processInstanceRecord, Boolean rejectToStarter,
List<ProcessNodeRecordAssignUser> assignUsers) {
if (!Objects.equals(processInstanceRecord.getStatus(), NodeStatusEnum.JXZ.getCode())) {
return new WithdrawState(false, getFinishedProcessReason(processInstanceRecord.getFinishReason()));
}
if (Boolean.TRUE.equals(rejectToStarter)) {
return new WithdrawState(false, "驳回待提交状态不可撤回");
}
boolean hasProcessedApproval = assignUsers.stream().anyMatch(this::isProcessedAssignUser);
if (hasProcessedApproval) {
return new WithdrawState(false, "首个审批节点已处理,无法撤回");
}
return new WithdrawState(true, null);
}
private boolean isProcessedAssignUser(ProcessNodeRecordAssignUser assignUser) {
return assignUser.getEndTime() != null
|| !Objects.equals(assignUser.getStatus(), NodeStatusEnum.JXZ.getCode());
}
private String getFinishedProcessReason(String finishReason) {
if (StrUtil.equals(finishReason, ProcessInstanceConstant.FINISH_REASON_WITHDRAW)) {
return "流程已撤回";
}
if (StrUtil.equals(finishReason, ProcessInstanceConstant.FINISH_REASON_TERMINATE)) {
return "流程已终止";
}
if (StrUtil.equals(finishReason, ProcessInstanceConstant.FINISH_REASON_PASS)) {
return "流程已通过";
}
if (StrUtil.equals(finishReason, ProcessInstanceConstant.FINISH_REASON_REFUSE)) {
return "流程已拒绝";
}
return "流程已结束";
}
private static final class WithdrawState {
private final boolean canWithdraw;
private final String disabledReason;
private WithdrawState(boolean canWithdraw, String disabledReason) {
this.canWithdraw = canWithdraw;
this.disabledReason = disabledReason;
}
}
}
@@ -10,14 +10,13 @@ import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
import com.pig4cloud.pigx.flow.constant.NodeStatusEnum;
import com.pig4cloud.pigx.flow.constant.ProcessInstanceConstant;
import com.pig4cloud.pigx.flow.dto.IndexPageStatistics;
import com.pig4cloud.pigx.flow.dto.Node;
import com.pig4cloud.pigx.flow.dto.TaskParamDto;
import com.pig4cloud.pigx.flow.dto.TaskResultDto;
import com.pig4cloud.pigx.flow.entity.*;
import com.pig4cloud.pigx.flow.entity.Process;
import com.pig4cloud.pigx.flow.entity.ProcessCopy;
import com.pig4cloud.pigx.flow.entity.ProcessInstanceRecord;
import com.pig4cloud.pigx.flow.entity.ProcessNodeRecordAssignUser;
import com.pig4cloud.pigx.flow.service.*;
import com.pig4cloud.pigx.flow.support.utils.NodeUtil;
import lombok.RequiredArgsConstructor;
@@ -67,6 +66,8 @@ public class TaskServiceImpl implements ITaskService {
private final IProcessInstanceRecordService processInstanceRecordService;
private final IProcessNodeRecordService processNodeRecordService;
private final IFlowableEngineService flowableEngineService;
private final TaskService taskService;
@@ -279,6 +280,8 @@ public class TaskServiceImpl implements ITaskService {
}
Dict set = Dict.create()
.set("taskId", taskId)
.set("nodeId", taskResultDto.getNodeId())
.set("processInstanceId", taskResultDto.getProcessInstanceId())
.set("node", node)
.set("process", oaForms.getProcess())
@@ -476,39 +479,60 @@ public class TaskServiceImpl implements ITaskService {
*/
@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();
}
});
return terminateProcessInstance(taskParamDto.getProcessInstanceId(),
ProcessInstanceConstant.FINISH_REASON_TERMINATE);
}
// 停止流程实例后,更新流程实例记录状态
processInstanceRecordService.lambdaUpdate()
.set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode())
.set(ProcessInstanceRecord::getFinishReason, String.valueOf(NodeStatusEnum.YZZ.getCode()))
.set(ProcessInstanceRecord::getEndTime, new Date())
.eq(ProcessInstanceRecord::getProcessInstanceId, reqProcessInstanceId)
.update(new ProcessInstanceRecord());
return R.ok();
/**
* 撤回流程
*
* @param taskParamDto 任务参数,包含流程实例ID
* @return R 操作结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R withdrawProcessInstance(TaskParamDto taskParamDto) {
String processInstanceId = taskParamDto.getProcessInstanceId();
if (StrUtil.isBlank(processInstanceId)) {
return R.failed("流程实例ID不能为空");
}
long userId = SecurityUtils.getUser().getId();
ProcessInstanceRecord processInstanceRecord = processInstanceRecordService.lambdaQuery()
.eq(ProcessInstanceRecord::getProcessInstanceId, processInstanceId)
.one();
if (processInstanceRecord == null
|| !Objects.equals(processInstanceRecord.getStatus(), NodeStatusEnum.JXZ.getCode())) {
return R.failed("流程不存在或已结束");
}
if (!Objects.equals(processInstanceRecord.getUserId(), userId)) {
return R.failed("仅发起人可撤回流程");
}
try {
Object rejectToStarter = runtimeService.getVariable(processInstanceId,
ProcessInstanceConstant.REJECT_TO_STARTER_VAR);
if (Boolean.TRUE.equals(rejectToStarter)) {
return R.failed("驳回待提交状态不可撤回");
}
} catch (Exception e) {
log.warn("查询撤回状态变量失败: processInstanceId={}, error={}", processInstanceId, e.getMessage());
}
boolean hasProcessedApproval = processNodeRecordAssignUserService.lambdaQuery()
.eq(ProcessNodeRecordAssignUser::getProcessInstanceId, processInstanceId)
.ne(ProcessNodeRecordAssignUser::getNodeId, "root")
.and(wrapper -> wrapper
.isNotNull(ProcessNodeRecordAssignUser::getEndTime)
.or()
.ne(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.JXZ.getCode()))
.exists();
if (hasProcessedApproval) {
return R.failed("首个审批节点已处理,无法撤回");
}
return terminateProcessInstance(processInstanceId, ProcessInstanceConstant.FINISH_REASON_WITHDRAW);
}
/**
@@ -626,4 +650,72 @@ public class TaskServiceImpl implements ITaskService {
return collect;
}
private R terminateProcessInstance(String reqProcessInstanceId, String finishReason) {
if (StrUtil.isBlank(reqProcessInstanceId)) {
return R.failed("流程实例ID不能为空");
}
List<String> allStopProcessInstanceIdList = getAllStopProcessInstanceIdList(reqProcessInstanceId);
CollUtil.reverse(allStopProcessInstanceIdList);
allStopProcessInstanceIdList.add(reqProcessInstanceId);
Date now = new Date();
int assignUserStatus = Objects.equals(finishReason, ProcessInstanceConstant.FINISH_REASON_WITHDRAW)
? ProcessInstanceConstant.ASSIGN_USER_STATUS_WITHDRAW
: ProcessInstanceConstant.ASSIGN_USER_STATUS_TERMINATE;
// 先更新本地记录,确保流程结束通知能拿到准确的 finishReason
processInstanceRecordService.lambdaUpdate()
.set(ProcessInstanceRecord::getStatus, NodeStatusEnum.YJS.getCode())
.set(ProcessInstanceRecord::getFinishReason, finishReason)
.set(ProcessInstanceRecord::getEndTime, now)
.in(ProcessInstanceRecord::getProcessInstanceId, allStopProcessInstanceIdList)
.update(new ProcessInstanceRecord());
processNodeRecordService.lambdaUpdate()
.set(ProcessNodeRecord::getStatus, NodeStatusEnum.YJS.getCode())
.set(ProcessNodeRecord::getEndTime, now)
.in(ProcessNodeRecord::getProcessInstanceId, allStopProcessInstanceIdList)
.eq(ProcessNodeRecord::getStatus, NodeStatusEnum.JXZ.getCode())
.update(new ProcessNodeRecord());
processNodeRecordAssignUserService.lambdaUpdate()
.set(ProcessNodeRecordAssignUser::getStatus, assignUserStatus)
.set(ProcessNodeRecordAssignUser::getEndTime, Convert.toLocalDateTime(now))
.set(ProcessNodeRecordAssignUser::getTaskType,
Objects.equals(finishReason, ProcessInstanceConstant.FINISH_REASON_WITHDRAW)
? "WITHDRAW"
: "TERMINATE")
.in(ProcessNodeRecordAssignUser::getProcessInstanceId, allStopProcessInstanceIdList)
.eq(ProcessNodeRecordAssignUser::getStatus, NodeStatusEnum.JXZ.getCode())
.update(new ProcessNodeRecordAssignUser());
allStopProcessInstanceIdList.forEach(processInstanceId -> {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.processInstanceTenantId(TenantContextHolder.getTenantId().toString())
.singleResult();
if (processInstance == null) {
return;
}
List<String> executionIds = runtimeService.createExecutionQuery()
.parentId(processInstanceId)
.list()
.stream()
.map(Execution::getId)
.toList();
if (CollUtil.isEmpty(executionIds)) {
log.warn("流程实例缺少可迁移执行流,跳过状态迁移: processInstanceId={}", processInstanceId);
return;
}
runtimeService.createChangeActivityStateBuilder()
.moveExecutionsToSingleActivityId(executionIds, "end")
.changeState();
});
return R.ok();
}
}
@@ -225,8 +225,6 @@ public class ModelUtil {
if (nodeDto.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue()) {
if (ord == size) {
expression = NodeExpressionStrategyFactory.handleDefaultBranch(branchs, ord - 1);
} else if (ord > 1) {
expression = NodeExpressionStrategyFactory.handleDefaultBranch(branchs, ord - 1);
} else {
expression = NodeExpressionStrategyFactory.handle(branch);
}
@@ -337,7 +335,7 @@ public class ModelUtil {
}
// 条件分支
if (node.getType() == NodeTypeEnum.EXCLUSIVE_GATEWAY.getValue().intValue()) {
flowElementList.addAll(buildInclusiveGatewayNode(node));
flowElementList.addAll(buildExclusiveGatewayNode(node));
}
// 并行分支
if (node.getType() == NodeTypeEnum.PARALLEL_GATEWAY.getValue().intValue()) {
@@ -560,27 +558,27 @@ public class ModelUtil {
}
/**
* 构建包容网关
* 构建排他网关
* <p>
* 创建包容网关(条件分支)节点和对应的合并网关。
* 包容网关用于根据条件创建不同的执行路径。
* 创建排他网关(条件分支)节点和对应的合并网关。
* 排他网关用于根据条件只选择一条执行路径,避免多个条件同时为真时并行进入多个分支。
*
* @param node 包容网关节点数据
* @param node 排他网关节点数据
* @return 包含分叉网关和合并网关的元素列表
*/
private static List<FlowElement> buildInclusiveGatewayNode(Node node) {
private static List<FlowElement> buildExclusiveGatewayNode(Node node) {
node.setTailId(StrUtil.format("{}_merge_gateway", node.getId()));
List<FlowElement> flowElementList = new ArrayList<>();
InclusiveGateway inclusiveGateway = new InclusiveGateway();
inclusiveGateway.setId(node.getId());
inclusiveGateway.setName(node.getName());
flowElementList.add(inclusiveGateway);
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
exclusiveGateway.setId(node.getId());
exclusiveGateway.setName(node.getName());
flowElementList.add(exclusiveGateway);
// 合并网关
InclusiveGateway gateway = new InclusiveGateway();
ExclusiveGateway gateway = new ExclusiveGateway();
gateway.setId(StrUtil.format("{}_merge_gateway", node.getId()));
gateway.setName(StrUtil.format("{}_合并网关", node.getName()));
flowElementList.add(gateway);