项目背景
我们公司接的客户是做资产评估的,所以对于审批&过程中的文件 要求都会非常严格,且需要留痕,那么 流程系统就是非常适合他们,客户之前也是用的 买断制产品(一个单机windows软件,只能固定操作,没有拓展性)。
ps:第一次做这方面深入分享,可能表达 以及设计上有瑕疵🤨
创建流程节点思路
知识概要
每个流程节点 都是可以配置 审批人, 可以为流程设置 默认审批人 也可以 动态设置审批人(即在流程发起时为节点选择)
需求分析
但是如果 发布的流程 存在多层嵌套,A流程执行过程会进入子流程B,这个时候B流程中的节点需要动态设置审批人, 那么问题来了,如果是 一个流程直接发布配置即可,但是他是子流程,他是不会发布的,而且有父流程也就是A流程发布,这就导致不能为他配置 审批人
解决办法
1.一次设置
既然发布时才能 配置,那我们可以通过 迭代发布流程下 所有子流程 集中处理自动审批人
2.动态设置
进入子流程前,先读取是否需要 设置审批人,然后创建一个 临时审批人 节点,为子流程配置
实现思路
1. 如何判断需要自定义审批人?
通过查看BPMN发现,在UserTask用户任务选择为 审批人自选时, -> flowable:candidateStrategy="35"

2. 临时节点应该在哪个节点创建?
经过讨论,最优解应该是在 主流程 进入 子流程 时进行创建,即使多层嵌套子流程,你总要进入必定触发。
代码实现
重写CallActivityBehavior中的 execute 方法(进入子流程时会启动)

/**
* 自定义的 CallActivity 行为类:
* - 在进入子流程前,如果子流程存在 START_USER_SELECT 节点,要求已提供对应的候选人;
* - 若未提供,则自动创建“选择子流程审批人”任务,等待补全后再继续执行。
*/
@Slf4j
@Setter
public class BpmCallActivityBehavior extends CallActivityBehavior {
private BpmProcessDefinitionService processDefinitionService;
public BpmCallActivityBehavior(CallActivity callActivity) {
super(callActivity);
}
@Override
public void execute(DelegateExecution execution) {
// 1) 获取子流程定义 Key
String subProcessKey = getSubProcessKey(execution);
// 2) 扫描子流程中的 START_USER_SELECT 任务(运行时通过 RepositoryService 获取)
List<UserTask> startUserSelectTasks = null;
try {
RepositoryService repositoryService = CommandContextUtil.getProcessEngineConfiguration().getRepositoryService();
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey(subProcessKey)
.active()
.processDefinitionTenantId(FlowableUtils.getTenantId());
org.flowable.engine.repository.ProcessDefinition def = query.singleResult();
if (def != null) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(def.getId());
startUserSelectTasks = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel);
}
} catch (Exception e) {
log.warn("[execute][检查子流程({})的 START_USER_SELECT 任务时出错,忽略校验,直接进入子流程]", subProcessKey, e);
}
// 子流程没有 START_USER_SELECT 节点,直接进入
if (CollUtil.isEmpty(startUserSelectTasks)) {
super.execute(execution);
return;
}
// 3) 若已在等待补全,检查变量是否就绪;未就绪则继续等待
Boolean waiting = (Boolean) execution.getVariable("waitingForStartUserSelectAssignees");
if (Boolean.TRUE.equals(waiting)) {
if (isSelectionComplete(startUserSelectTasks, getStartUserSelectAssignees(execution))) {
clearWaitingFlags(execution);
ensureStartUserId(execution);
super.execute(execution);
}
// 未就绪则继续等待
return;
}
// 4) 未在等待,检查当前变量是否已经完整;完整则直接进入子流程
if (isSelectionComplete(startUserSelectTasks, getStartUserSelectAssignees(execution))) {
ensureStartUserId(execution);
super.execute(execution);
return;
}
// 5) 变量不完整,创建一个“选择子流程审批人”任务,等待补全
createStartUserSelectAssigneesTask(execution, subProcessKey, startUserSelectTasks);
}
private String getSubProcessKey(DelegateExecution execution) {
CallActivity callActivity = (CallActivity) execution.getCurrentFlowElement();
return callActivity.getCalledElement();
}
@SuppressWarnings("unchecked")
private Map<String, List<Long>> getStartUserSelectAssignees(DelegateExecution execution) {
return (Map<String, List<Long>>) execution.getVariable(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES);
}
private boolean isSelectionComplete(List<UserTask> tasks, Map<String, List<Long>> selects) {
if (CollUtil.isEmpty(tasks)) {
return true;
}
if (selects == null) {
return false;
}
for (UserTask task : tasks) {
List<Long> ids = selects.get(task.getId());
if (CollUtil.isEmpty(ids)) {
return false;
}
}
return true;
}
private void createStartUserSelectAssigneesTask(DelegateExecution execution, String subProcessKey, List<UserTask> tasks) {
TaskService taskService = CommandContextUtil.getProcessEngineConfiguration().getTaskService();
TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID());
task.setName("选择子流程审批人");
task.setDescription("请为即将启动的子流程选择审批人");
task.setAssignee(getCurrentUserId(execution));
task.setProcessInstanceId(execution.getProcessInstanceId());
// 注意:不再绑定 executionId,避免 complete 时触发引擎导致 “this activity isn't waiting for a trigger”
// task.setExecutionId(execution.getId());
task.setTaskDefinitionKey("selectStartUserAssignees_" + execution.getCurrentActivityId());
// 先保存任务,再写变量,避免 Flowable 统计计数时的 NPE
taskService.saveTask(task);
// 设置辅助变量,便于前端渲染 + 后端恢复(统一用 TaskService 设置)
Map<String, Object> vars = new HashMap<>();
vars.put("formType", 10); // 让前端展示“选择节点审批人”按钮
vars.put("subProcessKey", subProcessKey);
vars.put("callActivityId", execution.getCurrentActivityId());
// 记录当前 callActivity 的 executionId,审批完成时用于恢复继续执行(仅信息保存)
vars.put("callActivityExecutionId", execution.getId());
vars.put("requiredStartUserSelectTaskIds",
tasks.stream().map(UserTask::getId).collect(Collectors.toList()));
taskService.setVariables(task.getId(), vars);
// 标记为等待状态
execution.setVariable("waitingForStartUserSelectAssignees", true);
execution.setVariable("startUserSelectTaskId", task.getId());
log.info("[createStartUserSelectAssigneesTask][流程实例({}) 创建选择子流程审批人任务({}) 子流程({}) 必填任务IDs({})]",
execution.getProcessInstanceId(), task.getId(), subProcessKey,
tasks.stream().map(UserTask::getId).collect(Collectors.toList()));
}
private void clearWaitingFlags(DelegateExecution execution) {
// 清理流程变量
execution.removeVariable("waitingForStartUserSelectAssignees");
execution.removeVariable("startUserSelectTaskId");
}
private void ensureStartUserId(DelegateExecution execution) {
if (execution.getVariable("startUserId") == null) {
String uid = getCurrentUserId(execution);
if (uid != null) {
execution.setVariable("startUserId", uid);
}
}
}
private String getCurrentUserId(DelegateExecution execution) {
// 优先用 Flowable Authentication 上下文,其次用流程变量 startUserId
String userId = Authentication.getAuthenticatedUserId();
if (userId != null) {
return userId;
}
Object startUserId = execution.getVariable("startUserId");
return startUserId != null ? Objects.toString(startUserId, null) : null;
}
}