diff --git a/pom.xml b/pom.xml index 03df7489b..130212e4f 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ workflow-engine - 1.4.0-SNAPSHOT + 1.4.1-SNAPSHOT 2.0.0-SNAPSHOT 2.0.0-SNAPSHOT 11.8 diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java index 9a3a41d7b..6c132c786 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/constant/BpmnConstants.java @@ -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"; /** * 用于国内审批节点填写审批建议 *

@@ -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; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/AutoApprovalTypeEnum.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/AutoApprovalTypeEnum.java new file mode 100644 index 000000000..91189769d --- /dev/null +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/enums/AutoApprovalTypeEnum.java @@ -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; + } +} diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java index bab520d14..abc5fe45e 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/BpmnApproveConf.java @@ -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; } diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java index 9337b7521..33896b0f3 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNodeProperty.java @@ -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; diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java index 7ddf139f7..6f5a22aff 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/task/BpmnActivitySetAssigneeDTO.java @@ -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 assigners; /** diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/AsyncJobRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/AsyncJobRespCode.java index 9dad9b374..9b8de2294 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/AsyncJobRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/AsyncJobRespCode.java @@ -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; - } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java index 02d029de7..471cd0e27 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnInstanceRespCode.java @@ -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; - } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnModelRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnModelRespCode.java index 2f5f12a2e..3deec0d1e 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnModelRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnModelRespCode.java @@ -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; - } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnProcessDefinitionRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnProcessDefinitionRespCode.java index 16b038961..145712343 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnProcessDefinitionRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnProcessDefinitionRespCode.java @@ -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; - } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java index e3b3b5616..3fdbeea92 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/BpmnTaskRespCode.java @@ -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; - } - } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/CategoryRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/CategoryRespCode.java index cca3ac338..c3456f937 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/CategoryRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/CategoryRespCode.java @@ -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; - } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/ConvertorRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/ConvertorRespCode.java index 84ea0fb9c..c961cbc02 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/ConvertorRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/ConvertorRespCode.java @@ -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; - } - } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java index e82a9f833..7fbdc0886 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FlowableEngineRespCode.java @@ -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; - } - } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormDefinitionRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormDefinitionRespCode.java index 4f1e9901b..8665d3154 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormDefinitionRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormDefinitionRespCode.java @@ -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; - } - } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormModelRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormModelRespCode.java index 4650677ee..7db2b5b6e 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormModelRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/FormModelRespCode.java @@ -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; - } - } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/OtherRespCode.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/OtherRespCode.java index 9edc8db47..ae80b5003 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/OtherRespCode.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/code/OtherRespCode.java @@ -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; - } - } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java index 024744e13..bb8691467 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java @@ -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) { diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java index d10efc4e9..3cbaf4f0c 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnMetaParserHelper.java @@ -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 defaultValid(FlowElement flowElement, String elementName) { if (Objects.isNull(flowElement)) { return Optional.empty(); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/SpringContextUtils.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/SpringContextUtils.java new file mode 100644 index 000000000..86871e9d1 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/SpringContextUtils.java @@ -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 getBean(Class clazz) { + return springContext.getBean(clazz); + } + + public static Map getBeansOfType(@Nullable Class type) { + return springContext.getBeansOfType(type); + } + + public interface SpringContext { + T getBean(Class var1); + + Map getBeansOfType(@Nullable Class type); + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java index 2b47c563b..f44d04057 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/conf/FlowableConfiguration.java @@ -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 getBean(Class clazz) { + return this.applicationContext.getBean(clazz); + } + + public Map getBeansOfType(@Nullable Class type){ + return this.applicationContext.getBeansOfType(type); + } + + public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + } + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/UserTaskJsonConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/UserTaskJsonConverter.java index 3b20e789b..83613b362 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/UserTaskJsonConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/UserTaskJsonConverter.java @@ -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 { setApprovalExtensionElement(node, userTask); // "权限设置" setFieldExtensionElement(node, userTask); - // "高级设置" - setButtonExtensionElement(node, userTask); + // "高级设置",包含按钮配置,自动过审配置 + setAdvancedExtensionElement(node, userTask); // "待办消息模板配置" setPendingMessageExtensionElement(node, userTask); @@ -130,7 +131,7 @@ public class UserTaskJsonConverter extends AbstractBpmnJsonConverter { } } - 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.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) { diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java index 2867af38e..f108311bf 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomBizSpecifyAssigneeToTaskCmd.java @@ -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 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 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 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); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCarbonCopyUserSelectorCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCarbonCopyUserSelectorCmd.java index 885e332e9..0a3a4eb15 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCarbonCopyUserSelectorCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCarbonCopyUserSelectorCmd.java @@ -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 getApproverRelationUser(BpmnCarbonCopyConf carbon, diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskAsyncCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskAsyncCmd.java index f347c124d..fa939ffb2 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskAsyncCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskAsyncCmd.java @@ -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 implements Serializable { @@ -50,6 +48,8 @@ public class CustomCountersignUserTaskAsyncCmd extends AbstractCommand imp validTaskAssignerDuplicated(commandContext, (TaskEntity) task, dto.getTargetAssignerList()); + validTaskAssignerCount(processEngineConfiguration.getRuntimeService(), (TaskEntity) task, dto.getTargetAssignerList()); + startAsync(processEngineConfiguration, task); return null; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java index 877cad88d..d524ad757 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomCountersignUserTaskCmd.java @@ -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 implemen validTask(historicTaskInstance, (TaskEntity) task, originTaskAssignee, null); + validTaskAssignerCount(processEngineConfiguration.getRuntimeService(), (TaskEntity) task, targetTaskAssigneeList); + List taskDelegateAssigners = validTaskAssignerDuplicated(commandContext, (TaskEntity) task, targetTaskAssigneeList); @@ -142,7 +146,9 @@ public class CustomCountersignUserTaskCmd extends AbstractCommand 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("、"); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomForecastUserTaskAssigneeCmd.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomForecastUserTaskAssigneeCmd.java index a17379858..84d6a2627 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomForecastUserTaskAssigneeCmd.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/cmd/CustomForecastUserTaskAssigneeCmd.java @@ -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 validTaskAssignerCount(RuntimeService runtimeService, + TaskEntity taskEntity, + List targetAssigneeList) { + // 这个节点下所有审批人快照 + String activityListSnapshot = INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT + taskEntity.getTaskDefinitionKey(); + List 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 List getLimitedElementList(List 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 getHistoryOperationUsers(CommandContext commandContext, String processInstanceId, BpmnHistoricTaskInstanceConverter historicTaskInstanceConverter, String serviceVersion) { diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java index 093f1d108..e48493113 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/interceptor/CustomRetryInterceptor.java @@ -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) command).paramToJsonString()); } return next.execute(config, command, commandExecutor); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AbstractExecuteWithLockJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AbstractExecuteWithLockJobHandler.java new file mode 100644 index 000000000..406dc44f0 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AbstractExecuteWithLockJobHandler.java @@ -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); +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskJobHandler.java index d411172ad..557406fda 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskJobHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncApproveTaskJobHandler.java @@ -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 = diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncRejectTaskJobHandler.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncRejectTaskJobHandler.java index 46363dd95..c52832ba5 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncRejectTaskJobHandler.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/job/AsyncRejectTaskJobHandler.java @@ -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 = diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java index 47d9a3cae..34f4b020e 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineCarbonCopyEventListener.java @@ -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()), diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java index b110445a1..b1a66b45b 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/engine/listener/EngineExecutionStartListener.java @@ -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 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 多实例, 该变量用于引擎 diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProperty.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProperty.java index 9fd14518d..c0adf9e72 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProperty.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/repository/entity/ExtAxProperty.java @@ -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 { private static final long serialVersionUID = 1L; @@ -39,4 +44,7 @@ public class ExtAxProperty extends BaseEntity { */ @TableField("manageable") private Boolean manageable; + + @TableField(value = "is_delete", fill = FieldFill.INSERT) + private Long isDelete = 0L; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxPropertyService.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxPropertyService.java index 289c27d0e..caf684e89 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxPropertyService.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/ExtAxPropertyService.java @@ -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 getByName(String name); + + int delete(String name, String value); + + /** + * 删除指定时间段之前创建的数据 + * + * @param name + * @param timeOut + * @param timeUnit + * @return + */ + int deleteByNameWithDuration(String name, Long timeOut, TimeUnit timeUnit); + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java index ebe551592..ec9305755 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnProcessActivityServiceImpl.java @@ -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); } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxPropertyServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxPropertyServiceImpl.java index 7ebf4702d..4f1171f60 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxPropertyServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/ExtAxPropertyServiceImpl.java @@ -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 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 queryWrapper = new QueryWrapper() + .eq("name", name) + .le("create_at", startTime); + return mapper.delete(queryWrapper); + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/version/MultiVersionBeanUtils.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/version/MultiVersionBeanUtils.java new file mode 100644 index 000000000..a6393917d --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/version/MultiVersionBeanUtils.java @@ -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 + * @return + */ + public static T getSpecifiedVersionBean(Class clazz, String version) { + Map 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 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); + } +} diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/version/Versioned.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/version/Versioned.java new file mode 100644 index 000000000..b024b21d3 --- /dev/null +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/version/Versioned.java @@ -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"); +} diff --git a/workflow-engine-core/src/main/resources/sql/upgrade_to_1.4.1.sql b/workflow-engine-core/src/main/resources/sql/upgrade_to_1.4.1.sql new file mode 100644 index 000000000..3d304a1ab --- /dev/null +++ b/workflow-engine-core/src/main/resources/sql/upgrade_to_1.4.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE ext_ax_property ADD UNIQUE INDEX name_unique_index (name); + diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/aspectj/RepeatSubmitAspect.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/aspectj/RepeatSubmitAspect.java index 259cbb313..172cf50de 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/aspectj/RepeatSubmitAspect.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/common/aspectj/RepeatSubmitAspect.java @@ -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()); diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEventListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEventListener.java index f6a54b77a..b9bc8cd16 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEventListener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/activity/RocketMqBpmActivityEventListener.java @@ -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()); diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java index 820d255a7..49a6cafa9 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/notice/RocketMqMessagePushEventListener.java @@ -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 getMessagePushDtoSlice(MessagePushEvent event, String templateId, ProcessMessagePushEventEnum type) { + if (event == null) { + throw new NullPointerException("event不能为空"); + } + List assigners = event.getAssigners(); + if (CollectionUtils.isEmpty(assigners) || assigners.size() <= MQ_ASSIGNER_BATCH_SIZE) { + return Collections.singletonList(build(templateId, type, event, collectionVariable(event))); + } + List slice = new ArrayList<>(); + Map objectMap = collectionVariable(event); + int startIndex = 0; + do { + List 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; + } + /** * 完成抄送 * diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java index 1783eb0f0..a50d516ec 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/AutoOperatorEvent_101_Listener.java @@ -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 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 !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 = 自动通过或自动驳回 * diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/service/CheckApproverService.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/service/CheckApproverService.java new file mode 100644 index 000000000..1039057a7 --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/service/CheckApproverService.java @@ -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); + +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/service/impl/CheckApproverServiceImpl.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/service/impl/CheckApproverServiceImpl.java new file mode 100644 index 000000000..62db9bd4a --- /dev/null +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/listener/task/service/impl/CheckApproverServiceImpl.java @@ -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 optConfig = BpmnMetaParserHelper.getButtonConfig(mainProcess, delegateTask.getTaskDefinitionKey()); + if (!optConfig.isPresent()) { + return exists.get(); + } + BpmnButtonConf bpmnButtonConf = optConfig.get(); + List currentButtons = bpmnButtonConf.getCurrent(); + if (CollectionUtils.isEmpty(currentButtons)) { + return exists.get(); + } + Optional 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; + } +} diff --git a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java index 0a87ef7e1..b50e31172 100644 --- a/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java +++ b/workflow-engine-server/src/main/java/cn/axzo/workflow/server/controller/web/bpmn/BpmnProcessTaskController.java @@ -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 countersignTask(@Validated @RequestBody BpmnTaskCountersignDTO countersignDTO) { - log.info("加签任务 countersignTask===>>>参数:{}", countersignDTO); + log.info("加签任务 countersignTask===>>>参数:{}", JSON.toJSONString(countersignDTO)); bpmnProcessTaskService.countersignTask(countersignDTO); return success(true); }