Browse Source

refactor(flow): 重构流程候选用户名解析逻辑

- 提取候选用户名解析为独立方法 getCandidateUsernamesByExecution
- 添加对 EL 表达式的完整支持,包括 ${projectBean.getCommonReportManager(execution)} 形式
- 简化 AutoSkipTaskListener 中的用户名获取逻辑
- 统一多实例分配监听器中的候选用户处理方式
- 移除重复的字符串分割和变量获取代码
- 增强表达式求值的安全性,添加执行上下文验证
feature/2026-08/0812-ccc-dev
chenchuchuan 1 week ago
parent
commit
fb35f2b406
  1. 11
      common/common-flow/src/main/java/apelet/common/flow/listener/AutoSkipTaskListener.java
  2. 8
      common/common-flow/src/main/java/apelet/common/flow/listener/MultiInstanceAssigneeListener.java
  3. 10
      common/common-flow/src/main/java/apelet/common/flow/service/FlowApiService.java
  4. 78
      common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowApiServiceImpl.java
  5. 12
      common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowTaskExtServiceImpl.java

11
common/common-flow/src/main/java/apelet/common/flow/listener/AutoSkipTaskListener.java

@ -135,16 +135,7 @@ public class AutoSkipTaskListener implements TaskListener {
} else { } else {
FlowTaskExt flowTaskExt = flowTaskExtService FlowTaskExt flowTaskExt = flowTaskExtService
.getByProcessDefinitionIdAndTaskId(t.getProcessDefinitionId(), userTask.getId()); .getByProcessDefinitionIdAndTaskId(t.getProcessDefinitionId(), userTask.getId());
List<String> candidateUsernames; List<String> candidateUsernames = flowApiService.getCandidateUsernamesByExecution(flowTaskExt, t.getExecutionId());
if (StrUtil.isBlank(flowTaskExt.getCandidateUsernames())) {
candidateUsernames = Collections.emptyList();
} else if (!StrUtil.equals(flowTaskExt.getCandidateUsernames(), "${" + FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR + "}")) {
candidateUsernames = StrUtil.split(flowTaskExt.getCandidateUsernames(), ",");
} else {
String value = flowApiService
.getExecutionVariableStringWithSafe(t.getExecutionId(), FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR);
candidateUsernames = value == null ? null : StrUtil.split(value, ",");
}
if (candidateUsernames != null && candidateUsernames.size() == 1) { if (candidateUsernames != null && candidateUsernames.size() == 1) {
assignedUser = candidateUsernames.get(0); assignedUser = candidateUsernames.get(0);
} }

8
common/common-flow/src/main/java/apelet/common/flow/listener/MultiInstanceAssigneeListener.java

@ -2,6 +2,7 @@ package apelet.common.flow.listener;
import apelet.common.core.util.ApplicationContextHolder; import apelet.common.core.util.ApplicationContextHolder;
import apelet.common.flow.model.FlowTaskExt; import apelet.common.flow.model.FlowTaskExt;
import apelet.common.flow.service.FlowApiService;
import apelet.common.flow.service.FlowTaskExtService; import apelet.common.flow.service.FlowTaskExtService;
import apelet.common.flow.util.BaseFlowIdentityExtHelper; import apelet.common.flow.util.BaseFlowIdentityExtHelper;
import apelet.common.flow.util.FlowCustomExtFactory; import apelet.common.flow.util.FlowCustomExtFactory;
@ -30,6 +31,8 @@ public class MultiInstanceAssigneeListener implements ExecutionListener {
private final transient FlowCustomExtFactory flowCustomExtFactory = ApplicationContextHolder.getBean(FlowCustomExtFactory.class); private final transient FlowCustomExtFactory flowCustomExtFactory = ApplicationContextHolder.getBean(FlowCustomExtFactory.class);
private final transient FlowApiService flowApiService = ApplicationContextHolder.getBean(FlowApiService.class);
@Override @Override
public void notify(DelegateExecution execution) { public void notify(DelegateExecution execution) {
@ -44,7 +47,10 @@ public class MultiInstanceAssigneeListener implements ExecutionListener {
} }
List<String> candidateUsernames = new ArrayList<>(); List<String> candidateUsernames = new ArrayList<>();
if (StrUtil.isNotBlank(flowTaskExt.getCandidateUsernames())) { if (StrUtil.isNotBlank(flowTaskExt.getCandidateUsernames())) {
candidateUsernames.addAll(StrUtil.split(flowTaskExt.getCandidateUsernames(), ",")); List<String> configuredUsernames = flowApiService.getCandidateUsernamesByExecution(flowTaskExt, execution.getId());
if (CollUtil.isNotEmpty(configuredUsernames)) {
candidateUsernames.addAll(configuredUsernames);
}
} else { } else {
BaseFlowIdentityExtHelper extHelper = flowCustomExtFactory.getFlowIdentityExtHelper(); BaseFlowIdentityExtHelper extHelper = flowCustomExtFactory.getFlowIdentityExtHelper();
if (extHelper == null) { if (extHelper == null) {

10
common/common-flow/src/main/java/apelet/common/flow/service/FlowApiService.java

@ -570,6 +570,16 @@ public interface FlowApiService {
List<String> getCandidateUsernames(FlowTaskExt flowTaskExt, String taskId); List<String> getCandidateUsernames(FlowTaskExt flowTaskExt, String taskId);
/** /**
* 根据执行实例Id,获取当前任务在流程图中配置候选用户组数据。
* 配置值支持逗号分隔的登录名,以及${appointedAssignee}、${projectBean.getCommonReportManager(execution)}形式的EL表达式。
*
* @param flowTaskExt 流程任务扩展对象。
* @param executionId 执行实例Id。
* @return 候选用户组数据。
*/
List<String> getCandidateUsernamesByExecution(FlowTaskExt flowTaskExt, String executionId);
/**
* 获取当前任务在流程图中配置到的部门岗位Id集合和岗位Id集合。 * 获取当前任务在流程图中配置到的部门岗位Id集合和岗位Id集合。
* *
* @param flowTaskExt 流程任务扩展对象。 * @param flowTaskExt 流程任务扩展对象。

78
common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowApiServiceImpl.java

@ -41,6 +41,7 @@ import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.*; import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.delegate.Expression;
import org.flowable.common.engine.impl.de.odysseus.el.ExpressionFactoryImpl; import org.flowable.common.engine.impl.de.odysseus.el.ExpressionFactoryImpl;
import org.flowable.common.engine.impl.de.odysseus.el.util.SimpleContext; import org.flowable.common.engine.impl.de.odysseus.el.util.SimpleContext;
import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.common.engine.impl.identity.Authentication;
@ -52,7 +53,10 @@ import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ChangeActivityStateBuilder; import org.flowable.engine.runtime.ChangeActivityStateBuilder;
import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.Execution;
@ -1526,14 +1530,78 @@ public class FlowApiServiceImpl implements FlowApiService {
@Override @Override
public List<String> getCandidateUsernames(FlowTaskExt flowTaskExt, String taskId) { public List<String> getCandidateUsernames(FlowTaskExt flowTaskExt, String taskId) {
if (StrUtil.isBlank(flowTaskExt.getCandidateUsernames())) { String configured = flowTaskExt.getCandidateUsernames();
if (StrUtil.isBlank(configured)) {
return Collections.emptyList(); return Collections.emptyList();
} }
if (!StrUtil.equals(flowTaskExt.getCandidateUsernames(), "${" + FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR + "}")) { if (StrUtil.equals(configured, "${" + FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR + "}")) {
return StrUtil.split(flowTaskExt.getCandidateUsernames(), ","); String value = getTaskVariableStringWithSafe(taskId, FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR);
return value == null ? null : StrUtil.splitTrim(value, ",");
} }
Object candidateUsernames = getTaskVariableStringWithSafe(taskId, FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR); if (!configured.startsWith("${")) {
return candidateUsernames == null ? null : StrUtil.split(candidateUsernames.toString(), ","); return StrUtil.splitTrim(configured, ",");
}
Task task = this.getTaskById(taskId);
return task == null ? Collections.emptyList() : this.getCandidateUsernamesByExecution(flowTaskExt, task.getExecutionId());
}
@Override
public List<String> getCandidateUsernamesByExecution(FlowTaskExt flowTaskExt, String executionId) {
String configured = flowTaskExt.getCandidateUsernames();
if (StrUtil.isBlank(configured)) {
return Collections.emptyList();
}
if (StrUtil.equals(configured, "${" + FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR + "}")) {
String value = getExecutionVariableStringWithSafe(executionId, FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR);
return value == null ? null : StrUtil.splitTrim(value, ",");
}
if (!configured.startsWith("${")) {
return StrUtil.splitTrim(configured, ",");
}
// 形如${projectBean.getCommonReportManager(execution)}的EL表达式,交给引擎的表达式管理器求值。
return this.parseCandidateUsernames(this.getExpressionValue(configured, executionId));
}
private List<String> parseCandidateUsernames(Object value) {
if (value == null) {
return Collections.emptyList();
}
if (value instanceof Collection) {
List<String> candidateUsernames = new LinkedList<>();
for (Object element : (Collection<?>) value) {
if (element != null && StrUtil.isNotBlank(element.toString())) {
candidateUsernames.add(element.toString());
}
}
return candidateUsernames;
}
if (StrUtil.isBlank(value.toString())) {
return Collections.emptyList();
}
return StrUtil.splitTrim(value.toString(), ",");
}
private Object getExpressionValue(String expressionStr, String executionId) {
ProcessEngineConfigurationImpl engineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
if (engineConfiguration != null) {
return this.evaluateExpression(engineConfiguration, expressionStr, executionId);
}
// 待办详情等HTTP接口场景没有活动的命令上下文,交由引擎开启一次后再求值,保证execution关键字可正常绑定。
return managementService.executeCommand(commandContext -> this.evaluateExpression(
CommandContextUtil.getProcessEngineConfiguration(commandContext), expressionStr, executionId));
}
private Object evaluateExpression(
ProcessEngineConfigurationImpl engineConfiguration, String expressionStr, String executionId) {
ExecutionEntity execution = CommandContextUtil.getExecutionEntityManager().findById(executionId);
if (execution == null) {
log.error("Failed to getExpressionValue expression [{}], executionId [{}], execution not found.",
expressionStr, executionId);
return null;
}
// 与引擎UserTaskActivityBehavior.handleAssignments的求值方式保持一致,execution关键字可正常绑定到当前执行流对象。
Expression expression = engineConfiguration.getExpressionManager().createExpression(expressionStr);
return expression.getValue(execution);
} }
@Override @Override

12
common/common-flow/src/main/java/apelet/common/flow/service/impl/FlowTaskExtServiceImpl.java

@ -219,17 +219,7 @@ public class FlowTaskExtServiceImpl extends BaseService<FlowTaskExt, String> imp
default: default:
break; break;
} }
List<String> candidateUsernames; List<String> candidateUsernames = flowApiService.getCandidateUsernamesByExecution(flowTaskExt, executionId);
if (StrUtil.isBlank(flowTaskExt.getCandidateUsernames())) {
candidateUsernames = Collections.emptyList();
} else {
if (!StrUtil.equals(flowTaskExt.getCandidateUsernames(), " + FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR + ")) {
candidateUsernames = StrUtil.split(flowTaskExt.getCandidateUsernames(), ",");
} else {
Object v = flowApiService.getExecutionVariableStringWithSafe(executionId, FlowConstant.TASK_APPOINTED_ASSIGNEE_VAR);
candidateUsernames = v == null ? null : StrUtil.split(v.toString(), ",");
}
}
if (CollUtil.isNotEmpty(candidateUsernames)) { if (CollUtil.isNotEmpty(candidateUsernames)) {
usernameSet.addAll(candidateUsernames); usernameSet.addAll(candidateUsernames);
} }

Loading…
Cancel
Save