add - 新增处理运行时,解析各种节点的配置,关于 BPMN 中的原数据解析,统一在 BpmnMetaParserHelper 类型中

This commit is contained in:
wangli 2023-11-15 23:46:07 +08:00
parent 50c1bd5fc6
commit 638624ae6c
7 changed files with 178 additions and 72 deletions

View File

@ -37,7 +37,7 @@ public interface BpmnConstants {
String COUNTERSIGN_ORIGIN_ASSIGNER = "COUNTERSIGN_ORIGIN_ASSIGNER";
String PROCESS_PREFIX = "Flowable";
String FLOW_NODE_JSON = "jsonMetaValue";
String FLOW_NODE_JSON = "jsonValue";
String FLOW_SERVER_VERSION = "serverVersion";
String FLOW_SERVER_VERSION_121 = "1.2.1";
String CONFIG_NOTICE = "noticeConfig";
@ -55,7 +55,7 @@ public interface BpmnConstants {
String CONFIG_FIELD_META = "field";
String CONFIG_FIELD_PERMISSION = "fieldPermission";
String CONFIG_FIELD_OPTION = "option";
String CONFIG_NODE_TYPE = "nodeType";
String CONFIG_BUTTON_TYPE_INITIATOR = "initiator";
String CONFIG_BUTTON_TYPE_CURRENT = "current";
String CONFIG_BUTTON_TYPE_HISTORY = "history";

View File

@ -1,6 +1,9 @@
package cn.axzo.workflow.common.enums;
import java.util.Arrays;
import java.util.Objects;
public enum BpmnFlowNodeType {
//0 发起人 1审批 2抄送 3条件 4路由
@ -44,4 +47,10 @@ public enum BpmnFlowNodeType {
this.desc = desc;
}
public static BpmnFlowNodeType valueOfType(String type) {
return Arrays.stream(BpmnFlowNodeType.values())
.filter(i -> Objects.equals(i.getType(), type))
.findAny()
.orElse(null);
}
}

View File

@ -91,7 +91,10 @@ import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.CONVERTOR_COMMON_
* @author wangli
* @since 2023/10/12 11:43
*/
public class BpmnJsonConverterUtil {
public final class BpmnJsonConverterUtil {
private BpmnJsonConverterUtil() {
}
private static final Logger log = LoggerFactory.getLogger(BpmnJsonConverterUtil.class);
private static Map<Class<? extends BaseElement>, AbstractBpmnJsonConverter<? extends BaseElement>> converters =
new HashMap<>();

View File

@ -0,0 +1,54 @@
package cn.axzo.workflow.core.common.utils;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVAL_METHOD;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_NODE_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_VALUE;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION;
/**
* 协助解析 BPMN 文件中的自定义扩展字段和属性
*
* @author wangli
* @since 2023/11/15 22:51
*/
public final class BpmnMetaParserHelper {
private BpmnMetaParserHelper() {}
public static Optional<String> getProcessServerVersion(Process process) {
return Optional.ofNullable(process.getAttributeValue(null, FLOW_SERVER_VERSION));
}
public static Optional<BpmnFlowNodeType> getNodeType(FlowElement flowElement) {
return defaultValid(flowElement, CONFIG_NODE_TYPE)
.map(element -> BpmnFlowNodeType.valueOfType(element.getElementText()));
}
public static Optional<String> getApprovalMethod(UserTask userTask) {
return defaultValid(userTask, CONFIG_APPROVAL_METHOD)
.map(element -> element.getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE));
}
private static Optional<ExtensionElement> defaultValid(FlowElement flowElement, String elementName) {
if (CollectionUtils.isEmpty(flowElement.getExtensionElements())
|| !flowElement.getExtensionElements().containsKey(elementName)) {
return Optional.empty();
}
List<ExtensionElement> elements = flowElement.getExtensionElements().getOrDefault(elementName,
Collections.emptyList());
if (CollectionUtils.isEmpty(elements)) {
return Optional.empty();
}
return Optional.ofNullable(elements.get(0));
}
}

View File

@ -3,15 +3,16 @@ package cn.axzo.workflow.core.converter.json;
import cn.axzo.framework.jackson.utility.JSON;
import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonNode;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import org.flowable.bpmn.model.ExtensionAttribute;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.SequenceFlow;
import static cn.axzo.workflow.common.constant.BpmnConstants.ELEMENT_ATTRIBUTE_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_NODE_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_NODE_JSON;
/**
* TODO
* 抽象的 JSON BPMN 协议的转换器
*
* @author wangli
* @since 2023/10/12 11:06
@ -22,17 +23,20 @@ public abstract class AbstractBpmnJsonConverter<T extends FlowElement> {
throw new WorkflowEngineException("暂不支持 BPMN 转 JSON");
}
public final void addTypeAttribute(T flowElement, BpmnJsonNode node) {
ExtensionAttribute typeAttribute = new ExtensionAttribute();
typeAttribute.setName(ELEMENT_ATTRIBUTE_TYPE);
typeAttribute.setValue(node.getType().getType());
flowElement.addAttribute(typeAttribute);
public final void addTypeAttribute(T flowElement, BpmnJsonNode jsonNode) {
ExtensionElement nodeTypeElement = new ExtensionElement();
nodeTypeElement.setName(CONFIG_NODE_TYPE);
nodeTypeElement.setElementText(jsonNode.getType().getType());
flowElement.addExtensionElement(nodeTypeElement);
}
public final void addJsonValueAttribute(T flowElement, BpmnJsonNode jsonNode) {
ExtensionAttribute extensionAttribute = new ExtensionAttribute();
extensionAttribute.setName(FLOW_NODE_JSON);
extensionAttribute.setValue(JSON.toJSONString(jsonNode));
flowElement.addAttribute(extensionAttribute);
if (flowElement instanceof SequenceFlow) {
return;
}
ExtensionElement jsonValueElement = new ExtensionElement();
jsonValueElement.setName(FLOW_NODE_JSON);
jsonValueElement.setElementText(JSON.toJSONString(jsonNode));
flowElement.addExtensionElement(jsonValueElement);
}
}

View File

@ -1,12 +1,12 @@
package cn.axzo.workflow.core.engine.listener;
import cn.axzo.workflow.common.constant.BpmnConstants;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.deletage.BpmnTaskCalculateDTO;
import cn.axzo.workflow.core.deletage.BpmnTaskDelegate;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.ExtensionElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.RepositoryService;
@ -20,15 +20,17 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_ALLOW_SKIP_USER_TASK;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVAL_METHOD;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_121;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprovalMethod;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNodeType;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getProcessServerVersion;
/**
@ -57,52 +59,82 @@ public class EngineExecutionStartListener implements ExecutionListener {
Process mainProcess = repositoryService.getBpmnModel(execution.getProcessDefinitionId()).getMainProcess();
UserTask userTask = (UserTask) mainProcess.getFlowElement(currentActivityId);
String flowServerVersion = mainProcess.getAttributeValue(null, FLOW_SERVER_VERSION);
if (Objects.equals(FLOW_SERVER_VERSION_121, flowServerVersion)) {
Map<String, List<ExtensionElement>> extensionElements = userTask.getExtensionElements();
extensionElements.get(CONFIG_APPROVAL_METHOD);
} else {
// 枢智版本的逻辑
BpmnTaskCalculateDTO calculateDTO = new BpmnTaskCalculateDTO();
calculateDTO.setTaskId(userTask.getId());
calculateDTO.setCategory(userTask.getCategory());
calculateDTO.setProcessDefinitionId(execution.getProcessDefinitionId());
calculateDTO.setProcessInstanceId(execution.getProcessInstanceId());
calculateDTO.setTaskName(userTask.getName());
calculateDTO.setTenantId(execution.getTenantId());
calculateDTO.setTaskDefinitionKey(currentActivityId);
calculateDTO.setVariables(execution.getVariables());
bpmTaskDelegate.ifAvailable(delegate -> {
List<BpmnTaskDelegateAssigner> assigners = delegate.calculateAssignerAtExecution(calculateDTO);
List<String> assigneeIdList = new ArrayList<>();
if (!CollectionUtils.isEmpty(assigners)) {
for (BpmnTaskDelegateAssigner user : assigners) {
assigneeIdList.add(user.buildAssigneeId());
// version=1.2.1-SNAPSHOT 开始才给 process 节点增加了 serverVersion 属性
getProcessServerVersion(mainProcess).ifPresent(version -> {
Optional<BpmnFlowNodeType> nodeType = getNodeType(userTask);
if (Objects.equals(FLOW_SERVER_VERSION_121, version)
&& nodeType.isPresent()
&& Objects.equals(NODE_TASK, nodeType.get())) {
getApprovalMethod(userTask).ifPresent(method -> {
switch (method) {
case "autoPassed":
case "autoRejection":
// nothing to do
// TODO 不要调用查询审批人的接口 只需要保证该节点在实际运行过程中能自动通过和拒绝即可
// 通过的功能可以参考下面枢智的方法拒绝还需研究可以结合 task 相关的事件去处理
break;
default:
// 这里只会是 human 这一种情况 因为 nobody 在转 BPMN 协议时Activity 直接变成了 ReceiveTask 节点了
// TODO 结合不同配置开始执行查询审批人的逻辑
break;
}
// 审批人为空并且当前节点设置了自动跳过条件
} else if (StringUtils.hasLength(userTask.getSkipExpression())) {
// 自动通过的默认引擎参数必须设置为 true
execution.setTransientVariable(BpmnConstants.FLOWABLE_SKIP_EXPRESSION_ENABLE, true);
// 设置当前 UserTask 使用的 skip 表达式
execution.setTransientVariable(BPM_ALLOW_SKIP_USER_TASK, true);
}
});
// UserTask 非多实例
if (!userTask.hasMultiInstanceLoopCharacteristics()) {
if (!CollectionUtils.isEmpty(assigneeIdList)) {
userTask.setAssignee(assigneeIdList.get(0));
}
} else {
// UserTask 多实例, 该变量用于引擎
execution.setVariable(assigneeListVariableName, assigneeIdList);
}
});
defaultCalcTaskAssigner(execution, userTask, currentActivityId, assigneeListVariableName);
}
/**
* 枢智版本的逻辑, 不太兼容了暂时先放在这里
*
* @param execution
* @param userTask
* @param currentActivityId
* @param assigneeListVariableName
*/
@Deprecated
private void defaultCalcTaskAssigner(DelegateExecution execution, UserTask userTask, String currentActivityId,
String assigneeListVariableName) {
BpmnTaskCalculateDTO calculateDTO = new BpmnTaskCalculateDTO();
calculateDTO.setTaskId(userTask.getId());
calculateDTO.setCategory(userTask.getCategory());
calculateDTO.setProcessDefinitionId(execution.getProcessDefinitionId());
calculateDTO.setProcessInstanceId(execution.getProcessInstanceId());
calculateDTO.setTaskName(userTask.getName());
calculateDTO.setTenantId(execution.getTenantId());
calculateDTO.setTaskDefinitionKey(currentActivityId);
calculateDTO.setVariables(execution.getVariables());
bpmTaskDelegate.ifAvailable(delegate -> {
List<BpmnTaskDelegateAssigner> assigners = delegate.calculateAssignerAtExecution(calculateDTO);
List<String> assigneeIdList = new ArrayList<>();
if (!CollectionUtils.isEmpty(assigners)) {
for (BpmnTaskDelegateAssigner user : assigners) {
assigneeIdList.add(user.buildAssigneeId());
}
// 将当次审批节点下计算出来的人存储起来,方便后续对 task 保持审批人快照
execution.setVariableLocal(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
assigners);
});
}
// 审批人为空并且当前节点设置了自动跳过条件
} else if (StringUtils.hasLength(userTask.getSkipExpression())) {
// 自动通过的默认引擎参数必须设置为 true
execution.setTransientVariable(BpmnConstants.FLOWABLE_SKIP_EXPRESSION_ENABLE, true);
// 设置当前 UserTask 使用的 skip 表达式
execution.setTransientVariable(BPM_ALLOW_SKIP_USER_TASK, true);
}
// UserTask 非多实例
if (!userTask.hasMultiInstanceLoopCharacteristics()) {
if (!CollectionUtils.isEmpty(assigneeIdList)) {
userTask.setAssignee(assigneeIdList.get(0));
}
} else {
// UserTask 多实例, 该变量用于引擎
execution.setVariable(assigneeListVariableName, assigneeIdList);
}
// 将当次审批节点下计算出来的人存储起来,方便后续对 task 保持审批人快照
execution.setVariableLocal(INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
assigners);
});
}
}

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.server.controller.listener.task;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.listener.BpmnTaskEventListener;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -12,7 +13,6 @@ import org.flowable.engine.TaskService;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Objects;
@ -45,13 +45,17 @@ public class StartNodeAutoCompleteEventListener implements BpmnTaskEventListener
Process mainProcess = repositoryService.getBpmnModel(delegateTask.getProcessDefinitionId()).getMainProcess();
UserTask userTask = (UserTask) mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());
if (Objects.equals(NODE_STARTER.getType(), delegateTask.getTaskDefinitionKey())
&& !StringUtils.hasLength(delegateTask.getAssignee())) {
BpmnTaskDelegateAssigner initiator = delegateTask.getVariable(INTERNAL_INITIATOR,
BpmnTaskDelegateAssigner.class);
delegateTask.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + delegateTask.getId(), initiator);
// 直接完成
taskService.complete(delegateTask.getId(), runtimeService.getVariables(delegateTask.getExecutionId()));
}
// 这一个通用的发起人节点完全不需要审批
// 为什么要创建这个节点只是为了在审批记录中有一个发起人的记录信息而已
BpmnMetaParserHelper.getNodeType(userTask).ifPresent(nodeType -> {
if (Objects.equals(NODE_STARTER, nodeType)) {
BpmnTaskDelegateAssigner initiator = delegateTask.getVariable(INTERNAL_INITIATOR,
BpmnTaskDelegateAssigner.class);
delegateTask.setVariable(INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + delegateTask.getId(),
initiator);
// 直接完成
taskService.complete(delegateTask.getId(), runtimeService.getVariables(delegateTask.getExecutionId()));
}
});
}
}