Merge remote-tracking branch 'refs/remotes/origin/feature/REQ-2596' into feature/merge-all

# Conflicts:
#	pom.xml
#	workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java
#	workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java
#	workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java
#	workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProperty.java
#	workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java
This commit is contained in:
yangqicheng 2024-07-29 10:25:18 +08:00
commit 00c7fa6812
48 changed files with 656 additions and 249 deletions

View File

@ -16,7 +16,7 @@
<name>workflow-engine</name>
<properties>
<revision>1.4.0-SNAPSHOT</revision>
<revision>1.4.1-SNAPSHOT</revision>
<axzo-bom.version>2.0.0-SNAPSHOT</axzo-bom.version>
<axzo-dependencies.version>2.0.0-SNAPSHOT</axzo-dependencies.version>
<feign-httpclient.version>11.8</feign-httpclient.version>

View File

@ -107,6 +107,7 @@ public interface BpmnConstants {
String END_EVENT_ID = "endEventNode";
String BPM_MODEL_CATEGORY = "bpm_model_category";
String BPM_ALLOW_SKIP_USER_TASK = "_INTERNAL_SKIP_USER_TASK_";
String AUTO_APPROVAL_TYPE = "autoApprovalType";
/**
* 用于国内审批节点填写审批建议
* <p>
@ -157,4 +158,24 @@ public interface BpmnConstants {
*/
String MQ_OWNERSHIP_APPLICATION = "MQ_OWNERSHIP_APPLICATION";
String MQ_OWNERSHIP_PROCESS_DEFINITION_KEY = "MQ_OWNERSHIP_PROCESS_DEFINITION_KEY";
/**
* 审批人数量限制
*/
Integer APPROVAL_ASSIGNER_LIMIT_NUMBER = 60;
/**
* 抄送人员数量限制
*/
Integer CARBON_ASSIGNER_LIMIT_NUMBER = 100;
/**
* MQ消息每批次人员数量
*/
Integer MQ_ASSIGNER_BATCH_SIZE = 20;
/**
* 加签显示人员数量
*/
Integer COUNTERSIGN_ASSIGNER_SHOW_NUMBER = 2;
}

View File

@ -0,0 +1,35 @@
package cn.axzo.workflow.common.enums;
import lombok.Getter;
import org.springframework.util.StringUtils;
/**
* 自动过审参数
*/
@Getter
public enum AutoApprovalTypeEnum {
NO_AUTO_APPROVAL("noAutoApproval", "不自动过审"),
CONTINUOUS_NODES_AUTO_APPROVAL("continuousNodesAutoApproval", "连续节点自动过审");
private final String type;
private final String desc;
AutoApprovalTypeEnum(String type, String desc) {
this.type = type;
this.desc = desc;
}
public static AutoApprovalTypeEnum fromType(String type) {
if (!StringUtils.hasText(type)) {
return null;
}
for (AutoApprovalTypeEnum typeEnum : AutoApprovalTypeEnum.values()) {
if (typeEnum.type.equals(type)) {
return typeEnum;
}
}
return null;
}
}

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.common.model.request;
import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ -27,4 +28,10 @@ public class BpmnApproveConf {
@ApiModelProperty(value = "审批同意录入手写签名")
@Valid
private Boolean userAgreeSignature;
/**
* 审批自动过审配置
*/
@ApiModelProperty(value = "同一审批人自动过审类型,默认不自动过审,枚举类型为'NO_AUTO_APPROVAL'")
private AutoApprovalTypeEnum autoApprovalType = AutoApprovalTypeEnum.NO_AUTO_APPROVAL;
}

View File

@ -4,6 +4,7 @@ import cn.axzo.workflow.common.enums.ApprovalMethodEnum;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.enums.ApproverScopeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeMode;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

View File

@ -9,6 +9,7 @@ import lombok.NoArgsConstructor;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.List;
/**
@ -53,6 +54,7 @@ public class BpmnActivitySetAssigneeDTO {
*/
@ApiModelProperty(value = "审批人集合信息", notes = "业务传参时,需要注意去重")
@Valid
@Size(max = 60, message = "指定审批人数量限制为60")
private List<BpmnTaskDelegateAssigner> assigners;
/**

View File

@ -18,8 +18,8 @@ public enum AsyncJobRespCode implements IModuleRespCode {
JOB_NOT_EXISTS_PROC_INST_ID("003", "流程实例id为【{}】对应任务不存在"),
RESUME_JOB_REQUEST_PARAM_ERROR("004", "恢复任务请求参数错误jobId 和 procInstId 不能同时为空"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -30,13 +30,4 @@ public enum AsyncJobRespCode implements IModuleRespCode {
public String getProjectCode() {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -27,8 +27,8 @@ public enum BpmnInstanceRespCode implements IModuleRespCode {
TASK_CANT_COMMENT_INSTANCE_NOT_EXISTS("012", "流程实例【{}】不存在, 不能评论"),
RUNNING_INSTANCE_ONLY_FORECAST("013", "仅运行中的实例可以推测"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -40,12 +40,4 @@ public enum BpmnInstanceRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -20,8 +20,8 @@ public enum BpmnModelRespCode implements IModuleRespCode {
BPMN_BYTES_NOT_EXISTS("005", "模型定义内容字节码不存在"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -33,13 +33,5 @@ public enum BpmnModelRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -24,8 +24,8 @@ public enum BpmnProcessDefinitionRespCode implements IModuleRespCode {
PROCESS_DEFINITION_HAS_DIRTY_DATA("009", "流程定义KEY【{}】存在脏数据,当前模型没有流程定义内容"),
PROCESS_DEFINITION_IS_INVALID("010", "暂时无法发起,请先配置流程模型")
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -37,12 +37,4 @@ public enum BpmnProcessDefinitionRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -4,6 +4,8 @@ import cn.axzo.framework.domain.web.code.IModuleRespCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ASSIGNER_LIMIT_NUMBER;
/**
* 流程模型响应码
*
@ -34,10 +36,11 @@ public enum BpmnTaskRespCode implements IModuleRespCode {
PROCESS_INSTANCE_IS_NOT_EXIST("019", "Execution:{} 对应流程实例不存在,流程状态异常!"),
TASK_TYPE_MISMATCH("020", "节点类型不匹配,当前节点类型:【{}】,指定节点类型:【{}】!"),
PROCESS_CANT_SET_ASSIGNEE("021", "当前审批状态不允许设置审批人"),
ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT("022", String.format("人员数量超过限制,节点审批人限制数量为: %d!", APPROVAL_ASSIGNER_LIMIT_NUMBER)),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -49,13 +52,4 @@ public enum BpmnTaskRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -21,8 +21,8 @@ public enum CategoryRespCode implements IModuleRespCode {
CATEGORY_CONFIG_EXISTS("006", "分类【{}】的【{}】配置中已存在重复数据"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -34,13 +34,5 @@ public enum CategoryRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -23,8 +23,8 @@ public enum ConvertorRespCode implements IModuleRespCode {
CONVERTOR_OPERATION_CHECKBOX_TYPE_ERROR("008", "条件节点(复选)运算符【{}】暂不支持"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -36,13 +36,4 @@ public enum ConvertorRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -22,8 +22,8 @@ public enum FlowableEngineRespCode implements IModuleRespCode {
ENGINE_ASYNC_COMMAND_EXECUTION_RETRY_GIVE_UP("007", "命令重试尝试【{}】次仍然失败,并出现异常, 将放弃"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -35,13 +35,4 @@ public enum FlowableEngineRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -17,8 +17,8 @@ public enum FormDefinitionRespCode implements IModuleRespCode {
FORM_DEFINITION_PARSER_ERROR("002", "表单定义内容解析出错"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -30,13 +30,4 @@ public enum FormDefinitionRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -17,8 +17,8 @@ public enum FormModelRespCode implements IModuleRespCode {
FORM_MODEL_ID_NOT_EXISTS("002", "表单模型ID【{}】不存在"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -30,13 +30,4 @@ public enum FormModelRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -21,8 +21,8 @@ public enum OtherRespCode implements IModuleRespCode {
MESSAGE_PUSH_EVENT_BUILD_ERROR("006", "不能使用 createEvent 函数创建`发送待办`的事件, 请调用 createPendingPushEvent 函数"),
;
private String code;
private String message;
private final String code;
private final String message;
@Override
public String getModuleCode() {
@ -34,12 +34,4 @@ public enum OtherRespCode implements IModuleRespCode {
return "998";
}
public void setCode(String code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -69,6 +69,7 @@ import java.util.UUID;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVE_SUPPORT_BATCH_OPERATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVE_USER_AGREE_SIGNATURE;
import static cn.axzo.workflow.common.constant.BpmnConstants.AUTO_APPROVAL_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_BUTTON_META;
@ -506,6 +507,15 @@ public final class BpmnJsonConverterUtil {
config.addAttribute(configAttribute);
approveConfigElement.addChildElement(config);
}
if (Objects.nonNull(approveConf.getAutoApprovalType())) {
ExtensionElement config = new ExtensionElement();
config.setName(AUTO_APPROVAL_TYPE);
ExtensionAttribute configAttribute = new ExtensionAttribute();
configAttribute.setName(ELEMENT_ATTRIBUTE_VALUE);
configAttribute.setValue(approveConf.getAutoApprovalType().getType());
config.addAttribute(configAttribute);
approveConfigElement.addChildElement(config);
}
}
public static byte[] transformBytes(BpmnModel bpmnModel) {

View File

@ -4,6 +4,7 @@ import cn.axzo.workflow.common.enums.ApprovalMethodEnum;
import cn.axzo.workflow.common.enums.ApproverEmptyHandleTypeEnum;
import cn.axzo.workflow.common.enums.ApproverScopeEnum;
import cn.axzo.workflow.common.enums.ApproverSpecifyEnum;
import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum;
import cn.axzo.workflow.common.enums.CarbonCopyObjectType;
@ -41,6 +42,7 @@ import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVE_SUPPORT_BATCH_OPERATION;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVE_USER_AGREE_SIGNATURE;
import static cn.axzo.workflow.common.constant.BpmnConstants.AUTO_APPROVAL_TYPE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVAL_METHOD;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVE;
import static cn.axzo.workflow.common.constant.BpmnConstants.CONFIG_APPROVER_EMPTY_HANDLE_TYPE;
@ -104,6 +106,7 @@ public final class BpmnMetaParserHelper {
if (CollectionUtils.isEmpty(elements)) {
conf.setUserAgreeSignature(USER_AGREE_SIGNATURE_DEFAULT_VALUE);
conf.setSupportBatchOperation(SUPPORT_BATCH_OPERATION_DEFAULT_VALUE);
conf.setAutoApprovalType(AutoApprovalTypeEnum.NO_AUTO_APPROVAL);
} else {
elements.get(0).getChildElements().forEach((k, v) -> {
if (APPROVE_SUPPORT_BATCH_OPERATION.equals(k)) {
@ -112,6 +115,10 @@ public final class BpmnMetaParserHelper {
} else if (APPROVE_USER_AGREE_SIGNATURE.equals(k)) {
String value = v.get(0).getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE);
conf.setUserAgreeSignature(Boolean.valueOf(value));
} else if (AUTO_APPROVAL_TYPE.equals(k)) {
String value = v.get(0).getAttributeValue(null, ELEMENT_ATTRIBUTE_VALUE);
AutoApprovalTypeEnum typeEnum = AutoApprovalTypeEnum.fromType(value);
conf.setAutoApprovalType(typeEnum == null ? AutoApprovalTypeEnum.NO_AUTO_APPROVAL : typeEnum);
}
});
}
@ -377,7 +384,6 @@ public final class BpmnMetaParserHelper {
return defaultValid(userTask, CONFIG_APPROVER_EMPTY_HANDLE_TYPE).map(element -> StringUtils.hasLength(element.getElementText()) ? element.getElementText() : "[]");
}
private static Optional<ExtensionElement> defaultValid(FlowElement flowElement, String elementName) {
if (Objects.isNull(flowElement)) {
return Optional.empty();

View File

@ -0,0 +1,28 @@
package cn.axzo.workflow.core.common.utils;
import org.springframework.lang.Nullable;
import java.util.Map;
public class SpringContextUtils {
private static SpringContextUtils.SpringContext springContext;
public SpringContextUtils(SpringContextUtils.SpringContext springContext) {
SpringContextUtils.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.SpringContextUtils;
import cn.axzo.workflow.core.engine.behavior.CustomActivityBehaviorFactory;
import cn.axzo.workflow.core.engine.cmd.CustomCommandContextFactory;
import cn.axzo.workflow.core.engine.id.BasedNacosSnowflakeIdGenerator;
@ -27,14 +28,20 @@ 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.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.lang.Nullable;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import static org.flowable.common.engine.impl.AbstractEngineConfiguration.DB_SCHEMA_UPDATE_TRUE;
@ -111,4 +118,30 @@ public class FlowableConfiguration {
return new CustomActivityBehaviorFactory();
}
@Bean
@Order
public SpringContextUtils springContextUtils(SpringContext springContext) {
return new SpringContextUtils(springContext);
}
@Configuration
public static class SpringContext implements SpringContextUtils.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

@ -21,6 +21,7 @@ import java.util.List;
import java.util.Objects;
import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.AUTO_APPROVAL_TYPE;
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.CONFIG_APPROVER_EMPTY_HANDLE_TYPE;
@ -71,8 +72,8 @@ public class UserTaskJsonConverter extends AbstractBpmnJsonConverter<UserTask> {
setApprovalExtensionElement(node, userTask);
// "权限设置"
setFieldExtensionElement(node, userTask);
// "高级设置"
setButtonExtensionElement(node, userTask);
// "高级设置",包含按钮配置,自动过审配置
setAdvancedExtensionElement(node, userTask);
// "待办消息模板配置"
setPendingMessageExtensionElement(node, userTask);
@ -130,7 +131,7 @@ public class UserTaskJsonConverter extends AbstractBpmnJsonConverter<UserTask> {
}
}
private static void setButtonExtensionElement(BpmnJsonNode node, UserTask userTask) {
private static void setAdvancedExtensionElement(BpmnJsonNode node, UserTask userTask) {
if (Objects.isNull(node.getProperty())) {
return;
}
@ -146,6 +147,13 @@ public class UserTaskJsonConverter extends AbstractBpmnJsonConverter<UserTask> {
}
userTask.addExtensionElement(buttonConfigElement);
//添加自动审批配置
ExtensionElement autoApprovalExtensionElement = new ExtensionElement();
ExtensionAttribute pendingMessageAttribute = new ExtensionAttribute();
pendingMessageAttribute.setName(AUTO_APPROVAL_TYPE);
autoApprovalExtensionElement.addAttribute(pendingMessageAttribute);
userTask.addExtensionElement(autoApprovalExtensionElement);
}
private static void setFieldExtensionElement(BpmnJsonNode node, UserTask userTask) {

View File

@ -4,7 +4,6 @@ import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
@ -32,6 +31,7 @@ import static cn.axzo.workflow.core.common.code.BpmnInstanceRespCode.PROCESS_INS
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_BIZ_SET_ASSIGNEE_ERROR;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ACTIVITY_CANT_SET_ASSIGNEE;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.PROCESS_CANT_SET_ASSIGNEE;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount;
/**
* 自定的业务指定审批人命令实现
@ -57,17 +57,33 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand<Boolean>
return JSON.toJSONString(params);
}
public static Task getOperateTask(TaskService taskService, String executionId) {
return taskService.createTaskQuery().executionId(executionId)
.taskAssignee(NO_ASSIGNEE)
.singleResult();
}
/**
* 校验
*
* @param runtimeService
* @param task
* @param assigners
*/
public static void validate(RuntimeService runtimeService, String executionId, Task task, List<BpmnTaskDelegateAssigner> assigners) {
validTask(task, executionId);
//校验审批人数量是否超过限制
validTaskAssignerCount(runtimeService, (TaskEntity) task, assigners);
}
@Override
public Boolean execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
TaskService taskService = processEngineConfiguration.getTaskService();
Task task = taskService.createTaskQuery().executionId(executionId)
.taskAssignee(NO_ASSIGNEE)
.singleResult();
validTask(task);
Task task = getOperateTask(taskService, executionId);
//校验
validate(processEngineConfiguration.getRuntimeService(), executionId, task, addedAssigners);
validProcessInstance(commandContext, task);
@ -124,7 +140,7 @@ public class CustomBizSpecifyAssigneeToTaskCmd extends AbstractCommand<Boolean>
CustomTaskHelper.deleteMultiTask(commandContext, (TaskEntity) task);
}
private void validTask(Task task) {
private static void validTask(Task task, String executionId) {
if (Objects.isNull(task)) {
throw new WorkflowEngineException(ACTIVITY_BIZ_SET_ASSIGNEE_ERROR, executionId);
}

View File

@ -35,11 +35,13 @@ import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.constant.BpmnConstants.CARBON_ASSIGNER_LIMIT_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.getLimitedElementList;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.removeDuplicateByPersonId;
/**
@ -157,7 +159,7 @@ public class CustomCarbonCopyUserSelectorCmd extends AbstractCommand<List<BpmnTa
break;
}
});
return removeDuplicateByPersonId(assigners);
return getLimitedElementList(removeDuplicateByPersonId(assigners), CARBON_ASSIGNER_LIMIT_NUMBER);
}
private List<BpmnTaskDelegateAssigner> getApproverRelationUser(BpmnCarbonCopyConf carbon,

View File

@ -4,7 +4,6 @@ import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO;
import cn.axzo.workflow.core.engine.job.AsyncCountersignUserTaskJobHandler;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.TaskService;
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
@ -17,10 +16,9 @@ import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerDuplicated;
public class CustomCountersignUserTaskAsyncCmd extends AbstractCommand<Void> implements Serializable {
@ -50,6 +48,8 @@ public class CustomCountersignUserTaskAsyncCmd extends AbstractCommand<Void> imp
validTaskAssignerDuplicated(commandContext, (TaskEntity) task, dto.getTargetAssignerList());
validTaskAssignerCount(processEngineConfiguration.getRuntimeService(), (TaskEntity) task, dto.getTargetAssignerList());
startAsync(processEngineConfiguration, task);
return null;
}

View File

@ -8,7 +8,7 @@ import cn.axzo.workflow.core.service.ExtAxHiTaskInstService;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.interceptor.Command;
import org.apache.commons.lang3.math.NumberUtils;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
@ -25,12 +25,14 @@ import java.util.List;
import java.util.Map;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.COUNTERSIGN_ASSIGNER_SHOW_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.COUNTERSIGN;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.createVirtualTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerCount;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTaskAssignerDuplicated;
/**
@ -90,6 +92,8 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
validTask(historicTaskInstance, (TaskEntity) task, originTaskAssignee, null);
validTaskAssignerCount(processEngineConfiguration.getRuntimeService(), (TaskEntity) task, targetTaskAssigneeList);
List<BpmnTaskDelegateAssigner> taskDelegateAssigners =
validTaskAssignerDuplicated(commandContext, (TaskEntity) task, targetTaskAssigneeList);
@ -142,7 +146,9 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand<Void> implemen
TaskService taskService, Task task) {
// 构建评论内容
StringBuilder message = new StringBuilder("添加");
for (int i = 0; i < targetTaskAssigneeList.size(); i++) {
int end = NumberUtils.min(targetTaskAssigneeList.size(), COUNTERSIGN_ASSIGNER_SHOW_NUMBER);
//加签人员数量显示指定个数
for (int i = 0; i < end; i++) {
message.append(targetTaskAssigneeList.get(i).getAssignerName());
if (i < targetTaskAssigneeList.size() - 1) {
message.append("");

View File

@ -23,7 +23,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ASSIGNER_LIMIT_NUMBER;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.getLimitedElementList;
/**
* 自定义的推测用户任务的审批人的命令实现
@ -88,7 +90,7 @@ public class CustomForecastUserTaskAssigneeCmd extends AbstractCommand<List<Bpmn
}
log.info("流程实例: {} , UserTask: {}推测的最终审批人为: {}", processInstanceId, userTask.getId(),
JSONUtil.toJsonStr(forecastAssigners));
return forecastAssigners;
return getLimitedElementList(forecastAssigners, APPROVAL_ASSIGNER_LIMIT_NUMBER);
}
}

View File

@ -49,6 +49,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ASSIGNER_LIMIT_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
@ -58,10 +59,10 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERA
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ASSIGNEE_HAS_BEEN_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_HAS_BEEN_COMPLETE;
import static cn.axzo.workflow.core.common.code.BpmnTaskRespCode.TASK_TYPE_MISMATCH;
import static org.flowable.task.api.Task.DEFAULT_PRIORITY;
/**
@ -153,7 +154,7 @@ public class CustomTaskHelper {
//不包含对应的任务
if (!nodeTypes.contains(nodeType)) {
// log.warn(TASK_TYPE_MISMATCH.getMessage(), nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
throw new WorkflowEngineException(TASK_TYPE_MISMATCH, nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
throw new WorkflowEngineException(ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT, nodeType.getDesc(), nodeTypes.stream().map(BpmnFlowNodeType::getDesc).collect(Collectors.joining(",")));
}
}
}
@ -193,6 +194,26 @@ public class CustomTaskHelper {
return taskAssignerListSnapshot;
}
/**
* 校验人员数量是否超过限制
*
* @param runtimeService
* @param taskEntity
* @param targetAssigneeList
* @return
*/
public static List<BpmnTaskDelegateAssigner> validTaskAssignerCount(RuntimeService runtimeService,
TaskEntity taskEntity,
List<BpmnTaskDelegateAssigner> targetAssigneeList) {
// 这个节点下所有审批人快照
String activityListSnapshot = INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey();
List<BpmnTaskDelegateAssigner> taskAssignerListSnapshot = runtimeService.getVariable(taskEntity.getProcessInstanceId(), activityListSnapshot, List.class);
if (taskAssignerListSnapshot.size() + targetAssigneeList.size() > APPROVAL_ASSIGNER_LIMIT_NUMBER) {
throw new WorkflowEngineException(ASSIGNER_NUMBER_EXCEEDS_NUMBER_LIMIT);
}
return taskAssignerListSnapshot;
}
/**
* 保存附件
*
@ -379,6 +400,22 @@ public class CustomTaskHelper {
.values());
}
/**
* 获取指定数量元素
*
* @param assigners 原始列表列表
* @return 截取后的列表
*/
public static <T> List<T> getLimitedElementList(List<T> assigners, Integer limitNumber) {
if (limitNumber == null || limitNumber <= 0) {
throw new IllegalArgumentException("limit number must be greater than 0");
}
if (CollectionUtils.isEmpty(assigners) || assigners.size() <= limitNumber) {
return assigners;
}
return new ArrayList<>(assigners.subList(0, limitNumber));
}
public static List<BpmnTaskDelegateAssigner> getHistoryOperationUsers(CommandContext commandContext, String processInstanceId,
BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter,
String serviceVersion) {

View File

@ -38,9 +38,10 @@ public class CustomRetryInterceptor extends AbstractCommandInterceptor {
try {
// try to execute the command
log.info("assignableFrom result: {}", AbstractCommand.class.isAssignableFrom(command.getClass()));
if (AbstractCommand.class.isAssignableFrom(command.getClass())) {
// 如果在以后,重试三次也不能解决的话, 可以利用这里的拿到的参数,重新自动构造CMD,并执行.
log.info("traceId:{} Executing command params: {}", TraceUtil.traceId(),
log.info("Executing command params: {} traceId:{} ", TraceUtil.traceId(),
((AbstractCommand<T>) command).paramToJsonString());
}
return next.execute(config, command, commandExecutor);

View File

@ -0,0 +1,45 @@
package cn.axzo.workflow.core.engine.job;
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
import cn.axzo.workflow.core.repository.entity.ExtAxProperty;
import cn.axzo.workflow.core.service.ExtAxPropertyService;
import org.apache.commons.lang.StringUtils;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.job.service.JobHandler;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.variable.api.delegate.VariableScope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
public abstract class AbstractExecuteWithLockJobHandler extends AbstractJobHandler implements JobHandler {
private static final Logger log = LoggerFactory.getLogger(AbstractExecuteWithLockJobHandler.class);
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
ExtAxPropertyService extAxPropertyService = SpringContextUtils.getBean(ExtAxPropertyService.class);
String processInstanceId = job.getProcessInstanceId(), jobId = job.getId();
if (StringUtils.isBlank(processInstanceId) || StringUtils.isBlank(jobId)) {
log.warn("processInstanceId or lockOwner is empty,cannot execute with lock,jobId:{},processInstanceId:{}", job.getId(), job.getProcessInstanceId());
executeInternal(job, configuration, variableScope, commandContext);
return;
}
//todo 处理超时时间超过一定时间锁还存在删除锁
try {
ExtAxProperty extAxProperty = new ExtAxProperty();
extAxProperty.setName(processInstanceId);
extAxProperty.setValue(jobId);
extAxPropertyService.add(extAxProperty);
log.info("job acquire lock success,processInstanceId:{},jobId:{}", processInstanceId, jobId);
executeInternal(job, configuration, variableScope, commandContext);
} catch (DuplicateKeyException e) {
log.error("executeWithLock error,lock by another job,jobId:{},processInstanceId:{}", job.getId(), job.getProcessInstanceId(), e);
throw e;
} finally {
extAxPropertyService.delete(processInstanceId, jobId);
}
}
abstract void executeInternal(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext);
}

View File

@ -12,13 +12,6 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.task.api.Task;
import org.flowable.variable.api.delegate.VariableScope;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
@ -30,7 +23,7 @@ import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
* @since 2024/4/15 22:41
*/
@Slf4j
public class AsyncApproveTaskJobHandler extends AbstractJobHandler implements JobHandler {
public class AsyncApproveTaskJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
public static final String TYPE = "async-approve-task";
@Override
@ -39,7 +32,7 @@ public class AsyncApproveTaskJobHandler extends AbstractJobHandler implements Jo
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
public void executeInternal(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("AsyncApproveTaskJobHandler executing...");
log(job);
ProcessEngineConfigurationImpl processEngineConfiguration =

View File

@ -19,7 +19,7 @@ import org.flowable.variable.api.delegate.VariableScope;
* @since 2024/4/16 11:11
*/
@Slf4j
public class AsyncRejectTaskJobHandler extends AbstractJobHandler implements JobHandler {
public class AsyncRejectTaskJobHandler extends AbstractExecuteWithLockJobHandler implements JobHandler {
public static final String TYPE = "async-reject-task";
private final ExtAxHiTaskInstService extAxHiTaskInstService;
@ -33,7 +33,7 @@ public class AsyncRejectTaskJobHandler extends AbstractJobHandler implements Job
}
@Override
public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
public void executeInternal(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) {
log.info("AsyncRejectTaskJobHandler executing...");
log(job);
ProcessEngineConfigurationImpl processEngineConfiguration =

View File

@ -108,7 +108,6 @@ public class EngineCarbonCopyEventListener implements JavaDelegate {
}
return conf;
}).orElse(null);
MessagePushEventImpl event = MessagePushEventBuilder.createEvent(MessagePushEventType.CARBON_COPY,
carbonUsers, bpmnNoticeConf, execution.getProcessInstanceId(),
parseProcessDefinitionKey(execution.getProcessDefinitionId()),

View File

@ -37,6 +37,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ASSIGNER_LIMIT_NUMBER;
import static cn.axzo.workflow.common.constant.BpmnConstants.BPM_ALLOW_SKIP_USER_TASK;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.DUMMY_ASSIGNEE_TYPE;
@ -53,6 +54,7 @@ import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApprove
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getApproverSpecify;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getNodeType;
import static cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper.getProcessServerVersion;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.getLimitedElementList;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.removeDuplicateByPersonId;
@ -148,13 +150,13 @@ public class EngineExecutionStartListener implements ExecutionListener {
// 审批候选人为空时的兜底
emptyAssigneeHandle(assigners, userTask, execution);
for (BpmnTaskDelegateAssigner user : assigners) {
List<BpmnTaskDelegateAssigner> resultAssigners = getLimitedElementList(assigners, APPROVAL_ASSIGNER_LIMIT_NUMBER);
for (BpmnTaskDelegateAssigner user : resultAssigners) {
assigneeIdList.add(user.buildAssigneeId());
}
execution.setVariable(INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + currentActivityId,
assigners);
resultAssigners);
break;
}
// UserTask 多实例, 该变量用于引擎

View File

@ -1,10 +1,13 @@
package cn.axzo.workflow.core.repository.entity;
import cn.axzo.framework.data.mybatisplus.model.BaseEntity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
@ -17,6 +20,8 @@ import lombok.ToString;
@TableName(value = "ext_ax_property", autoResultMap = true)
@Data
@ToString(callSuper = true)
@AllArgsConstructor
@NoArgsConstructor
public class ExtAxProperty extends BaseEntity<ExtAxProperty> {
private static final long serialVersionUID = 1L;
@ -39,4 +44,7 @@ public class ExtAxProperty extends BaseEntity<ExtAxProperty> {
*/
@TableField("manageable")
private Boolean manageable;
@TableField(value = "is_delete", fill = FieldFill.INSERT)
private Long isDelete = 0L;
}

View File

@ -3,6 +3,7 @@ package cn.axzo.workflow.core.service;
import cn.axzo.workflow.core.repository.entity.ExtAxProperty;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* 引擎服务持久配置信息表操作 Service
@ -17,4 +18,17 @@ public interface ExtAxPropertyService {
ExtAxProperty update(ExtAxProperty property);
Optional<ExtAxProperty> getByName(String name);
int delete(String name, String value);
/**
* 删除指定时间段之前创建的数据
*
* @param name
* @param timeOut
* @param timeUnit
* @return
*/
int deleteByNameWithDuration(String name, Long timeOut, TimeUnit timeUnit);
}

View File

@ -17,6 +17,7 @@ import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.job.service.JobService;
import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@ -95,6 +96,10 @@ public class BpmnProcessActivityServiceImpl implements BpmnProcessActivityServic
@Override
public void setAssigneeAsync(BpmnActivitySetAssigneeDTO dto) {
//查询任务
Task task = CustomBizSpecifyAssigneeToTaskCmd.getOperateTask(processEngineConfiguration.getTaskService(), dto.getTriggerId());
//先校验
CustomBizSpecifyAssigneeToTaskCmd.validate(processEngineConfiguration.getRuntimeService(), dto.getTriggerId(), task, dto.getAssigners());
validateAndStartAsyncJob(dto.getTriggerId(), dto, JOB_ASSIGNEE_ASYNC_NAME);
}

View File

@ -9,10 +9,17 @@ import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
/**
* 引擎服务持久配置信息表操作 Service 实现
@ -28,6 +35,7 @@ public class ExtAxPropertyServiceImpl implements ExtAxPropertyService {
@CacheEvict(value = "property", key = "#property.name")
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public ExtAxProperty add(ExtAxProperty property) {
mapper.insert(property);
return property;
@ -50,4 +58,30 @@ public class ExtAxPropertyServiceImpl implements ExtAxPropertyService {
.eq("name", name);
return Optional.ofNullable(mapper.selectOne(queryWrapper));
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int delete(String name, String value) {
if (!StringUtils.hasText(name)) {
return 0;
}
Map<String, Object> deleteMap = new HashMap<>();
deleteMap.put("name", name);
if (StringUtils.hasText(value)) {
deleteMap.put("value", value);
}
return mapper.deleteByMap(deleteMap);
}
public int deleteByNameWithDuration(String name, Long timeOut, TimeUnit timeUnit) {
if (!StringUtils.hasText(name) || timeOut == null || timeUnit == null) {
log.error("argument not valid,name:{},timeOut:{},timeUnit:{}", name, timeOut, timeUnit);
throw new IllegalArgumentException("argument not valid");
}
LocalDateTime startTime = LocalDateTime.now().minus(timeUnit.toMillis(timeOut), ChronoUnit.MILLIS);
QueryWrapper<ExtAxProperty> queryWrapper = new QueryWrapper<ExtAxProperty>()
.eq("name", name)
.le("create_at", startTime);
return mapper.delete(queryWrapper);
}
}

View File

@ -0,0 +1,53 @@
package cn.axzo.workflow.core.version;
import cn.axzo.workflow.core.common.utils.SpringContextUtils;
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;
import static cn.axzo.workflow.core.version.Versioned.UNKNOWN_VERSION;
public class MultiVersionBeanUtils {
/**
* 获取支持指定版本的bean
*
* @param clazz 类型
* @param version 版本号
* @param <T>
* @return
*/
public static <T extends Versioned> T getSpecifiedVersionBean(Class<T> clazz, String version) {
Map<String, T> beans = SpringContextUtils.getBeansOfType(clazz);
if (CollectionUtils.isEmpty(beans)) {
throw new NullPointerException("no beans of type " + clazz.getName());
}
if (StringUtils.isEmpty(version)) {
T t = beans.values().stream().filter(bean -> bean.getVersion() == null || bean.getVersion() == UNKNOWN_VERSION).findFirst().orElse(null);
if (t != null) {
return t;
}
throw new IllegalArgumentException("no default version bean found for version: " + version);
}
//根据版本号排序
List<T> sortedList = beans.values().stream().sorted((o1, o2) -> {
DefaultArtifactVersion version1 = o1.getVersion() == null ? UNKNOWN_VERSION : o1.getVersion();
DefaultArtifactVersion version2 = o2.getVersion() == null ? UNKNOWN_VERSION : o2.getVersion();
return version1.compareTo(version2);
}).collect(Collectors.toList());
DefaultArtifactVersion targetVersion = new DefaultArtifactVersion(version);
for (int i = sortedList.size() - 1; i >= 0; i--) {
DefaultArtifactVersion classVersion = sortedList.get(i).getVersion() == null ? UNKNOWN_VERSION : sortedList.get(i).getVersion();
int flag = classVersion.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,10 @@
package cn.axzo.workflow.core.version;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
public interface Versioned {
DefaultArtifactVersion getVersion();
DefaultArtifactVersion UNKNOWN_VERSION = new DefaultArtifactVersion("0.0.0");
}

View File

@ -0,0 +1,2 @@
ALTER TABLE ext_ax_property ADD UNIQUE INDEX name_unique_index (name);

View File

@ -1,6 +1,7 @@
package cn.axzo.workflow.server.common.aspectj;
import cn.axzo.workflow.core.common.exception.WorkflowEngineException;
import cn.axzo.workflow.core.common.utils.TraceUtil;
import cn.axzo.workflow.server.common.annotation.RepeatSubmit;
import cn.axzo.workflow.server.common.config.RepeatSubmitResolver;
import cn.axzo.workflow.server.common.util.RedisUtils;
@ -81,7 +82,7 @@ public class RepeatSubmitAspect implements Ordered {
String key = RedisUtils.getCacheObject(cacheRepeatKey);
if (key == null) {
RedisUtils.setCacheObject(cacheRepeatKey, "", Duration.ofMillis(interval));
RedisUtils.setCacheObject(cacheRepeatKey, TraceUtil.traceId(), Duration.ofMillis(interval));
KEY_CACHE.set(cacheRepeatKey);
} else {
log.warn("{}", repeatSubmit.message());

View File

@ -139,7 +139,7 @@ public class RocketMqBpmActivityEventListener extends AbstractBpmnEventListener<
// 这个 ID 等于 ExecutionId
dto.setTriggerId(execution.getId());
dto.setActivityId(execution.getCurrentActivityId());
dto.setActivityName(((ExecutionEntityImpl) execution).getCurrentActivityName());
dto.setActivityName(execution.getCurrentFlowElement().getName());
ProcessInstance processInstance = getContext().getProcessInstance(() ->
runtimeService.createProcessInstanceQuery().processInstanceId(execution.getProcessInstanceId())
.includeProcessVariables().singleResult());

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.server.controller.listener.notice;
import cn.axzo.core.utils.converter.BeanConverter;
import cn.axzo.framework.rocketmq.Event;
import cn.axzo.framework.rocketmq.EventProducer;
import cn.axzo.workflow.common.enums.ProcessMessagePushEventEnum;
@ -12,6 +13,7 @@ import cn.axzo.workflow.core.common.context.NoticeOperationContext;
import cn.axzo.workflow.core.common.utils.BpmnMetaParserHelper;
import cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper;
import cn.axzo.workflow.core.engine.event.MessagePushEvent;
import cn.axzo.workflow.core.engine.event.MessagePushEventImpl;
import cn.axzo.workflow.core.listener.AbstractBpmnEventListener;
import cn.axzo.workflow.core.listener.BpmnMessagePushEventListener;
import cn.axzo.workflow.core.service.BpmnProcessInstanceService;
@ -55,6 +57,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_SPECIFY_NE
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_OWNERSHIP_PROCESS_DEFINITION_KEY;
import static cn.axzo.workflow.common.constant.BpmnConstants.MQ_ASSIGNER_BATCH_SIZE;
import static cn.axzo.workflow.common.constant.BpmnConstants.MULTI_INSTANCE_LOOP_COUNTER;
import static cn.axzo.workflow.common.constant.BpmnConstants.NUMBER_OF_INSTANCES;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
@ -135,9 +138,7 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener<
log.info("RocketMqMessagePushEventListener#onNotice...msgTemplateId: {}, receivePerson: {}, processInstanceId: {}",
event.getNoticeConfig().getNotice().getNoticeMessageId(), JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId());
if (Objects.nonNull(event.getNoticeConfig().getNotice())) {
MessagePushDTO dto = build(noticeTemplateCode,
PROCESS_PUSH_NOTICE, event, collectionVariable(event));
sendMessageQueue(dto, PROCESS_PUSH_NOTICE);
getMessagePushDtoSlice(event, noticeTemplateCode, PROCESS_PUSH_NOTICE).forEach(dto -> sendMessageQueue(dto, PROCESS_PUSH_NOTICE));
}
log.info("RocketMqMessagePushEventListener#onNotice...end, msgTemplateId: {}, receivePerson: {}, processInstanceId: {}",
event.getNoticeConfig().getNotice().getNoticeMessageId(), JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId());
@ -245,20 +246,47 @@ public class RocketMqMessagePushEventListener extends AbstractBpmnEventListener<
|| !StringUtils.hasText(event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId())) {
return;
}
// TODO 这里的抄送人,不建议使用一个 MQ 事件来承载,而是一个人一个事件
log.info("RocketMqMessagePushEventListener#onCarbonCopy... cc' templateId: {}, receivePerson: {}, processInstanceId: {}",
event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(),
JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId());
if (Objects.nonNull(event.getNoticeConfig().getCarbonCopy())) {
MessagePushDTO dto = build(event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(),
PROCESS_CARBON_COPY, event, collectionVariable(event));
sendMessageQueue(dto, PROCESS_CARBON_COPY);
//按人员拆分为多个批次发送消息
getMessagePushDtoSlice(event, event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(), PROCESS_CARBON_COPY).forEach(dto -> sendMessageQueue(dto, PROCESS_CARBON_COPY));
}
log.info("RocketMqMessagePushEventListener#onCarbonCopy...end, cc' templateId: {}, receivePerson: {}, processInstanceId: {}",
event.getNoticeConfig().getCarbonCopy().getCarbonCopyMessageId(),
JSONUtil.toJsonStr(event.getAssigners()), event.getProcessInstanceId());
}
/**
* 根据人员数量拆分消息发送
*
* @param event 事件消息
* @return 发送mq消息列表
*/
private List<MessagePushDTO> getMessagePushDtoSlice(MessagePushEvent event, String templateId, ProcessMessagePushEventEnum type) {
if (event == null) {
throw new NullPointerException("event不能为空");
}
List<BpmnTaskDelegateAssigner> assigners = event.getAssigners();
if (CollectionUtils.isEmpty(assigners) || assigners.size() <= MQ_ASSIGNER_BATCH_SIZE) {
return Collections.singletonList(build(templateId, type, event, collectionVariable(event)));
}
List<MessagePushDTO> slice = new ArrayList<>();
Map<String, Object> objectMap = collectionVariable(event);
int startIndex = 0;
do {
List<BpmnTaskDelegateAssigner> batchAssigners = assigners.subList(startIndex, Integer.min(startIndex + MQ_ASSIGNER_BATCH_SIZE, assigners.size()));
MessagePushEventImpl messagePushEvent = new MessagePushEventImpl(event.getType());
BeanConverter.convert(event, messagePushEvent);
messagePushEvent.setAssigner(batchAssigners);
MessagePushDTO dto = build(templateId, type, messagePushEvent, objectMap);
slice.add(dto);
startIndex = startIndex + MQ_ASSIGNER_BATCH_SIZE;
} while (startIndex < assigners.size());
return slice;
}
/**
* 完成抄送
*

View File

@ -1,9 +1,9 @@
package cn.axzo.workflow.server.controller.listener.task;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.enums.AutoApprovalTypeEnum;
import cn.axzo.workflow.common.model.request.BpmnApproveConf;
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 +11,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.MultiVersionBeanUtils;
import cn.axzo.workflow.server.controller.listener.task.service.CheckApproverService;
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,11 +35,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.concurrent.atomic.AtomicBoolean;
import java.util.Optional;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
@ -53,10 +50,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;
@ -79,7 +74,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
@ -94,14 +88,20 @@ public class AutoOperatorEvent_101_Listener extends AbstractBpmnEventListener<Ta
Process mainProcess = repositoryService.getBpmnModel(delegateTask.getProcessDefinitionId()).getMainProcess();
UserTask userTask = (UserTask) mainProcess.getFlowElement(delegateTask.getTaskDefinitionKey());
boolean exists = checkApproverExists(delegateTask, userTask, mainProcess);
log.info("是否需要自动过程判断 exists:{}", exists);
Optional<BpmnApproveConf> processApproveConf = BpmnMetaParserHelper.getProcessApproveConf(mainProcess);
//自动过审配置连续节点自动过审才处理历史数据默认不自动过审
if (processApproveConf.isPresent() && AutoApprovalTypeEnum.CONTINUOUS_NODES_AUTO_APPROVAL == processApproveConf.get().getAutoApprovalType()) {
Object versionVar = delegateTask.getVariable(WORKFLOW_ENGINE_VERSION);
String version = versionVar == null ? null : String.valueOf(versionVar);
CheckApproverService checkApproverService = MultiVersionBeanUtils.getSpecifiedVersionBean(CheckApproverService.class, version);
boolean exists = checkApproverService.checkApproverExists(delegateTask, userTask, mainProcess, getContext());
log.info("是否需要自动过程判断 exists:{},processInstId:{},taskDefinitionKey:{}", exists, delegateTask.getProcessInstanceId(), delegateTask.getTaskDefinitionKey());
if (exists) {
taskService.addComment(delegateTask.getId(), delegateTask.getProcessInstanceId(), COMMENT_TYPE_ADVICE,
"同一审批人,自动过审");
autoPass(delegateTask);
}
}
// 检测节点自身配置是否有自动操作
checkApprovalMethod(delegateTask, userTask);
@ -140,72 +140,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,13 @@
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 CheckApproverService extends Versioned {
boolean checkApproverExists(DelegateTask delegateTask, UserTask userTask, Process mainProcess, TaskOperationContext taskOperationContext);
}

View File

@ -0,0 +1,141 @@
package cn.axzo.workflow.server.controller.listener.task.service.impl;
import cn.axzo.workflow.common.enums.BpmnButtonEnum;
import cn.axzo.workflow.common.enums.BpmnFlowNodeType;
import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo;
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.CheckApproverService;
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.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_INITIATOR;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS;
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;
/**
* REQ-2596. 自动过审规则
* a. 审批人=发起人/历史审批人
* b. 连续节点
* c. 当前节点有同意按钮
*/
@Component
@AllArgsConstructor
public class CheckApproverServiceImpl implements CheckApproverService {
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);
//业务节点,指定业务审批人,或者业务设置了审批人,也要参与自动过审,node_type为NODE_BUSINESS,节点元素类型为UserTask
//业务节点审批人为空,或者人员未指定,此时不进行自动过审操作,需要再业务指定审批人后,再做自动过审动作
if (!(Objects.equals(currentNodeType, NODE_TASK) || Objects.equals(currentNodeType, NODE_BUSINESS)) ||
!StringUtils.hasText(delegateTask.getAssignee()) ||
delegateTask.getAssignee().equals(NO_ASSIGNEE)) {
return exists.get();
}
Optional<BpmnButtonConf> optConfig = BpmnMetaParserHelper.getButtonConfig(mainProcess, delegateTask.getTaskDefinitionKey());
if (!optConfig.isPresent()) {
return exists.get();
}
BpmnButtonConf bpmnButtonConf = optConfig.get();
List<BpmnButtonMetaInfo> currentButtons = bpmnButtonConf.getCurrent();
if (CollectionUtils.isEmpty(currentButtons)) {
return exists.get();
}
Optional<BpmnButtonMetaInfo> agreeButton = currentButtons.stream()
.filter(button -> button.getType().equals("SYSTEM") //系统按钮
&& button.getChecked() != null && button.getChecked() //选中
&& (button.getDisabled() == null || !button.getDisabled()) //没用禁用
&& button.getBtnKey().equals(BpmnButtonEnum.BPMN_APPROVE.getBtnKey())) //类型为同意
.findFirst();
//不存在同意按钮
if (!agreeButton.isPresent()) {
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) || (Objects.equals(NODE_BUSINESS, j) && flowElement.getClass().isAssignableFrom(UserTask.class))) {
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);
return null;
}
}

View File

@ -23,6 +23,7 @@ import cn.axzo.workflow.core.service.BpmnProcessTaskService;
import cn.axzo.workflow.server.common.annotation.ErrorReporter;
import cn.axzo.workflow.server.common.annotation.RepeatSubmit;
import cn.azxo.framework.common.model.CommonResponse;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
@ -186,7 +187,7 @@ public class BpmnProcessTaskController implements ProcessTaskApi {
@PostMapping("/countersign")
@RepeatSubmit
public CommonResponse<Boolean> countersignTask(@Validated @RequestBody BpmnTaskCountersignDTO countersignDTO) {
log.info("加签任务 countersignTask===>>>参数:{}", countersignDTO);
log.info("加签任务 countersignTask===>>>参数:{}", JSON.toJSONString(countersignDTO));
bpmnProcessTaskService.countersignTask(countersignDTO);
return success(true);
}