diff --git a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/daointerface/RegisterDAO.java b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/daointerface/RegisterDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..fb8fd07e1700aaba490c7df4751b6013dc809fe8 --- /dev/null +++ b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/daointerface/RegisterDAO.java @@ -0,0 +1,27 @@ +package cn.icanci.rec.admin.dal.mongodb.daointerface; + +import cn.icanci.rec.admin.dal.mongodb.dateobject.RegisterDO; + +/** + * TODO 注册中心 + * + * @author icanci + * @since 1.0 Created in 2022/11/17 19:02 + */ +public interface RegisterDAO extends BaseDAO { + /** + * 文档对应的名字 + */ + String COLLECTION_NAME = "rec-register"; + /** + * 文档对应的Class + */ + Class COLLECTION_CLASS = RegisterDO.class; + + interface SceneColumn extends BaseColumn { + /** + * 域Code + */ + String domainCode = "domainCode"; + } +} diff --git a/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/dateobject/RegisterDO.java b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/dateobject/RegisterDO.java new file mode 100644 index 0000000000000000000000000000000000000000..7c59af34554aca1d865114096da93e7c243b7961 --- /dev/null +++ b/rec-admin/rec-admin-dal/src/main/java/cn/icanci/rec/admin/dal/mongodb/dateobject/RegisterDO.java @@ -0,0 +1,12 @@ +package cn.icanci.rec.admin.dal.mongodb.dateobject; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 19:03 + */ +public class RegisterDO extends BaseDO { + /** + * 注册到哪个域上 + */ + private String domainCode; +} diff --git a/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/StrategyDTO.java b/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/StrategyDTO.java index 6dd284ec218487d8f1e1107bdab64a041db48840..6c96d15c471a43c8666891174603f8c5a398a3ba 100644 --- a/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/StrategyDTO.java +++ b/rec-common/src/main/java/cn/icanci/rec/common/aggregation/model/StrategyDTO.java @@ -288,14 +288,14 @@ public class StrategyDTO extends BaseDTO { /** * 多个conditions为或的关系 */ - private List conditions; + private List> conditionGroups; - public List getConditions() { - return conditions; + public List> getConditionGroups() { + return conditionGroups; } - public void setConditions(List conditions) { - this.conditions = conditions; + public void setConditionGroups(List> conditionGroups) { + this.conditionGroups = conditionGroups; } } } diff --git a/rec-engine/rec-engine-script/src/test/java/cn/icanci/rec/engine/script/test/RecScriptEngineManagerTest.java b/rec-engine/rec-engine-script/src/test/java/cn/icanci/rec/engine/script/test/RecScriptEngineManagerTest.java index eb8f6e9b1e3d7875c6a8271d3fc79c780d0f8212..dbf6fa5de3a1cb8932fba80e9c0422bec100c897 100644 --- a/rec-engine/rec-engine-script/src/test/java/cn/icanci/rec/engine/script/test/RecScriptEngineManagerTest.java +++ b/rec-engine/rec-engine-script/src/test/java/cn/icanci/rec/engine/script/test/RecScriptEngineManagerTest.java @@ -9,6 +9,8 @@ import cn.icanci.rec.engine.script.context.RecScriptEngineContext; import cn.icanci.rec.engine.script.wrapper.HttpResponseWrapper; import java.io.Serializable; +import java.util.Arrays; +import java.util.Map; import org.junit.Test; @@ -54,6 +56,20 @@ public class RecScriptEngineManagerTest { System.out.println(Boolean.parseBoolean("false")); } + @Test + public void testMvel2() { + String script = "return {\"Jim\", \"Bob\", \"Smith\"}"; + RecScriptEngine recScriptEngine = RecScriptEngineManager.getRecScriptEngine(); + RecScriptEngineContext context = recScriptEngine.eval(ScriptTypeEnum.MVEL2, script, Object.class); + System.out.println(context.getRealRetVal()); + System.out.println(context.getRealRetVal().getClass().isArray()); + Object[] arr = (Object[]) context.getRealRetVal(); + for (Object o : Arrays.asList(arr)) { + System.out.println(o); + } + System.out.println(context.getRealRetVal() instanceof Map); + } + private static class ConfigRequest implements Serializable { private static final long serialVersionUID = 2380811183737977291L; diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/ContainCondition.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/ContainCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..e8d4ab380edf38be70689a464e4cb3356addc756 --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/ContainCondition.java @@ -0,0 +1,21 @@ +package cn.icanci.rec.engine.sdk.condition; + +import cn.icanci.rec.common.aggregation.model.StrategyDTO; +import cn.icanci.rec.common.enums.OperatorEnum; + +import javax.script.Bindings; + +import org.springframework.stereotype.Component; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 09:24 + */ +@Component +@ConditionBean(OperatorEnum.CONTAIN) +public class ContainCondition extends AbstractCondition { + @Override + public boolean match(Bindings bindings, StrategyDTO.SingleCondition singleCondition, String domainCode) { + return false; + } +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/GTECondition.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/GTECondition.java new file mode 100644 index 0000000000000000000000000000000000000000..0243b193fecfab8a90c7895c655b02afe85b8718 --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/GTECondition.java @@ -0,0 +1,22 @@ +package cn.icanci.rec.engine.sdk.condition; + +import cn.icanci.rec.common.aggregation.model.StrategyDTO; +import cn.icanci.rec.common.enums.OperatorEnum; + +import javax.script.Bindings; + +import org.springframework.stereotype.Component; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 09:22 + */ +@Component +@ConditionBean(OperatorEnum.GTE) +public class GTECondition extends AbstractCondition { + + @Override + public boolean match(Bindings bindings, StrategyDTO.SingleCondition singleCondition, String domainCode) { + return false; + } +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/IncludedCondition.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/IncludedCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..994f4fa0cd1e564164d067b141a98105b62cf30f --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/IncludedCondition.java @@ -0,0 +1,22 @@ +package cn.icanci.rec.engine.sdk.condition; + +import cn.icanci.rec.common.aggregation.model.StrategyDTO; +import cn.icanci.rec.common.enums.OperatorEnum; + +import javax.script.Bindings; + +import org.springframework.stereotype.Component; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 09:24 + */ +@Component +@ConditionBean(OperatorEnum.INCLUDED) +public class IncludedCondition extends AbstractCondition { + + @Override + public boolean match(Bindings bindings, StrategyDTO.SingleCondition singleCondition, String domainCode) { + return false; + } +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/LTCondition.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/LTCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..3b6d916bf4e0d7a18c64c3ce6458a75d45ad3f0e --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/LTCondition.java @@ -0,0 +1,22 @@ +package cn.icanci.rec.engine.sdk.condition; + +import cn.icanci.rec.common.aggregation.model.StrategyDTO; +import cn.icanci.rec.common.enums.OperatorEnum; + +import javax.script.Bindings; + +import org.springframework.stereotype.Component; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/16 23:08 + */ +@Component +@ConditionBean(OperatorEnum.LT) +public class LTCondition extends AbstractCondition { + + @Override + public boolean match(Bindings bindings, StrategyDTO.SingleCondition singleCondition, String domainCode) { + return false; + } +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/LTECondition.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/LTECondition.java new file mode 100644 index 0000000000000000000000000000000000000000..13807f4f03b229140cce6ed33c7bd09f31c0789d --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/LTECondition.java @@ -0,0 +1,22 @@ +package cn.icanci.rec.engine.sdk.condition; + +import cn.icanci.rec.common.aggregation.model.StrategyDTO; +import cn.icanci.rec.common.enums.OperatorEnum; + +import javax.script.Bindings; + +import org.springframework.stereotype.Component; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 09:22 + */ +@Component +@ConditionBean(OperatorEnum.LTE) +public class LTECondition extends AbstractCondition { + + @Override + public boolean match(Bindings bindings, StrategyDTO.SingleCondition singleCondition, String domainCode) { + return false; + } +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/NECondition.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/NECondition.java new file mode 100644 index 0000000000000000000000000000000000000000..da9d6d684ee083080ad99dd1b06375229a6aa4af --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/NECondition.java @@ -0,0 +1,21 @@ +package cn.icanci.rec.engine.sdk.condition; + +import cn.icanci.rec.common.aggregation.model.StrategyDTO; +import cn.icanci.rec.common.enums.OperatorEnum; + +import javax.script.Bindings; + +import org.springframework.stereotype.Component; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 09:23 + */ +@Component +@ConditionBean(OperatorEnum.NE) +public class NECondition extends AbstractCondition { + @Override + public boolean match(Bindings bindings, StrategyDTO.SingleCondition singleCondition, String domainCode) { + return false; + } +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/UnContainCondition.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/UnContainCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..3c3cf558ff391e177fd065307d784b9d96f6bc6e --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/condition/UnContainCondition.java @@ -0,0 +1,21 @@ +package cn.icanci.rec.engine.sdk.condition; + +import cn.icanci.rec.common.aggregation.model.StrategyDTO; +import cn.icanci.rec.common.enums.OperatorEnum; + +import javax.script.Bindings; + +import org.springframework.stereotype.Component; + +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 09:24 + */ +@Component +@ConditionBean(OperatorEnum.UN_CONTAIN) +public class UnContainCondition extends AbstractCondition { + @Override + public boolean match(Bindings bindings, StrategyDTO.SingleCondition singleCondition, String domainCode) { + return false; + } +} diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineConfig.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineConfigHolder.java similarity index 79% rename from rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineConfig.java rename to rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineConfigHolder.java index 94aedf766b23806feeaadc844529c983dec75ed4..7934cd016329d726a4c13eba55d3ba54fe3ccc94 100644 --- a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineConfig.java +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineConfigHolder.java @@ -6,6 +6,6 @@ package cn.icanci.rec.engine.sdk.context; * @author icanci * @since 1.0 Created in 2022/11/13 15:31 */ -public class RecRuleEngineConfig { +public class RecRuleEngineConfigHolder { } diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineContext.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineContext.java index e48fbc99f789f929b429e6a4daa2642a248f4abf..9d5ada6834126670981d7c0e96822765d278b0ad 100644 --- a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineContext.java +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/context/RecRuleEngineContext.java @@ -7,5 +7,12 @@ package cn.icanci.rec.engine.sdk.context; * @since 1.0 Created in 2022/11/13 15:31 */ public class RecRuleEngineContext { + // 域、场景、策略、元数据 + private String domainName; + private String domainCode; + private String sceneName; + private String sceneCode; + private String strategyName; + // 执行日志等等 } diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java index 92b97a83dfc29cb5f04e6b3449013ae7270d1c60..22e2747ed3681e417c6678ab7de9f9af43036fe4 100644 --- a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/EngineExecutor.java @@ -10,6 +10,7 @@ import cn.icanci.rec.engine.script.context.RecScriptEngineContext; import cn.icanci.rec.engine.script.wrapper.HttpResponseWrapper; import cn.icanci.rec.engine.sdk.actuator.RuleEngineRequest; import cn.icanci.rec.engine.sdk.actuator.RuleEngineResponse; +import cn.icanci.rec.engine.sdk.condition.ConditionSupport; import cn.icanci.rec.engine.sdk.exception.ValidatorException; import cn.icanci.rec.engine.sdk.rule.pool.ScriptExecutorPoolHolder; import cn.icanci.rec.engine.sdk.rule.repository.EngineRepositoryHolder; @@ -24,6 +25,7 @@ import javax.script.CompiledScript; import javax.script.ScriptException; import javax.script.SimpleBindings; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; /** @@ -41,6 +43,9 @@ public final class EngineExecutor { @Resource private ScriptExecutorPoolHolder scriptExecutorPoolHolder; + @Resource + private ConditionSupport conditionSupport; + /** * 执行入口 * @@ -74,10 +79,10 @@ public final class EngineExecutor { RuleModeEnum ruleMode = RuleModeEnum.valueOf(strategy.getRuleMode()); switch (ruleType) { case LIST: - executorResult = ruleListExecutor(strategy.getRuleListInfo(), ruleMode, bindings); + executorResult = ruleListExecutor(strategy.getDomainCode(), strategy.getRuleListInfo(), ruleMode, bindings); break; case TREE: - executorResult = ruleTreeExecutor(strategy.getRuleTreeInfo(), ruleMode, bindings); + executorResult = ruleTreeExecutor(strategy.getDomainCode(), strategy.getRuleTreeInfo(), ruleMode, bindings); break; default: // no op @@ -92,12 +97,13 @@ public final class EngineExecutor { /** * List 执行 * + * @param domainCode domainCode * @param ruleListInfo ruleListInfo * @param ruleMode ruleMode * @param bindings bindings * @return 返回执行结果 */ - private Object ruleListExecutor(StrategyDTO.RuleListInfo ruleListInfo, RuleModeEnum ruleMode, Bindings bindings) { + private Object ruleListExecutor(String domainCode, StrategyDTO.RuleListInfo ruleListInfo, RuleModeEnum ruleMode, Bindings bindings) { // List 配置模式 // [condition1 ---------] ----------| @@ -114,35 +120,100 @@ public final class EngineExecutor { // condition1 和 condition2 是或的关系 // condition1 中 sc1.1 和 sc1.2 是且的关系 // condition2 中 sc2.1、sc2.2、sc2.3 是且的关系 + // 如果没一组的最后一个cell具备孩子节点,则继续执行孩子节点 // 判断执行模式,简单还是复杂 List conditions = ruleListInfo.getConditions(); - for (StrategyDTO.Condition condition : conditions) { - // 组内 - List group = condition.getGroup(); - for (StrategyDTO.SingleCondition sc : group) { - String name = sc.getName(); - - } - } - return null; + // 模式执行 + return doConditionsRun(domainCode, ruleMode, bindings, conditions); } /** * Tree 执行 * + * @param domainCode domainCode * @param ruleTreeInfo ruleTreeInfo * @param ruleMode ruleMode - * @param mergeMap mergeMap + * @param bindings bindings * @return 返回执行结果 */ - private Object ruleTreeExecutor(StrategyDTO.RuleTreeInfo ruleTreeInfo, RuleModeEnum ruleMode, Map mergeMap) { - List conditions = ruleTreeInfo.getConditions(); + private Object ruleTreeExecutor(String domainCode, StrategyDTO.RuleTreeInfo ruleTreeInfo, RuleModeEnum ruleMode, Bindings bindings) { + List> conditionGroups = ruleTreeInfo.getConditionGroups(); + for (List conditionGroup : conditionGroups) { + // 单元组执行 + Object ret = doConditionsRun(domainCode, ruleMode, bindings, conditionGroup); + // 返回值是否是boolean + boolean isBoolean = ret instanceof Boolean; + // 如果不是布尔值,说明命中中断,返回中断 + if (!isBoolean) { + return ret; + } else if (((Boolean) ret)) { + // 是布尔值并且为true,说符合全组条件,返回 + return true; + } + } + // 一个都没执行到,返回false + return false; + } + + /** + * 模式执行 + * + * @param domainCode domainCode + * @param ruleMode ruleMode + * @param bindings bindings + * @param conditions conditions + * @return 返回执行结果 + */ + private Object doConditionsRun(String domainCode, RuleModeEnum ruleMode, Bindings bindings, List conditions) { for (StrategyDTO.Condition condition : conditions) { - List group = condition.getGroup(); + // 组内 + Object ret = matchGroup(condition.getGroup(), domainCode, ruleMode, bindings); + // 返回值是否是boolean + boolean isBoolean = ret instanceof Boolean; + // 如果不是布尔值,说明命中中断,返回中断 + if (!isBoolean) { + return ret; + } else if (((Boolean) ret)) { + // 是布尔值并且为true,说符合全组条件,返回 + return true; + } + } + // 一个都没执行到,返回false + return false; + } + /** + * 是否命中此组 + * + * @param group group + * @param domainCode domainCode + * @param ruleMode ruleMode + * @param bindings bindings + * @return 返回是否命中或者中断结果 + */ + private Object matchGroup(List group, String domainCode, RuleModeEnum ruleMode, Bindings bindings) { + if (CollectionUtils.isEmpty(group)) { + return Boolean.FALSE; + } + for (StrategyDTO.SingleCondition sc : group) { + OperatorEnum operator = OperatorEnum.valueOf(sc.getOperator()); + boolean match = conditionSupport.getCondition(operator).match(bindings, sc, domainCode); + // 命中中断条件的返回值 + if (match && ruleMode == RuleModeEnum.COMPLEX && InterruptEnum.valueOf(sc.getInterrupt()) == InterruptEnum.TRUE) { + // 执行中断 + return sc.getReturnVal(); + } + if (!match) { + return Boolean.FALSE; + } + } + StrategyDTO.SingleCondition last = group.get(group.size() - 1); + if (CollectionUtils.isEmpty(last.getChildren())) { + return Boolean.TRUE; } - return null; + // 执行孩子节点 + return matchGroup(last.getChildren(), domainCode, ruleMode, bindings); } /** diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/mapper/package-info.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/mapper/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..2b1a9a5c6acb591b29d515ca1b09c31de9845d63 --- /dev/null +++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/mapper/package-info.java @@ -0,0 +1,5 @@ +/** + * @author icanci + * @since 1.0 Created in 2022/11/17 23:17 + */ +package cn.icanci.rec.engine.sdk.rule.mapper; \ No newline at end of file