diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java b/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java index 1332639d..797f5e9d 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/action/actions/RejectAction.java @@ -7,7 +7,8 @@ import com.codingapi.flow.context.RepositoryHolderContext; import com.codingapi.flow.event.FlowRecordTodoEvent; import com.codingapi.flow.event.IFlowEvent; -import com.codingapi.flow.exception.FlowConfigException; +import com.codingapi.flow.exception.FlowStateException; +import com.codingapi.flow.exception.FlowValidationException; import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.record.FlowRecord; import com.codingapi.flow.script.action.RejectActionScript; @@ -76,7 +77,7 @@ public List generateRecords(FlowSession flowSession) { currentNode = flowSession.getWorkflow().getEndNode(); } if (currentNode == null) { - throw FlowConfigException.currentNodeNotNull(); + throw FlowStateException.currentNodeNotNull(); } flowSession = flowSession.updateSession(currentNode); return currentNode.generateCurrentRecords(flowSession); diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/context/RepositoryHolderContext.java b/flow-engine-framework/src/main/java/com/codingapi/flow/context/RepositoryHolderContext.java index 9a5c1aa1..2730c415 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/context/RepositoryHolderContext.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/context/RepositoryHolderContext.java @@ -1,7 +1,7 @@ package com.codingapi.flow.context; import com.codingapi.flow.domain.DelayTask; -import com.codingapi.flow.exception.FlowConfigException; +import com.codingapi.flow.exception.FlowStateException; import com.codingapi.flow.gateway.FlowOperatorGateway; import com.codingapi.flow.operator.IFlowOperator; import com.codingapi.flow.record.FlowRecord; @@ -53,7 +53,7 @@ public boolean isRegistered() { public void verify() { if (!isRegistered()) { - throw FlowConfigException.repositoryNotRegistered(); + throw FlowStateException.repositoryNotRegistered(); } } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowConfigException.java b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowConfigException.java deleted file mode 100644 index 8bf6ba1c..00000000 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowConfigException.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.codingapi.flow.exception; - -/** - * Flow configuration exception - *

- * Thrown when the flow configuration is incorrect - * For example: node configuration error, edge configuration error, strategy configuration error, etc. - * - * @since 1.0.0 - */ -public class FlowConfigException extends FlowException { - - private static final long serialVersionUID = 1L; - - /** - * Constructor - * - * @param code error code - * @param message error message - */ - public FlowConfigException(String code, String message) { - super(code, message); - } - - /** - * Constructor - * - * @param code error code - * @param message error message - * @param cause cause - */ - public FlowConfigException(String code, String message, Throwable cause) { - super(code, message, cause); - } - - /** - * Strategies cannot be null - * - * @return exception - */ - public static FlowConfigException strategiesNotNull() { - return new FlowConfigException("config.node.strategies.required", "Strategies cannot be null"); - } - - /** - * Actions cannot be null - * - * @return exception - */ - public static FlowConfigException actionsNotNull() { - return new FlowConfigException("config.node.actions.required", "Actions cannot be null"); - } - - /** - * Node configuration error - * - * @param nodeName node name - * @param reason reason - * @return exception - */ - public static FlowConfigException nodeConfigError(String nodeName, String reason) { - return new FlowConfigException("config.node.error", - String.format("Node '%s' configuration error: %s", nodeName, reason)); - } - - /** - * Edge configuration error - * - * @param reason reason - * @return exception - */ - public static FlowConfigException edgeConfigError(String reason) { - return new FlowConfigException("config.edge.error", String.format("Edge configuration error: %s", reason)); - } - - /** - * Form configuration error - * - * @param formName form name - * @param reason reason - * @return exception - */ - public static FlowConfigException formConfigError(String formName, String reason) { - return new FlowConfigException("config.form.error", - String.format("Form '%s' configuration error: %s", formName, reason)); - } - - /** - * View cannot be null - * - * @return exception - */ - public static FlowConfigException viewNotNull() { - return new FlowConfigException("config.view.required", "View cannot be null"); - } - - /** - * Parallel end node cannot be null - * - * @return exception - */ - public static FlowConfigException parallelEndNodeNotNull() { - return new FlowConfigException("config.parallel.endNode.required", "Parallel end node cannot be null"); - } - - /** - * Current node cannot be null - * - * @return exception - */ - public static FlowConfigException currentNodeNotNull() { - return new FlowConfigException("config.node.current.required", "Current node cannot be null"); - } - - /** - * Router node script is null - * - * @return exception - */ - public static FlowConfigException routerNodeScriptNull() { - return new FlowConfigException("config.router.script.required", "Router node script cannot be null"); - } - - /** - * Repository not registered - * - * @return exception - */ - public static FlowConfigException repositoryNotRegistered() { - return new FlowConfigException("config.repository.notRegistered", "Flow repository components not registered"); - } - - /** - * Error throw cannot be null - * - * @return exception - */ - public static FlowConfigException errorThrowNotNull() { - return new FlowConfigException("config.errorThrow.required", "Error throw cannot be null"); - } -} diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowException.java b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowException.java index 45610694..3f130853 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowException.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowException.java @@ -1,5 +1,7 @@ package com.codingapi.flow.exception; +import com.codingapi.springboot.framework.exception.LocaleMessageException; + /** * 流程引擎框架异常基类 *

@@ -7,14 +9,7 @@ * * @since 1.0.0 */ -public abstract class FlowException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - /** - * 错误码 - */ - private final String errorCode; +public abstract class FlowException extends LocaleMessageException { /** * 构造函数 @@ -23,8 +18,7 @@ public abstract class FlowException extends RuntimeException { * @param message 错误信息 */ public FlowException(String errorCode, String message) { - super(message); - this.errorCode = errorCode; + super(errorCode,message); } /** @@ -35,21 +29,7 @@ public FlowException(String errorCode, String message) { * @param cause 原因 */ public FlowException(String errorCode, String message, Throwable cause) { - super(message, cause); - this.errorCode = errorCode; + super(errorCode,message, cause); } - /** - * 获取错误码 - * - * @return 错误码 - */ - public String getErrorCode() { - return errorCode; - } - - @Override - public String toString() { - return String.format("[%s] %s", errorCode, getMessage()); - } } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowExecutionException.java b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowExecutionException.java index d0ef9b21..9c1676e8 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowExecutionException.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowExecutionException.java @@ -10,8 +10,6 @@ */ public class FlowExecutionException extends FlowException { - private static final long serialVersionUID = 1L; - /** * Constructor * @@ -45,54 +43,6 @@ public static FlowExecutionException scriptExecutionError(String scriptType, Thr String.format("Script execution error: %s", scriptType), cause); } - /** - * Node execution error - * - * @param nodeId node ID - * @param cause cause - * @return exception - */ - public static FlowExecutionException nodeExecutionError(String nodeId, Throwable cause) { - return new FlowExecutionException("execution.node.error", - String.format("Node execution error: %s", nodeId), cause); - } - - /** - * Action execution error - * - * @param actionId action ID - * @param cause cause - * @return exception - */ - public static FlowExecutionException actionExecutionError(String actionId, Throwable cause) { - return new FlowExecutionException("execution.action.error", - String.format("Action execution error: %s", actionId), cause); - } - - /** - * Workflow execution error - * - * @param processId process instance ID - * @param cause cause - * @return exception - */ - public static FlowExecutionException workflowExecutionError(String processId, Throwable cause) { - return new FlowExecutionException("execution.workflow.error", - String.format("Workflow execution error: %s", processId), cause); - } - - /** - * Database operation error - * - * @param operation operation type - * @param cause cause - * @return exception - */ - public static FlowExecutionException databaseError(String operation, Throwable cause) { - return new FlowExecutionException("execution.database.error", - String.format("Database operation error: %s", operation), cause); - } - /** * Router node not found * diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowNotFoundException.java b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowNotFoundException.java index c8af1238..020414f6 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowNotFoundException.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowNotFoundException.java @@ -10,7 +10,6 @@ */ public class FlowNotFoundException extends FlowException { - private static final long serialVersionUID = 1L; /** * Constructor @@ -23,14 +22,12 @@ public FlowNotFoundException(String code, String message) { } /** - * Constructor + * Parallel end node cannot be null * - * @param code error code - * @param message error message - * @param cause cause + * @return exception */ - public FlowNotFoundException(String code, String message, Throwable cause) { - super(code, message, cause); + public static FlowNotFoundException parallelEndNodeNotNull() { + return new FlowNotFoundException("notFound.parallel.endNode.required", "Parallel end node cannot be null"); } /** diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowPermissionException.java b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowPermissionException.java index 97fec56f..94bca088 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowPermissionException.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowPermissionException.java @@ -10,8 +10,6 @@ */ public class FlowPermissionException extends FlowException { - private static final long serialVersionUID = 1L; - /** * Constructor * @@ -22,38 +20,7 @@ public FlowPermissionException(String code, String message) { super(code, message); } - /** - * Constructor - * - * @param code error code - * @param message error message - * @param cause cause - */ - public FlowPermissionException(String code, String message, Throwable cause) { - super(code, message, cause); - } - - /** - * Field is read-only - * - * @param fieldName field name - * @return exception - */ - public static FlowPermissionException fieldReadOnly(String fieldName) { - return new FlowPermissionException("permission.field.readOnly", - String.format("Field '%s' is read-only and cannot be modified", fieldName)); - } - /** - * Field not found - * - * @param fieldName field name - * @return exception - */ - public static FlowPermissionException fieldNotFound(String fieldName) { - return new FlowPermissionException("permission.field.notFound", - String.format("Field '%s' does not exist", fieldName)); - } /** * Access denied @@ -66,14 +33,4 @@ public static FlowPermissionException accessDenied(String operation) { String.format("Access denied to operation: %s", operation)); } - /** - * No permission to operate the flow - * - * @param operatorId operator ID - * @return exception - */ - public static FlowPermissionException noPermission(long operatorId) { - return new FlowPermissionException("permission.access.noPermission", - String.format("Operator %d has no permission to access this resource", operatorId)); - } } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowStateException.java b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowStateException.java index 107ccedb..3ab896c8 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowStateException.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowStateException.java @@ -10,7 +10,6 @@ */ public class FlowStateException extends FlowException { - private static final long serialVersionUID = 1L; /** * Constructor @@ -23,14 +22,31 @@ public FlowStateException(String code, String message) { } /** - * Constructor + * Repository not registered * - * @param code error code - * @param message error message - * @param cause cause + * @return exception + */ + public static FlowStateException repositoryNotRegistered() { + return new FlowStateException("state.repository.notRegistered", "Flow repository components not registered"); + } + + /** + * Current node cannot be null + * + * @return exception */ - public FlowStateException(String code, String message, Throwable cause) { - super(code, message, cause); + public static FlowStateException currentNodeNotNull() { + return new FlowStateException("state.node.current.required", "Current node cannot be null"); + } + + /** + * Edge configuration error + * + * @param reason reason + * @return exception + */ + public static FlowStateException edgeConfigError(String reason) { + return new FlowStateException("state.edge.error", String.format("Edge configuration error: %s", reason)); } /** @@ -62,10 +78,10 @@ public static FlowStateException operatorNotMatch() { } - /** * * Workflow is already disable + * * @param workflowId workflowId * @return exception */ @@ -74,37 +90,6 @@ public static FlowStateException workflowAlreadyDisable(String workflowId) { String.format("Workflow is disable: %s", workflowId)); } - /** - * Workflow is already finished - * - * @return exception - */ - public static FlowStateException workflowAlreadyFinished() { - return new FlowStateException("state.workflow.alreadyFinished", "Workflow is finished, further operation not allowed"); - } - - /** - * Workflow is already terminated - * - * @return exception - */ - public static FlowStateException workflowAlreadyTerminated() { - return new FlowStateException("state.workflow.alreadyTerminated", "Workflow is terminated, further operation not allowed"); - } - - /** - * Invalid state transition - * - * @param fromState current state - * @param toState target state - * @return exception - */ - public static FlowStateException invalidStateTransition(String fromState, String toState) { - return new FlowStateException("state.transition.invalid", - String.format("Invalid state transition: from '%s' to '%s'", fromState, toState)); - } - - /** * Record not support revoke * diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowValidationException.java b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowValidationException.java index 393288b3..f4c83c48 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowValidationException.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/exception/FlowValidationException.java @@ -10,7 +10,6 @@ */ public class FlowValidationException extends FlowException { - private static final long serialVersionUID = 1L; /** * Constructor @@ -23,36 +22,59 @@ public FlowValidationException(String code, String message) { } /** - * Constructor + * node field is empty * - * @param code error code - * @param message error message - * @param cause cause + * @param fieldName node field name + * @return exception */ - public FlowValidationException(String code, String message, Throwable cause) { - super(code, message, cause); + public static FlowValidationException nodeRequired(String fieldName) { + return new FlowValidationException("validation.node."+fieldName, + String.format("Required field %s cannot be empty", fieldName)); } + /** - * Required field is empty + * Field is read-only * * @param fieldName field name * @return exception */ - public static FlowValidationException required(String fieldName) { - return new FlowValidationException("validation.field.required", + public static FlowValidationException fieldReadOnly(String fieldName) { + return new FlowValidationException("validation.field.readOnly", + String.format("Field '%s' is read-only and cannot be modified", fieldName)); + } + + /** + * Field not found + * + * @param fieldName field name + * @return exception + */ + public static FlowValidationException fieldNotFound(String fieldName) { + return new FlowValidationException("validation.field.notFound", + String.format("Field '%s' does not exist", fieldName)); + } + + /** + * workflow field is empty + * + * @param fieldName node field name + * @return exception + */ + public static FlowValidationException workflowRequired(String fieldName) { + return new FlowValidationException("validation.workflow."+fieldName, String.format("Required field %s cannot be empty", fieldName)); } /** - * Field value cannot be empty + * Required field is empty * * @param fieldName field name * @return exception */ - public static FlowValidationException notEmpty(String fieldName) { - return new FlowValidationException("validation.field.notEmpty", - String.format("Field %s value cannot be empty", fieldName)); + public static FlowValidationException required(String fieldName) { + return new FlowValidationException("validation.field.required", + String.format("Required field %s cannot be empty", fieldName)); } /** diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/manager/FlowNodeManager.java b/flow-engine-framework/src/main/java/com/codingapi/flow/manager/FlowNodeManager.java index 5cda052e..92b91f60 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/manager/FlowNodeManager.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/manager/FlowNodeManager.java @@ -1,6 +1,6 @@ package com.codingapi.flow.manager; -import com.codingapi.flow.exception.FlowConfigException; +import com.codingapi.flow.exception.FlowStateException; import com.codingapi.flow.node.IFlowNode; import java.util.ArrayList; @@ -99,7 +99,7 @@ private List loadNextNodes(FlowNodeState current, Iterator strategies; - public NodeStrategyManager(List strategies) { this.strategies = strategies; - if (this.strategies == null) { - throw FlowConfigException.strategiesNotNull(); - } } /** @@ -165,6 +160,7 @@ public OperatorManager loadOperators(FlowSession session) { } public void verifySession(FlowSession session) { + FlowAdvice flowAdvice = session.getAdvice(); IFlowAction flowAction = flowAdvice.getAction(); // 是否必须填写审批意见 @@ -182,6 +178,10 @@ public void verifySession(FlowSession session) { } } } + + for (INodeStrategy strategy : strategies){ + strategy.verifySession(session); + } } @SuppressWarnings("unchecked") diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseAuditNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseAuditNode.java index 8e102801..d953ed66 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseAuditNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseAuditNode.java @@ -2,7 +2,7 @@ import com.codingapi.flow.action.IFlowAction; import com.codingapi.flow.error.ErrorThrow; -import com.codingapi.flow.exception.FlowConfigException; +import com.codingapi.flow.exception.FlowValidationException; import com.codingapi.flow.form.FormMeta; import com.codingapi.flow.manager.NodeStrategyManager; import com.codingapi.flow.manager.OperatorManager; @@ -49,7 +49,7 @@ public Map toMap() { public void verifyNode(FormMeta form) { super.verifyNode(form); if (!StringUtils.hasText(view)) { - throw FlowConfigException.viewNotNull(); + throw FlowValidationException.nodeRequired("view"); } } @@ -121,7 +121,7 @@ public List generateCurrentRecords(FlowSession session) { if(operatorManager.isEmpty()){ ErrorThrow errorThrow = nodeStrategyManager.errorTrigger(session); if(errorThrow==null){ - throw FlowConfigException.errorThrowNotNull(); + throw FlowValidationException.nodeRequired("errorTrigger"); } if(errorThrow.isNode()){ IFlowNode errorNode = errorThrow.getNode(); diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java index a1f3905e..c3d933bd 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/BaseFlowNode.java @@ -5,7 +5,7 @@ import com.codingapi.flow.builder.NodeMapBuilder; import com.codingapi.flow.common.IMapConvertor; import com.codingapi.flow.context.RepositoryHolderContext; -import com.codingapi.flow.exception.FlowConfigException; +import com.codingapi.flow.exception.FlowValidationException; import com.codingapi.flow.form.FormMeta; import com.codingapi.flow.manager.ActionManager; import com.codingapi.flow.manager.FlowNodeState; @@ -184,23 +184,23 @@ public void verifyNode(FormMeta form) { private void verifyDefaultConfig() { if (!StringUtils.hasText(name)) { - throw FlowConfigException.nodeConfigError(name, "name can not be null"); + throw FlowValidationException.nodeRequired("name"); } if (!StringUtils.hasText(id)) { - throw FlowConfigException.nodeConfigError(id, "id can not be null"); + throw FlowValidationException.nodeRequired("id"); } if (actions == null) { - throw FlowConfigException.actionsNotNull(); + throw FlowValidationException.nodeRequired("action"); } if (strategies == null) { - throw FlowConfigException.strategiesNotNull(); + throw FlowValidationException.nodeRequired("strategies"); } FlowNodeState nodeState = new FlowNodeState(this); if (nodeState.isBlockNode() || nodeState.isBranchNode()) { List blocks = this.blocks(); if (blocks == null || blocks.isEmpty()) { - throw FlowConfigException.nodeConfigError(id, "blocks can not be null"); + throw FlowValidationException.nodeRequired("blocks"); } } } @@ -225,8 +225,15 @@ public boolean isWaitRecordMargeParallelNode(FlowSession session) { } + /** + * 匹配条件 + */ @Override - public boolean handle(FlowSession session) { + public boolean handle(FlowSession request) { + // 是否等待并行合并节点 + if (this.isWaitRecordMargeParallelNode(request)) { + return false; + } return true; } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/IFlowNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/IFlowNode.java index b8e5dd09..6803c623 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/IFlowNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/IFlowNode.java @@ -16,7 +16,7 @@ * 1. 流程的调用第一步将会执行 {@link com.codingapi.flow.action.IFlowAction#run(FlowSession)} 函数。
* 2. 在{@link com.codingapi.flow.action.IFlowAction#run(FlowSession)} 中流程将需要判断当前流程{@link IFlowNode#isDone(FlowSession)} 是否已经办理完成。
* 3. 流程办理完成后将会分析流程对的下一节点对象 {@link com.codingapi.flow.action.BaseAction#triggerNode(FlowSession, Consumer)} ()},将递归掉分析执行下一节点
- * 4. 在获取下一节点对象时,将会访问当节点的拦截策略 {@link IFlowNode#filterBranches(List, FlowSession)},该函数将根据节点的配置进行匹配下一节点。
+ * 4. 在获取下一节点对象时,将会访问节点的过滤策略 {@link IFlowNode#filterBranches(List, FlowSession)},该函数将根据节点的配置进行过滤匹配下一节点。
* 5. 获取到下一节点对象后,则会访问流程节点的 {@link IFlowNode#handle(FlowSession)} 函数分析流程是否继续执行。当函数返回true时则会继续循环调用匹配下一节点的逻辑,即triggerNode的递归逻辑。
* 6. 当{@link IFlowNode#handle(FlowSession)} 返回的是false时,则停止继续下一节点流程,开始执行当前节点的生成流程记录函数 {@link IFlowNode#generateCurrentRecords(FlowSession)}
* 7. 在构建出先的流程记录数据以后,在数据保存时还将会触发节点对流程记录对象的填充函数 {@link IFlowNode#fillNewRecord(FlowSession, FlowRecord)}
@@ -112,7 +112,7 @@ public interface IFlowNode extends IMapConvertor { /** - * 过滤条件分支 + * 过滤节点 * * @param nodeList 当前节点下的所有条件 * @param flowSession 当前会话 diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/helper/ParallelNodeRelationHelper.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/helper/ParallelNodeRelationHelper.java index a0adb14b..8385902a 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/helper/ParallelNodeRelationHelper.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/helper/ParallelNodeRelationHelper.java @@ -1,105 +1,54 @@ package com.codingapi.flow.node.helper; import com.codingapi.flow.node.IFlowNode; -import com.codingapi.flow.node.nodes.EndNode; import com.codingapi.flow.workflow.Workflow; -import lombok.AllArgsConstructor; -import lombok.Getter; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +/** + * 并行节点需要在最后的合并的节点上统一触发下一流程,因此需要分析最终汇聚的节点 + *

+ */ public class ParallelNodeRelationHelper { private final Workflow workflow; + private final IFlowNode currentNode; private final List parallelNodes; - public ParallelNodeRelationHelper(List parallelNodes, Workflow workflow) { + public ParallelNodeRelationHelper(Workflow workflow, IFlowNode currentNode, List parallelNodes) { this.parallelNodes = parallelNodes; + this.currentNode = currentNode; this.workflow = workflow; } - public IFlowNode fetchParallelEndNode() { + /** + * 获取合并节点 + */ + public IFlowNode fetchMargeNode() { if (parallelNodes.isEmpty()) { return null; } - if (parallelNodes.size() > 1) { - ParallelNodeRelationHelper.LineManager lineManager = new ParallelNodeRelationHelper.LineManager(); - for (IFlowNode node : parallelNodes) { - List nodeLines = this.getNodeLines(node); - lineManager.addLine(nodeLines); - } - return lineManager.fetchEndNode(workflow); - } - return parallelNodes.get(0); - } - private List getNodeLines(IFlowNode node) { - List lines = new ArrayList<>(); - lines.add(node.getId()); - IFlowNode currentNode = node; - ParallelNodeRelationHelper.NodeManger nodeManger = null; - do { - nodeManger = this.nextNodes(currentNode); - currentNode = nodeManger.getCurrentNode(); - lines.add(currentNode.getId()); - } while (nodeManger.next()); - return lines; - } - - private ParallelNodeRelationHelper.NodeManger nextNodes(IFlowNode node) { - return new ParallelNodeRelationHelper.NodeManger(workflow.nextNodes(node)); - } - - private static class LineManager { - - private final List> lines = new ArrayList<>(); - - public void addLine(List line) { - lines.add(line); + IFlowNode overBlockNode = loadOverBlockNodes(); + List nextNodes = workflow.nextNodes(overBlockNode); + if (nextNodes != null && nextNodes.size() == 1) { + return nextNodes.get(0); } - - - public IFlowNode fetchEndNode(Workflow workflow) { - // 对线进行倒叙 - List firstLine = lines.get(0); - Collections.reverse(firstLine); - - IFlowNode flowNode = null; - for (int i = 1; i < lines.size(); i++) { - List line = lines.get(i); - if (flowNode == null) { - for (String nodeId : firstLine) { - if (line.contains(nodeId)) { - flowNode = workflow.getFlowNode(nodeId); - } - } - } - } - return flowNode; - } - + return null; } - @AllArgsConstructor - private static class NodeManger { - @Getter - private final List nodes; - - public IFlowNode getCurrentNode() { - return nodes.get(0); - } - - public boolean next() { - if (nodes.isEmpty()) { - return false; - } - IFlowNode currentNode = nodes.get(0); - if (currentNode instanceof EndNode) { - return false; - } - return true; + /** + * 获取最后的block节点 + */ + public IFlowNode loadOverBlockNodes() { + IFlowNode node = currentNode; + List blocks = node.blocks(); + while (blocks != null && !blocks.isEmpty()) { + int size = blocks.size(); + node = blocks.get(size - 1); + blocks = node.blocks(); } + return node; } + } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ConditionNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ConditionNode.java index 5d906c4a..3a6865c8 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ConditionNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ConditionNode.java @@ -5,11 +5,9 @@ import com.codingapi.flow.node.IBlockNode; import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.node.NodeType; -import com.codingapi.flow.session.FlowSession; import com.codingapi.flow.utils.RandomUtils; import java.util.ArrayList; -import java.util.Comparator; import java.util.List; import java.util.Map; @@ -34,30 +32,6 @@ public ConditionNode() { this(RandomUtils.generateStringId(), DEFAULT_NAME, 0); } - /** - * 匹配条件 - */ - @Override - public boolean handle(FlowSession request) { - return true; - } - - @Override - public List filterBranches(List nodeList, FlowSession flowSession) { - List nodes = new ArrayList<>(); - for (IFlowNode node : nodeList) { - if (node.handle(flowSession)) { - nodes.add(node); - } - } - // 获取最小order的节点 - nodes.sort(Comparator.comparingInt(IFlowNode::getOrder)); - if (!nodes.isEmpty()) { - return nodes.subList(0, 1); - } - return nodes; - } - @Override public void addDefaultBranch(int count){ List branches = new ArrayList<>(); diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/DelayNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/DelayNode.java index b82ea73e..8a816711 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/DelayNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/DelayNode.java @@ -31,12 +31,15 @@ public String getType() { @Override public boolean handle(FlowSession session) { - NodeStrategyManager nodeStrategyManager = this.strategyManager(); - DelayStrategy delayStrategy = nodeStrategyManager.getStrategy(DelayStrategy.class); - if (delayStrategy != null) { - FlowRecord currentRecord = session.getCurrentRecord(); - DelayTask delayTask = new DelayTask(delayStrategy, currentRecord, this.getId()); - DelayTaskManager.getInstance().addTask(delayTask); + if(super.handle(session)) { + NodeStrategyManager nodeStrategyManager = this.strategyManager(); + DelayStrategy delayStrategy = nodeStrategyManager.getStrategy(DelayStrategy.class); + if (delayStrategy != null) { + FlowRecord currentRecord = session.getCurrentRecord(); + DelayTask delayTask = new DelayTask(delayStrategy, currentRecord, this.getId()); + DelayTaskManager.getInstance().addTask(delayTask); + } + return false; } return false; } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveBranchNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveBranchNode.java index 2b8f1fe5..097a1693 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveBranchNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveBranchNode.java @@ -1,7 +1,7 @@ package com.codingapi.flow.node.nodes; import com.codingapi.flow.builder.BaseNodeBuilder; -import com.codingapi.flow.exception.FlowConfigException; +import com.codingapi.flow.exception.FlowNotFoundException; import com.codingapi.flow.node.BaseFlowNode; import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.node.NodeType; @@ -76,11 +76,12 @@ public static InclusiveBranchNode formMap(Map map) { */ public List filterBranches(List nodeList, FlowSession flowSession) { Workflow workflow = flowSession.getWorkflow(); - ParallelNodeRelationHelper helper = new ParallelNodeRelationHelper(nodeList, workflow); + IFlowNode currentNode = flowSession.getCurrentNode(); + ParallelNodeRelationHelper helper = new ParallelNodeRelationHelper(workflow,currentNode,nodeList); // 分析并行分支的结束汇聚节点 - IFlowNode overNode = helper.fetchParallelEndNode(); + IFlowNode overNode = helper.fetchMargeNode(); if (overNode == null) { - throw FlowConfigException.parallelEndNodeNotNull(); + throw FlowNotFoundException.parallelEndNodeNotNull(); } // 在流程记录中记录,合并的条件信息。 diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveNode.java index d6c5fc1f..b582cf3b 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/InclusiveNode.java @@ -1,16 +1,11 @@ package com.codingapi.flow.node.nodes; import com.codingapi.flow.builder.BaseNodeBuilder; -import com.codingapi.flow.exception.FlowConfigException; import com.codingapi.flow.node.BaseFlowNode; import com.codingapi.flow.node.IBlockNode; import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.node.NodeType; -import com.codingapi.flow.node.helper.ParallelNodeRelationHelper; -import com.codingapi.flow.record.FlowRecord; -import com.codingapi.flow.session.FlowSession; import com.codingapi.flow.utils.RandomUtils; -import com.codingapi.flow.workflow.Workflow; import java.util.ArrayList; import java.util.List; @@ -39,14 +34,6 @@ public InclusiveNode() { this(RandomUtils.generateStringId(), DEFAULT_NAME, 0); } - /** - * 匹配条件 - */ - @Override - public boolean handle(FlowSession request) { - return true; - } - @Override public void addDefaultBranch(int count){ @@ -63,28 +50,6 @@ public static InclusiveNode formMap(Map map) { return BaseFlowNode.fromMap(map, InclusiveNode.class); } - /** - * 匹配条件分支 - * - * @param nodeList 当前节点下的所有条件 - * @param flowSession 当前会话 - * @return 匹配的节点 - */ - public List filterBranches(List nodeList, FlowSession flowSession) { - Workflow workflow = flowSession.getWorkflow(); - ParallelNodeRelationHelper helper = new ParallelNodeRelationHelper(nodeList, workflow); - // 分析并行分支的结束汇聚节点 - IFlowNode overNode = helper.fetchParallelEndNode(); - if (overNode == null) { - throw FlowConfigException.parallelEndNodeNotNull(); - } - - // 在流程记录中记录,合并的条件信息。 - FlowRecord flowRecord = flowSession.getCurrentRecord(); - flowRecord.parallelBranchNode(overNode.getId(), nodeList.size(), RandomUtils.generateStringId()); - - return nodeList; - } public static Builder builder() { return new Builder(); diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/NotifyNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/NotifyNode.java index fa389792..1f095a9c 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/NotifyNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/NotifyNode.java @@ -40,6 +40,9 @@ public NotifyNode() { @Override public boolean handle(FlowSession session) { + if (this.isWaitRecordMargeParallelNode(session)) { + return false; + } List records = this.generateCurrentRecords(session); for (FlowRecord record : records) { this.fillNewRecord(session, record); diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelBranchNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelBranchNode.java index 520fe524..795ea22b 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelBranchNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelBranchNode.java @@ -1,7 +1,7 @@ package com.codingapi.flow.node.nodes; import com.codingapi.flow.builder.BaseNodeBuilder; -import com.codingapi.flow.exception.FlowConfigException; +import com.codingapi.flow.exception.FlowNotFoundException; import com.codingapi.flow.node.BaseFlowNode; import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.node.NodeType; @@ -35,6 +35,11 @@ public ParallelBranchNode() { this(RandomUtils.generateStringId(), DEFAULT_NAME); } + @Override + public boolean handle(FlowSession request) { + return true; + } + /** * 匹配条件分支 * @@ -44,11 +49,12 @@ public ParallelBranchNode() { */ public List filterBranches(List nodeList, FlowSession flowSession) { Workflow workflow = flowSession.getWorkflow(); - ParallelNodeRelationHelper helper = new ParallelNodeRelationHelper(nodeList, workflow); + IFlowNode currentNode = flowSession.getCurrentNode(); + ParallelNodeRelationHelper helper = new ParallelNodeRelationHelper(workflow, currentNode, nodeList); // 分析并行分支的结束汇聚节点 - IFlowNode overNode = helper.fetchParallelEndNode(); + IFlowNode overNode = helper.fetchMargeNode(); if (overNode == null) { - throw FlowConfigException.parallelEndNodeNotNull(); + throw FlowNotFoundException.parallelEndNodeNotNull(); } // 在流程记录中记录,合并的条件信息。 diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelNode.java index ee2423f3..b9d7df23 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/ParallelNode.java @@ -1,16 +1,11 @@ package com.codingapi.flow.node.nodes; import com.codingapi.flow.builder.BaseNodeBuilder; -import com.codingapi.flow.exception.FlowConfigException; import com.codingapi.flow.node.BaseFlowNode; import com.codingapi.flow.node.IBlockNode; import com.codingapi.flow.node.IFlowNode; import com.codingapi.flow.node.NodeType; -import com.codingapi.flow.node.helper.ParallelNodeRelationHelper; -import com.codingapi.flow.record.FlowRecord; -import com.codingapi.flow.session.FlowSession; import com.codingapi.flow.utils.RandomUtils; -import com.codingapi.flow.workflow.Workflow; import java.util.ArrayList; import java.util.List; @@ -37,28 +32,6 @@ public ParallelNode() { this(RandomUtils.generateStringId(), DEFAULT_NAME); } - /** - * 匹配条件分支 - * - * @param nodeList 当前节点下的所有条件 - * @param flowSession 当前会话 - * @return 匹配的节点 - */ - public List filterBranches(List nodeList, FlowSession flowSession) { - Workflow workflow = flowSession.getWorkflow(); - ParallelNodeRelationHelper helper = new ParallelNodeRelationHelper(nodeList, workflow); - // 分析并行分支的结束汇聚节点 - IFlowNode overNode = helper.fetchParallelEndNode(); - if (overNode == null) { - throw FlowConfigException.parallelEndNodeNotNull(); - } - - // 在流程记录中记录,合并的条件信息。 - FlowRecord flowRecord = flowSession.getCurrentRecord(); - flowRecord.parallelBranchNode(overNode.getId(), nodeList.size(), RandomUtils.generateStringId()); - - return nodeList; - } @Override public void addDefaultBranch(int count){ diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/StartNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/StartNode.java index ff40d5bb..06c24ecb 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/StartNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/StartNode.java @@ -65,6 +65,7 @@ public List generateCurrentRecords(FlowSession session) { IFlowOperator operator = session.getCurrentOperator(); if (currentRecord == null) { FlowRecord flowRecord = new FlowRecord(session.updateSession(operator), 0); + session.setCurrentRecord(flowRecord); records.add(flowRecord); } else { // 获取流程创建者 diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/SubProcessNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/SubProcessNode.java index 513a680f..c05a2e4b 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/SubProcessNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/SubProcessNode.java @@ -43,10 +43,13 @@ private static List defaultStrategies() { @Override public boolean handle(FlowSession session) { - NodeStrategyManager nodeStrategyManager = this.strategyManager(); - SubProcessStrategy processStrategy = nodeStrategyManager.getStrategy(SubProcessStrategy.class); - processStrategy.execute(session); - return true; + if(super.handle(session)) { + NodeStrategyManager nodeStrategyManager = this.strategyManager(); + SubProcessStrategy processStrategy = nodeStrategyManager.getStrategy(SubProcessStrategy.class); + processStrategy.execute(session); + return true; + } + return false; } public static SubProcessNode formMap(Map map) { diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/TriggerNode.java b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/TriggerNode.java index e99b5b42..e145b0d4 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/TriggerNode.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/node/nodes/TriggerNode.java @@ -43,10 +43,13 @@ private static List defaultStrategies() { @Override public boolean handle(FlowSession session) { - NodeStrategyManager nodeStrategyManager = this.strategyManager(); - TriggerStrategy triggerStrategy = nodeStrategyManager.getStrategy(TriggerStrategy.class); - triggerStrategy.execute(session); - return true; + if(super.handle(session)) { + NodeStrategyManager nodeStrategyManager = this.strategyManager(); + TriggerStrategy triggerStrategy = nodeStrategyManager.getStrategy(TriggerStrategy.class); + triggerStrategy.execute(session); + return true; + } + return false; } public static TriggerNode formMap(Map map) { diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowCreateService.java b/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowCreateService.java index b5a51763..30092672 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowCreateService.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/service/impl/FlowCreateService.java @@ -67,10 +67,10 @@ public long create() { IFlowAction action = currentNode.actionManager().getActionById(request.getActionId()); FlowSession session = FlowSession.startSession(currentOperator, workflow, currentNode, action, formData, workflowBackup.getId()); - currentNode.verifySession(session); - List flowRecords = currentNode.generateCurrentRecords(session); + currentNode.verifySession(session); + if (flowRecords.size() > 1) { throw FlowExecutionException.createRecordSizeError(); } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/session/FlowSession.java b/flow-engine-framework/src/main/java/com/codingapi/flow/session/FlowSession.java index 08fc02be..d59a4453 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/session/FlowSession.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/session/FlowSession.java @@ -10,7 +10,9 @@ import com.codingapi.flow.record.FlowRecord; import com.codingapi.flow.workflow.Workflow; import lombok.Getter; +import lombok.Setter; +import java.util.ArrayList; import java.util.List; /** @@ -40,7 +42,8 @@ public class FlowSession { /** * 当前审批流程记录 */ - private final FlowRecord currentRecord; + @Setter + private FlowRecord currentRecord; /** * 当前节点的流程记录 @@ -114,7 +117,7 @@ public static FlowSession startSession(IFlowOperator currentOperator, IFlowAction currentAction, FormData formData, long backupId) { - return new FlowSession(currentOperator, workflow, currentNode, currentAction, formData, null, null, backupId, new FlowAdvice()); + return new FlowSession(currentOperator, workflow, currentNode, currentAction, formData, null, new ArrayList<>(), backupId, new FlowAdvice()); } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java b/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java index 56caaf79..ca36936d 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/strategy/node/FormFieldPermissionStrategy.java @@ -2,8 +2,7 @@ import com.codingapi.flow.builder.NodeMapBuilder; import com.codingapi.flow.common.IMapConvertor; -import com.codingapi.flow.exception.FlowConfigException; -import com.codingapi.flow.exception.FlowPermissionException; +import com.codingapi.flow.exception.FlowValidationException; import com.codingapi.flow.form.FormMeta; import com.codingapi.flow.form.permission.FormFieldPermission; import com.codingapi.flow.form.permission.PermissionType; @@ -47,7 +46,7 @@ public void verifyNode(FormMeta form) { for (FormFieldPermission permission : fieldPermissions) { String key = permission.getFormCode() + "." + permission.getFieldCode(); if (!fieldTypes.containsKey(key)) { - throw FlowPermissionException.fieldNotFound(key); + throw FlowValidationException.fieldNotFound(key); } } } @@ -64,11 +63,11 @@ public void verifySession(FlowSession session) { List> currentSubFormData = (List>) currentData.get(permission.getFormCode()); List> latestSubFormData = (List>) latestData.get(permission.getFormCode()); if (currentSubFormData == null || latestSubFormData == null) { - throw FlowConfigException.formConfigError(permission.getFormCode(), "is not a sub form"); + throw FlowValidationException.nodeRequired("form"); } if (currentSubFormData.size() != latestSubFormData.size()) { - throw FlowConfigException.formConfigError(permission.getFormCode(), "size is not equal"); + throw FlowValidationException.nodeRequired("form"); } for (int i = 0; i < currentSubFormData.size(); i++) { @@ -77,7 +76,7 @@ public void verifySession(FlowSession session) { Object currentValue = currentSubFormItem.get(permission.getFieldCode()); Object latestValue = latestSubFormItem.get(permission.getFieldCode()); if (!currentValue.equals(latestValue)) { - throw FlowPermissionException.fieldReadOnly(permission.getFieldCode()); + throw FlowValidationException.fieldReadOnly(permission.getFieldCode()); } } } @@ -87,7 +86,7 @@ public void verifySession(FlowSession session) { Object currentValue = currentData.get(permission.getFieldCode()); Object latestValue = latestData.get(permission.getFieldCode()); if (!currentValue.equals(latestValue)) { - throw FlowPermissionException.fieldReadOnly(permission.getFieldCode()); + throw FlowValidationException.fieldReadOnly(permission.getFieldCode()); } } } diff --git a/flow-engine-framework/src/main/java/com/codingapi/flow/workflow/Workflow.java b/flow-engine-framework/src/main/java/com/codingapi/flow/workflow/Workflow.java index c4f288f6..4574cc23 100644 --- a/flow-engine-framework/src/main/java/com/codingapi/flow/workflow/Workflow.java +++ b/flow-engine-framework/src/main/java/com/codingapi/flow/workflow/Workflow.java @@ -2,7 +2,6 @@ import com.alibaba.fastjson.JSON; import com.codingapi.flow.context.GatewayContext; -import com.codingapi.flow.exception.FlowConfigException; import com.codingapi.flow.exception.FlowValidationException; import com.codingapi.flow.form.FormMeta; import com.codingapi.flow.manager.FlowNodeManager; @@ -289,22 +288,22 @@ private void verifyFields() { throw FlowValidationException.required("workflow id"); } if (!StringUtils.hasText(code)) { - throw FlowValidationException.required("workflow code"); + throw FlowValidationException.workflowRequired("code"); } if (!StringUtils.hasText(title)) { - throw FlowValidationException.required("workflow title"); + throw FlowValidationException.workflowRequired("title"); } if (createdTime <= 0) { - throw FlowValidationException.required("workflow createdTime"); + throw FlowValidationException.workflowRequired("createdTime"); } if (form == null) { - throw FlowValidationException.required("workflow form"); + throw FlowValidationException.workflowRequired("form"); } if (createdOperator == null) { - throw FlowValidationException.required("workflow createdOperator"); + throw FlowValidationException.workflowRequired("createdOperator"); } if (nodes == null || nodes.isEmpty()) { - throw FlowConfigException.nodeConfigError(title, "nodes can not be null"); + throw FlowValidationException.workflowRequired( "nodes"); } } @@ -330,7 +329,11 @@ private void verifyNodes() { } } if (start != 1 || end != 1) { - throw FlowConfigException.nodeConfigError(title, "must have one start node and one end node"); + if(start!=1) { + throw FlowValidationException.nodeRequired("startNode"); + }else { + throw FlowValidationException.nodeRequired("endNode"); + } } for (IFlowNode node : nodes) { diff --git a/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowParallelServiceTest.java b/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowParallelServiceTest.java new file mode 100644 index 00000000..39f971c7 --- /dev/null +++ b/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowParallelServiceTest.java @@ -0,0 +1,316 @@ +package com.codingapi.flow.service; + +import com.codingapi.flow.action.IFlowAction; +import com.codingapi.flow.builder.FormFieldPermissionsBuilder; +import com.codingapi.flow.builder.NodeStrategyBuilder; +import com.codingapi.flow.context.GatewayContext; +import com.codingapi.flow.form.FormMeta; +import com.codingapi.flow.form.FormMetaBuilder; +import com.codingapi.flow.form.permission.PermissionType; +import com.codingapi.flow.gateway.impl.UserGateway; +import com.codingapi.flow.node.IFlowNode; +import com.codingapi.flow.node.nodes.*; +import com.codingapi.flow.pojo.body.FlowAdviceBody; +import com.codingapi.flow.pojo.request.FlowActionRequest; +import com.codingapi.flow.pojo.request.FlowCreateRequest; +import com.codingapi.flow.record.FlowRecord; +import com.codingapi.flow.repository.*; +import com.codingapi.flow.strategy.node.FormFieldPermissionStrategy; +import com.codingapi.flow.strategy.node.OperatorLoadStrategy; +import com.codingapi.flow.user.User; +import com.codingapi.flow.workflow.Workflow; +import com.codingapi.flow.workflow.WorkflowBuilder; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class FlowParallelServiceTest { + + private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); + private final UserGateway userGateway = new UserGateway(); + private final WorkflowBackupRepository workflowBackupRepository = new WorkflowBackupRepositoryImpl(); + private final WorkflowRepository workflowRepository = new WorkflowRepositoryImpl(); + private final ParallelBranchRepository parallelBranchRepository = new ParallelBranchRepositoryImpl(); + private final DelayTaskRepository delayTaskRepository = new DelayTaskRepositoryImpl(); + private final UrgeIntervalRepository urgeIntervalRepository = new UrgeIntervalRepositoryImpl(); + private final FlowService flowService = new FlowService(workflowRepository, userGateway, flowRecordRepository, workflowBackupRepository, parallelBranchRepository, delayTaskRepository, urgeIntervalRepository); + + + /** + * 并行分支测试 + */ + @Test + void parallelAndParallel() { + + User user = new User(1, "user"); + User depart = new User(2, "depart"); + User boss = new User(3, "boss"); + userGateway.save(user); + userGateway.save(depart); + userGateway.save(boss); + + GatewayContext.getInstance().setFlowOperatorGateway(userGateway); + + FormMeta form = FormMetaBuilder.builder() + .name("请假流程") + .code("leave") + .addField("请假人", "name", "string") + .addField("请假天数", "days", "int") + .addField("请假事由", "reason", "string") + .build(); + + StartNode startNode = StartNode + .builder() + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .build()) + .build(); + + + ApprovalNode departApprovalNode1 = ApprovalNode.builder() + .name("经理审批") + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .addStrategy(new OperatorLoadStrategy("def run(request){return [$bind.getOperatorById(2)]}")) + .build() + ) + .build(); + + ApprovalNode bossApprovalNode1 = ApprovalNode.builder() + .name("老板审批") + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .addStrategy(new OperatorLoadStrategy("def run(request){return [$bind.getOperatorById(3)]}")) + .build() + ) + .build(); + + ApprovalNode bigBossApprovalNode1 = ApprovalNode.builder() + .name("大老板审批") + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.READ) + .addPermission("leave", "days", PermissionType.READ) + .addPermission("leave", "reason", PermissionType.READ) + .build())) + .addStrategy(new OperatorLoadStrategy("def run(request){return [$bind.getOperatorById(3)]}")) + .build() + ) + .build(); + + + ParallelBranchNode parallelBranchNode11 = ParallelBranchNode.builder() + .name("并行分支1") + .blocks(departApprovalNode1) + .order(1) + .build(); + + ParallelBranchNode parallelBranchNode12 = ParallelBranchNode.builder() + .name("并行分支2") + .blocks(bossApprovalNode1, bigBossApprovalNode1) + .order(2) + .build(); + + ParallelNode parallelNode1 = ParallelNode.builder() + .name("并行控制节点") + .blocks(parallelBranchNode11, parallelBranchNode12) + .build(); + + + ApprovalNode departApprovalNode2 = ApprovalNode.builder() + .name("经理审批") + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .addStrategy(new OperatorLoadStrategy("def run(request){return [$bind.getOperatorById(2)]}")) + .build() + ) + .build(); + + ApprovalNode bossApprovalNode2 = ApprovalNode.builder() + .name("老板审批") + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .addStrategy(new OperatorLoadStrategy("def run(request){return [$bind.getOperatorById(3)]}")) + .build() + ) + .build(); + + ApprovalNode bigBossApprovalNode2 = ApprovalNode.builder() + .name("大老板审批") + .strategies(NodeStrategyBuilder.builder() + .addStrategy(new FormFieldPermissionStrategy(FormFieldPermissionsBuilder.builder() + .addPermission("leave", "name", PermissionType.WRITE) + .addPermission("leave", "days", PermissionType.WRITE) + .addPermission("leave", "reason", PermissionType.WRITE) + .build())) + .addStrategy(new OperatorLoadStrategy("def run(request){return [$bind.getOperatorById(3)]}")) + .build() + ) + .build(); + + + ParallelBranchNode parallelBranchNode21 = ParallelBranchNode.builder() + .name("并行分支1") + .blocks(departApprovalNode2) + .order(1) + .build(); + + ParallelBranchNode parallelBranchNode22 = ParallelBranchNode.builder() + .name("并行分支2") + .blocks(bossApprovalNode2, bigBossApprovalNode2) + .order(2) + .build(); + + ParallelNode parallelNode2 = ParallelNode.builder() + .name("并行控制节点") + .blocks(parallelBranchNode21, parallelBranchNode22) + .build(); + + EndNode endNode = EndNode.builder().build(); + Workflow workflow = WorkflowBuilder.builder() + .title("请假流程") + .code("leave") + .createdOperator(user) + .form(form) + .addNode(startNode) + .addNode(parallelNode1) + .addNode(parallelNode2) + .addNode(endNode) + .build(); + + workflowRepository.save(workflow); + + + List nextNodes = workflow.nextNodes(bossApprovalNode1); + assertEquals(1, nextNodes.size()); + assertEquals(bigBossApprovalNode1, nextNodes.get(0)); + + + Map data = Map.of("name", "lorne", "days", 3, "reason", "leave"); + + List startActions = startNode.actionManager().getActions(); + + FlowCreateRequest userCreateRequest = new FlowCreateRequest(); + userCreateRequest.setWorkId(workflow.getId()); + userCreateRequest.setFormData(data); + userCreateRequest.setActionId(startActions.get(0).id()); + userCreateRequest.setOperatorId(user.getUserId()); + + flowService.create(userCreateRequest); + + List userRecordList = flowRecordRepository.findTodoByOperator(user.getUserId()); + assertEquals(1, userRecordList.size()); + + FlowActionRequest userRequest = new FlowActionRequest(); + userRequest.setFormData(data); + userRequest.setRecordId(userRecordList.get(0).getId()); + userRequest.setAdvice(new FlowAdviceBody(startActions.get(0).id(), "同意", user.getUserId())); + flowService.action(userRequest); + + List departRecordList = flowRecordRepository.findTodoByOperator(depart.getUserId()); + assertEquals(1, departRecordList.size()); + + List boosRecordList = flowRecordRepository.findTodoByOperator(boss.getUserId()); + assertEquals(1, boosRecordList.size()); + + List departActions = departApprovalNode1.actionManager().getActions(); + + FlowActionRequest departRequest = new FlowActionRequest(); + departRequest.setFormData(data); + departRequest.setRecordId(departRecordList.get(0).getId()); + departRequest.setAdvice(new FlowAdviceBody(departActions.get(0).id(), "同意", depart.getUserId())); + flowService.action(departRequest); + + boosRecordList = flowRecordRepository.findTodoByOperator(boss.getUserId()); + assertEquals(1, boosRecordList.size()); + + List bossActions = bossApprovalNode1.actionManager().getActions(); + + FlowActionRequest dossRequest = new FlowActionRequest(); + dossRequest.setFormData(data); + dossRequest.setRecordId(boosRecordList.get(0).getId()); + dossRequest.setAdvice(new FlowAdviceBody(bossActions.get(0).id(), "同意", boss.getUserId())); + flowService.action(dossRequest); + + + boosRecordList = flowRecordRepository.findTodoByOperator(boss.getUserId()); + assertEquals(1, boosRecordList.size()); + + List bigBossActions = bigBossApprovalNode1.actionManager().getActions(); + + FlowActionRequest bigBossRequest = new FlowActionRequest(); + bigBossRequest.setFormData(data); + bigBossRequest.setRecordId(boosRecordList.get(0).getId()); + bigBossRequest.setAdvice(new FlowAdviceBody(bigBossActions.get(0).id(), "同意", boss.getUserId())); + flowService.action(bigBossRequest); + + departRecordList = flowRecordRepository.findTodoByOperator(depart.getUserId()); + assertEquals(1, departRecordList.size()); + + boosRecordList = flowRecordRepository.findTodoByOperator(boss.getUserId()); + assertEquals(1, boosRecordList.size()); + + + departActions = departApprovalNode2.actionManager().getActions(); + + departRequest = new FlowActionRequest(); + departRequest.setFormData(data); + departRequest.setRecordId(departRecordList.get(0).getId()); + departRequest.setAdvice(new FlowAdviceBody(departActions.get(0).id(), "同意", depart.getUserId())); + flowService.action(departRequest); + + boosRecordList = flowRecordRepository.findTodoByOperator(boss.getUserId()); + assertEquals(1, boosRecordList.size()); + + + bossActions = bossApprovalNode2.actionManager().getActions(); + + dossRequest = new FlowActionRequest(); + dossRequest.setFormData(data); + dossRequest.setRecordId(boosRecordList.get(0).getId()); + dossRequest.setAdvice(new FlowAdviceBody(bossActions.get(0).id(), "同意", boss.getUserId())); + flowService.action(dossRequest); + + + boosRecordList = flowRecordRepository.findTodoByOperator(boss.getUserId()); + assertEquals(1, boosRecordList.size()); + + bigBossActions = bigBossApprovalNode2.actionManager().getActions(); + + bigBossRequest = new FlowActionRequest(); + bigBossRequest.setFormData(data); + bigBossRequest.setRecordId(boosRecordList.get(0).getId()); + bigBossRequest.setAdvice(new FlowAdviceBody(bigBossActions.get(0).id(), "同意", boss.getUserId())); + flowService.action(bigBossRequest); + + + List records = flowRecordRepository.findProcessRecords(departRecordList.get(0).getProcessId()); + assertEquals(8, records.size()); + assertEquals(0, records.stream().filter(FlowRecord::isTodo).toList().size()); + assertEquals(8, records.stream().filter(FlowRecord::isFinish).toList().size()); + + } +} \ No newline at end of file diff --git a/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowServiceTest.java b/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowSampleServiceTest.java similarity index 99% rename from flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowServiceTest.java rename to flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowSampleServiceTest.java index 535b8143..dfe33a60 100644 --- a/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowServiceTest.java +++ b/flow-engine-framework/src/test/java/com/codingapi/flow/service/FlowSampleServiceTest.java @@ -35,7 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -class FlowServiceTest { +class FlowSampleServiceTest { private final FlowRecordRepositoryImpl flowRecordRepository = new FlowRecordRepositoryImpl(); private final UserGateway userGateway = new UserGateway();