REQ-2596,自动过审支持不同流程数据版本执行不同业务代码

This commit is contained in:
yangqicheng 2024-07-02 16:04:06 +08:00
parent 21c4541a70
commit 872b84f1ae
7 changed files with 220 additions and 80 deletions

View File

@ -0,0 +1,28 @@
package cn.axzo.workflow.core.common.utils;
import org.springframework.lang.Nullable;
import java.util.Map;
public class SpringContentUtils {
private static SpringContentUtils.SpringContext springContext;
public SpringContentUtils(SpringContentUtils.SpringContext springContext) {
SpringContentUtils.springContext = springContext;
}
public static <T> T getBean(Class<T> clazz) {
return springContext.getBean(clazz);
}
public static <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) {
return springContext.getBeansOfType(type);
}
public interface SpringContext {
<T> T getBean(Class<T> var1);
<T> Map<String, T> getBeansOfType(@Nullable Class<T> type);
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.core.conf;
import cn.axzo.workflow.core.common.utils.SpringContentUtils;
import cn.axzo.workflow.core.engine.behavior.CustomActivityBehaviorFactory;
import cn.axzo.workflow.core.engine.cmd.CustomCommandContextFactory;
import cn.axzo.workflow.core.engine.id.BasedNacosSnowflakeIdGenerator;
@ -20,19 +21,26 @@ import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.google.common.collect.Lists;
import org.apache.ibatis.session.SqlSessionFactory;
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
import org.flowable.common.engine.impl.history.HistoryLevel;
import org.flowable.form.spring.SpringFormEngineConfiguration;
import org.flowable.job.service.JobProcessor;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import static org.flowable.common.engine.impl.AbstractEngineConfiguration.DB_SCHEMA_UPDATE_TRUE;
@ -105,4 +113,25 @@ public class FlowableConfiguration {
return new CustomActivityBehaviorFactory();
}
@Configuration
@ConditionalOnBean({SqlSessionFactory.class})
public static class SpringContext implements SpringContentUtils.SpringContext, ApplicationContextAware {
private ApplicationContext applicationContext;
public SpringContext() {
}
public <T> T getBean(Class<T> clazz) {
return this.applicationContext.getBean(clazz);
}
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type){
return this.applicationContext.getBeansOfType(type);
}
public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
}

View File

@ -0,0 +1,33 @@
package cn.axzo.workflow.core.version;
import cn.axzo.workflow.core.common.utils.SpringContentUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class GetSpecifiedVersionBeanUtils {
public static <T extends Versioned> T getSpecialVersionBean(Class<T> clazz, String version) {
Map<String, T> beans = SpringContentUtils.getBeansOfType(clazz);
if (CollectionUtils.isEmpty(beans)) {
throw new NullPointerException("no beans of type " + clazz.getName());
}
if (StringUtils.isEmpty(version)) {
//todo 版本为空需要一个默认处理类
}
//根据版本号排序
List<T> sortedList = beans.values().stream().sorted().collect(Collectors.toList());
DefaultArtifactVersion targetVersion = new DefaultArtifactVersion(version);
for (int i = sortedList.size() - 1; i >= 0; i--) {
int flag = sortedList.get(i).getVersion().compareTo(targetVersion);
if (flag <= 0) {
return sortedList.get(i);
}
}
throw new NullPointerException("no beans of type " + clazz.getName() + " and version " + version);
}
}

View File

@ -0,0 +1,8 @@
package cn.axzo.workflow.core.version;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
public interface Versioned {
DefaultArtifactVersion getVersion();
}

View File

@ -1,9 +1,7 @@
package cn.axzo.workflow.server.controller.listener.task;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO;
import cn.axzo.workflow.core.common.context.TaskOperationContext;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.cmd.CustomApproveTaskCmd;
@ -11,22 +9,20 @@ import cn.axzo.workflow.core.engine.job.AsyncApproveTaskJobHandler;
import cn.axzo.workflow.core.engine.operation.DeleteProcessInstanceOperation;
import cn.axzo.workflow.core.listener.AbstractBpmnEventListener;
import cn.axzo.workflow.core.listener.BpmnTaskEventListener;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.core.version.GetSpecifiedVersionBeanUtils;
import cn.axzo.workflow.server.controller.listener.task.service.CheckApproveService;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.common.engine.impl.interceptor.CommandExecutor;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.job.service.JobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
@ -37,12 +33,10 @@ import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
@ -54,10 +48,8 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_REJECT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED;
import static cn.axzo.workflow.core.common.enums.BpmnProcessTaskResultEnum.REJECTION_AUTO_COMPLETED;
@ -80,7 +72,6 @@ public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener<Ta
private final TaskService taskService;
private final RuntimeService runtimeService;
private final RepositoryService repositoryService;
private final HistoryService historyService;
private final ExtAxHiTaskInstService extAxHiTaskInstService;
@Override
@ -96,9 +87,12 @@ public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener<Ta
UserTask userTask = (UserTask) mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());
Optional<Boolean> autoApprovalOpt = BpmnMetaParserHelper.getAutoApprovalValue(userTask);
//自动过审配置为true才执行这段代码默认为false老数据默认不跳过审批
//自动过审配置为true才执行这段代码默认为false老数据默认不自动过审
if (autoApprovalOpt.orElse(false)) {
boolean exists = checkApproverExists(delegateTask, userTask, mainProcess);
Object versionVar = delegateTask.getVariable(WORKFLOW_ENGINE_VERSION);
String version = versionVar == null ? null : String.valueOf(versionVar);
CheckApproveService checkApproveService = GetSpecifiedVersionBeanUtils.getSpecialVersionBean(CheckApproveService.class, version);
boolean exists = checkApproveService.checkApproverExists(delegateTask, userTask, mainProcess, getContext());
log.info("是否需要自动过程判断 exists:{}", exists);
if (exists) {
taskService.addComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), COMMENT_TYPE_ADVICE,
@ -144,72 +138,6 @@ public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener<Ta
jobService.scheduleAsyncJob(job);
}
/**
* 校验当前的审批人是否存在过前一个节点
*
* @param delegateTask
* @param userTask
* @param mainProcess
*/
private boolean checkApproverExists(DelegateTask delegateTask, UserTask userTask, Process mainProcess) {
AtomicBoolean exists = new AtomicBoolean(false);
FlowElement currentFlowElement = mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());
BpmnFlowNodeType currentNodeType = BpmnMetaParserHelper.getNodeType(currentFlowElement).orElse(NODE_EMPTY);
if (!Objects.equals(currentNodeType, NODE_TASK)) {
return exists.get();
}
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
processEngineConfiguration.getActivityInstanceEntityManager()
.findActivityInstancesByProcessInstanceId(delegateTask.getProcessInstanceId(), false)
.stream()
.filter(i -> !Objects.equals(i.getActivityId(), userTask.getId()))
.filter(i -> !Objects.equals(i.getActivityType(), "exclusiveGateway"))
.filter(i -> !Objects.equals(i.getActivityType(), "sequenceFlow"))
.max(Comparator.comparing(ActivityInstanceEntity::getStartTime))
.ifPresent(i -> {
// 与发起人比对
if (Objects.equals(NODE_STARTER.getType(), i.getActivityId())) {
BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(delegateTask.getVariable(INTERNAL_INITIATOR));
if (Objects.nonNull(initiator) && initiator.comparePersonIdToOther(delegateTask.getAssignee())) {
exists.compareAndSet(false, true);
}
} else {
FlowElement flowElement = mainProcess.getFlowElement(i.getActivityId());
BpmnMetaParserHelper.getNodeType(flowElement).ifPresent(j -> {
if (Objects.equals(NODE_TASK, j)) {
ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
searchDTO.setProcessInstanceId(delegateTask.getProcessInstanceId());
searchDTO.setTaskDefinitionKey(i.getActivityId());
getContext().getExtAxHiTaskInsts(() -> extAxHiTaskInstService.queryList(searchDTO))
.stream().filter(e -> Objects.equals(e.getStatus(), APPROVED.getStatus()))
.map(ExtAxHiTaskInst::getAssignee)
.filter(Objects::nonNull)
.filter(StringUtils::hasText)
.filter(k -> specialApproverComparison(k, delegateTask.getAssignee()))
.findAny().ifPresent(k -> exists.compareAndSet(false, true));
}
});
}
});
return exists.get();
}
/**
* 特殊审批人字段的比对, 兼容旧迭代导致的数据格式
*
* @return
*/
private boolean specialApproverComparison(String compareAssignee, String currentAssignee) {
if (StringUtils.hasText(compareAssignee) && StringUtils.hasText(currentAssignee)) {
String[] compareSplit = compareAssignee.split("\\|");
String[] currentSplit = currentAssignee.split("\\|");
if (compareSplit.length == 2 || currentSplit.length == 2) {
return Objects.equals(compareSplit[1], currentSplit[1]);
}
}
return false;
}
/**
* 如果审批人为空时, 读取 approverEmptyHandleType = 自动通过或自动驳回
*

View File

@ -0,0 +1,11 @@
package cn.axzo.workflow.server.controller.listener.task.service;
import cn.axzo.workflow.core.common.context.TaskOperationContext;
import cn.axzo.workflow.core.version.Versioned;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.task.service.delegate.DelegateTask;
public interface CheckApproveService extends Versioned {
boolean checkApproverExists(DelegateTask delegateTask, UserTask userTask, Process mainProcess, TaskOperationContext taskOperationContext);
}

View File

@ -0,0 +1,103 @@
package cn.axzo.workflow.server.controller.listener.task.service.impl;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO;
import cn.axzo.workflow.core.common.context.TaskOperationContext;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.repository.entity.ExtAxHiTaskInst;
import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.axzo.workflow.server.controller.listener.task.service.CheckApproveService;
import lombok.AllArgsConstructor;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.persistence.entity.ActivityInstanceEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.service.delegate.DelegateTask;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import static cn.axzo.workflow.common.constant.BpmnConstants.FLOW_SERVER_VERSION_130;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
@Component
@AllArgsConstructor
public class CheckApproverServiceImpl implements CheckApproveService {
private final ExtAxHiTaskInstService extAxHiTaskInstService;
public boolean checkApproverExists(DelegateTask delegateTask, UserTask userTask, Process mainProcess, TaskOperationContext taskOperationContext) {
AtomicBoolean exists = new AtomicBoolean(false);
FlowElement currentFlowElement = mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());
BpmnFlowNodeType currentNodeType = BpmnMetaParserHelper.getNodeType(currentFlowElement).orElse(NODE_EMPTY);
if (!Objects.equals(currentNodeType, NODE_TASK)) {
return exists.get();
}
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
processEngineConfiguration.getActivityInstanceEntityManager()
.findActivityInstancesByProcessInstanceId(delegateTask.getProcessInstanceId(), false)
.stream()
.filter(i -> !Objects.equals(i.getActivityId(), userTask.getId()))
.filter(i -> !Objects.equals(i.getActivityType(), "exclusiveGateway"))
.filter(i -> !Objects.equals(i.getActivityType(), "sequenceFlow"))
.max(Comparator.comparing(ActivityInstanceEntity::getStartTime))
.ifPresent(i -> {
// 与发起人比对
if (Objects.equals(NODE_STARTER.getType(), i.getActivityId())) {
BpmnTaskDelegateAssigner initiator = BpmnTaskDelegateAssigner.toObjectCompatible(delegateTask.getVariable(INTERNAL_INITIATOR));
if (Objects.nonNull(initiator) && initiator.comparePersonIdToOther(delegateTask.getAssignee())) {
exists.compareAndSet(false, true);
}
} else {
FlowElement flowElement = mainProcess.getFlowElement(i.getActivityId());
BpmnMetaParserHelper.getNodeType(flowElement).ifPresent(j -> {
if (Objects.equals(NODE_TASK, j)) {
ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
searchDTO.setProcessInstanceId(delegateTask.getProcessInstanceId());
searchDTO.setTaskDefinitionKey(i.getActivityId());
taskOperationContext.getExtAxHiTaskInsts(() -> extAxHiTaskInstService.queryList(searchDTO))
.stream().filter(e -> Objects.equals(e.getStatus(), APPROVED.getStatus()))
.map(ExtAxHiTaskInst::getAssignee)
.filter(Objects::nonNull)
.filter(StringUtils::hasText)
.filter(k -> specialApproverComparison(k, delegateTask.getAssignee()))
.findAny().ifPresent(k -> exists.compareAndSet(false, true));
}
});
}
});
return exists.get();
}
/**
* 特殊审批人字段的比对, 兼容旧迭代导致的数据格式
*
* @return
*/
private boolean specialApproverComparison(String compareAssignee, String currentAssignee) {
if (StringUtils.hasText(compareAssignee) && StringUtils.hasText(currentAssignee)) {
String[] compareSplit = compareAssignee.split("\\|");
String[] currentSplit = currentAssignee.split("\\|");
if (compareSplit.length == 2 || currentSplit.length == 2) {
return Objects.equals(compareSplit[1], currentSplit[1]);
}
}
return false;
}
@Override
public DefaultArtifactVersion getVersion() {
return new DefaultArtifactVersion(FLOW_SERVER_VERSION_130);
}
}