diff --git a/pom.xml b/pom.xml
index e1922e895e925dd7954b6bed41ff484929121f25..867d32f441b1ff9f6a1f0ad10e06c5051623a4ad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,8 +66,8 @@
2.4.7.Final
2.5.11
-
-
+
+ 4.1.63.Final
@@ -190,6 +190,11 @@
${groovy.version}
pom
+
+ io.netty
+ netty-all
+ ${netty-all.version}
+
diff --git a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/mapper/config/StrategyVoDtoMapper.java b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/mapper/config/StrategyVoDtoMapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..e377e44ea74178dce4e9759b26e322879677fd22
--- /dev/null
+++ b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/mapper/config/StrategyVoDtoMapper.java
@@ -0,0 +1,28 @@
+package cn.icanci.rec.admin.biz.mapper.config;
+
+import org.mapstruct.Mapper;
+import org.mapstruct.NullValueMappingStrategy;
+
+import cn.icanci.rec.admin.biz.mapper.convertor.*;
+import cn.icanci.rec.common.aggregation.model.StrategyDTO;
+import cn.icanci.rec.common.model.config.StrategyVO;
+
+/**
+ * @author icanci
+ * @since 1.0 Created in 2022/11/11 17:49
+ */
+@Mapper(componentModel = "spring", uses = { DataSourceTypeEnumConverter.class, RuleTypeEnumConverter.class, OperatorEnumConverter.class, InterruptEnumConverter.class,
+ ResultTypeEnumConverter.class, RuleModeEnumConverter.class }, nullValueMappingStrategy = NullValueMappingStrategy.RETURN_NULL)
+public interface StrategyVoDtoMapper extends BaseMapper {
+
+ StrategyDTO vo2dto(StrategyVO strategyVO);
+
+ StrategyDTO.RuleListInfo vo2dto(StrategyVO.RuleListInfo ruleListInfo);
+
+ StrategyDTO.RuleTreeInfo vo2dto(StrategyVO.RuleTreeInfo ruleTreeInfo);
+
+ StrategyDTO.Condition vo2dto(StrategyVO.Condition condition);
+
+ StrategyDTO.SingleCondition vo2dto(StrategyVO.SingleCondition singleCondition);
+
+}
diff --git a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/model/StrategyDebugResult.java b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/model/StrategyDebugResult.java
index aded7bcb2b126b19056b1b80895d651bee0f48a3..2b4391fa7dacdf80c1c349d03425f714b7ec315e 100644
--- a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/model/StrategyDebugResult.java
+++ b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/model/StrategyDebugResult.java
@@ -2,10 +2,72 @@ package cn.icanci.rec.admin.biz.model;
import java.io.Serializable;
+import org.apache.commons.lang3.StringUtils;
+
/**
* @author icanci
* @since 1.0 Created in 2022/11/18 22:35
*/
public class StrategyDebugResult implements Serializable {
private static final long serialVersionUID = -5412720512170304475L;
+ /** 是否执行成功 */
+ public boolean success;
+
+ /** 返回值是否是布尔类型 */
+ public boolean retBoolean;
+
+ /** 返回值Fastjson序列化值 */
+ public Object retValue;
+
+ /** 执行异常结果 */
+ public String exceptionMessage;
+
+ /** TODO 实际执行请求参数 */
+ public String executorParam;
+
+ public boolean isRetBoolean() {
+ if (retValue == null) {
+ return false;
+ }
+ if (retValue instanceof Boolean) {
+ return true;
+ }
+ if (retValue instanceof String) {
+ return StringUtils.equalsIgnoreCase(retValue.toString(), Boolean.TRUE.toString()) //
+ || StringUtils.equalsIgnoreCase(retValue.toString(), Boolean.FALSE.toString());
+ }
+ return false;
+ }
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+ public Object getRetValue() {
+ return retValue;
+ }
+
+ public void setRetValue(Object retValue) {
+ this.retValue = retValue;
+ }
+
+ public String getExceptionMessage() {
+ return exceptionMessage;
+ }
+
+ public void setExceptionMessage(String exceptionMessage) {
+ this.exceptionMessage = exceptionMessage;
+ }
+
+ public String getExecutorParam() {
+ return executorParam;
+ }
+
+ public void setExecutorParam(String executorParam) {
+ this.executorParam = executorParam;
+ }
}
diff --git a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/StrategyService.java b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/StrategyService.java
index d98e1b98fc7aea7324d35d878c670100f5726aa1..cd483de5f1114892b40f288095ef58604d36bae9 100644
--- a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/StrategyService.java
+++ b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/StrategyService.java
@@ -23,7 +23,21 @@ public interface StrategyService extends BaseService {
@Override
PageList queryPage(StrategyVO strategyVO, int pageNum, int pageSize);
+ /**
+ * 根据domainCode和strategyName查询
+ *
+ * @param domainCode domainCode
+ * @param strategyName strategyName
+ * @return 返回查询结果
+ */
StrategyVO queryByStrategyName(String domainCode, String strategyName);
+ /**
+ * 对策略进行测试
+ *
+ * @param strategy strategy
+ * @param scriptContentTest 测试参数,当数据源存在且为脚本的时候,此项有值
+ * @return 返回测试结果
+ */
StrategyDebugResult debug(StrategyVO strategy, String scriptContentTest);
}
diff --git a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/impl/StrategyServiceImpl.java b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/impl/StrategyServiceImpl.java
index 50778cd80645df6ca52812871eea610cb068465d..1a7e8ad7fc578e5b694b27c7f724ba7f60090af6 100644
--- a/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/impl/StrategyServiceImpl.java
+++ b/rec-admin/rec-admin-biz/src/main/java/cn/icanci/rec/admin/biz/service/impl/StrategyServiceImpl.java
@@ -2,23 +2,44 @@ package cn.icanci.rec.admin.biz.service.impl;
import cn.icanci.rec.admin.biz.event.log.LogEvent;
import cn.icanci.rec.admin.biz.mapper.config.StrategyMapper;
+import cn.icanci.rec.admin.biz.mapper.config.StrategyVoDtoMapper;
import cn.icanci.rec.admin.biz.model.StrategyDebugResult;
import cn.icanci.rec.admin.biz.service.StrategyService;
+import cn.icanci.rec.admin.biz.service.WebApiService;
import cn.icanci.rec.admin.dal.mongodb.common.PageList;
import cn.icanci.rec.admin.dal.mongodb.daointerface.StrategyDAO;
import cn.icanci.rec.admin.dal.mongodb.dateobject.StrategyDO;
+import cn.icanci.rec.common.aggregation.model.*;
+import cn.icanci.rec.common.enums.DataSourceTypeEnum;
import cn.icanci.rec.common.enums.LogOperatorTypeEnum;
import cn.icanci.rec.common.enums.ModuleTypeEnum;
+import cn.icanci.rec.common.enums.ScriptTypeEnum;
import cn.icanci.rec.common.model.config.StrategyVO;
import cn.icanci.rec.common.utils.FastJsonUtils;
+import cn.icanci.rec.engine.script.RecScriptEngine;
+import cn.icanci.rec.engine.sdk.actuator.RuleEngineRequest;
+import cn.icanci.rec.engine.sdk.actuator.RuleEngineResponse;
+import cn.icanci.rec.engine.sdk.rule.EngineExecutor;
+import cn.icanci.rec.engine.sdk.rule.repository.DomainSceneKey;
+import cn.icanci.rec.engine.sdk.rule.repository.EngineRepositoryHolder;
import cn.icanci.rec.spi.event.EventDispatcher;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import javax.annotation.Resource;
+import javax.script.CompiledScript;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
/**
* @author icanci
* @since 1.0 Created in 2022/11/12 10:38
@@ -26,11 +47,21 @@ import org.springframework.stereotype.Service;
@Service
public class StrategyServiceImpl implements StrategyService {
@Resource
- private StrategyDAO strategyDAO;
+ private StrategyDAO strategyDAO;
+ @Resource
+ private StrategyMapper strategyMapper;
+ @Resource
+ private StrategyVoDtoMapper strategyVoDtoMapper;
+ @Resource
+ private EventDispatcher eventDispatcher;
+ @Resource
+ private EngineRepositoryHolder holder;
+ @Resource
+ private EngineExecutor engineExecutor;
@Resource
- private StrategyMapper strategyMapper;
+ private RecScriptEngine recScriptEngine;
@Resource
- private EventDispatcher eventDispatcher;
+ private WebApiService webApiService;
@Override
public List queryAll() {
@@ -67,7 +98,89 @@ public class StrategyServiceImpl implements StrategyService {
@Override
public StrategyDebugResult debug(StrategyVO strategy, String scriptContentTest) {
+ // 执行结果
+ StrategyDebugResult result = new StrategyDebugResult();
+
+ String domainCode = strategy.getDomainCode();
+ HashSet domainCodes = Sets.newHashSet(domainCode);
+ try {
+ // 刷新仓储
+
+ // 1.获取domain
+ DomainDTO domain = webApiService.loadDomainByDomains(domainCodes).iterator().next();
+ // 2.获取基础数据
+ List baseDatas = webApiService.loadBaseDataByDomains(domainCodes);
+ compileBaseDataScript(baseDatas);
+ Map refreshBaseDatas = baseDatas.stream().collect(Collectors.toMap(BaseDTO::getUuid, baseData -> baseData));
+ // 3.构建元数据信息
+ List metadatas = webApiService.loadMetadataByDomains(domainCodes);
+ Map refreshMetadatas = metadatas.stream().collect(Collectors.toMap(BaseDTO::getUuid, metadata -> metadata));
+ // 4.构建数据源信息
+ List dataSources = webApiService.loadDataSourceByDomains(domainCodes);
+ compileDataSourceScript(dataSources);
+ Map refreshDataSources = dataSources.stream().collect(Collectors.toMap(BaseDTO::getUuid, dataSource -> dataSource));
+ // 5.构建策略信息
+ StrategyDTO strategyDTO = strategyVoDtoMapper.vo2dto(strategy);
+ Map refreshStrategies = Lists.newArrayList(strategyDTO).stream()
+ .collect(Collectors.toMap(x -> new DomainSceneKey(x.getDomainCode(), x.getSceneCode()), s -> s));
+
+ // 6.全部构建成功之后,刷新缓存
+ holder.refresh(domain, refreshBaseDatas, refreshMetadatas, refreshDataSources, refreshStrategies);
+
+ // 7.构建执行参数
+ RuleEngineRequest request = new RuleEngineRequest();
+ request.setDomainCode(domainCode);
+ request.setSceneCode(strategy.getSceneCode());
+ request.setParameters(StringUtils.isNotBlank(scriptContentTest) ? FastJsonUtils.fromJSONString(scriptContentTest, Map.class) : Maps.newHashMap());
+
+ // 8.执行
+ RuleEngineResponse execute = engineExecutor.execute(request);
- return null;
+ // 9.构建执行返回结果
+ result.setSuccess(execute.isSuccess());
+ result.setRetValue(execute.getResult());
+ result.setExceptionMessage(execute.isSuccess() ? StringUtils.EMPTY : execute.getErrorMessage());
+ } catch (Throwable e) {
+ result.setSuccess(false);
+ result.setExceptionMessage(e.getMessage());
+ } finally {
+ // 10.清除仓储
+ holder.clear(domainCodes);
+ }
+ return result;
+ }
+
+ /**
+ * 编译执行脚本
+ *
+ * @param baseDatas baseDatas
+ */
+ private void compileBaseDataScript(List baseDatas) {
+ if (CollectionUtils.isEmpty(baseDatas)) {
+ return;
+ }
+ for (BaseDataDTO baseData : baseDatas) {
+ CompiledScript compiledScript = recScriptEngine.compile(ScriptTypeEnum.valueOf(baseData.getScriptType()), baseData.getScriptContent());
+ baseData.setCompiledScript(compiledScript);
+ }
}
+
+ /**
+ * 编译执行脚本
+ *
+ * @param dataSources dataSources
+ */
+ private void compileDataSourceScript(List dataSources) {
+ if (CollectionUtils.isEmpty(dataSources)) {
+ return;
+ }
+ for (DataSourceDTO dataSource : dataSources) {
+ DataSourceTypeEnum sourceType = DataSourceTypeEnum.valueOf(dataSource.getDataSourceType());
+ if (sourceType == DataSourceTypeEnum.SCRIPT) {
+ DataSourceDTO.ScriptInfo scriptInfo = dataSource.getScriptInfo();
+ scriptInfo.setCompiledScript(recScriptEngine.compile(ScriptTypeEnum.valueOf(scriptInfo.getScriptType()), scriptInfo.getScriptContent()));
+ }
+ }
+ }
+
}
diff --git a/rec-engine/rec-engine-sdk/pom.xml b/rec-engine/rec-engine-sdk/pom.xml
index 663c0b10b0fe11c7a9812b21a86cf1d9e085ee44..7cf35adaa40ec3ae3e6710de57f27b1855de3fd9 100644
--- a/rec-engine/rec-engine-sdk/pom.xml
+++ b/rec-engine/rec-engine-sdk/pom.xml
@@ -60,5 +60,9 @@
com.google.guava
guava
+
+ io.netty
+ netty-all
+
\ No newline at end of file
diff --git a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/repository/EngineRepositoryHolder.java b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/repository/EngineRepositoryHolder.java
index 0122b49c2919476291df2d286aaa19bd3fbabf2d..686af9dfc6acb1b522d7b157ebba8bf6210742b8 100644
--- a/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/repository/EngineRepositoryHolder.java
+++ b/rec-engine/rec-engine-sdk/src/main/java/cn/icanci/rec/engine/sdk/rule/repository/EngineRepositoryHolder.java
@@ -43,7 +43,6 @@ public class EngineRepositoryHolder {
* @param domainCodes domainCodes
*/
public void refresh(Set domainCodes) {
-
if (CollectionUtils.isEmpty(domainCodes)) {
return;
}
@@ -92,7 +91,7 @@ public class EngineRepositoryHolder {
// 3.5 构建策略信息
List strategies = engineRepositoryLoader.loadStrategy(domain);
Map refreshStrategies = strategies.stream()
- .collect(Collectors.toMap(x -> new DomainSceneKey(x.getDomainCode(), x.getSceneCode()), dataSource -> dataSource));
+ .collect(Collectors.toMap(x -> new DomainSceneKey(x.getDomainCode(), x.getSceneCode()), strategy -> strategy));
// 4. 全部构建成功之后,再刷新缓存
repository.setDomainRepository(domainRepository);
@@ -106,6 +105,58 @@ public class EngineRepositoryHolder {
}
+ /**
+ * 本地缓存刷新,提供自行构建方法
+ *
+ * @param domain domain
+ * @param refreshBaseDatas refreshBaseDatas
+ * @param refreshMetadatas refreshMetadatas
+ * @param refreshDataSources refreshDataSources
+ * @param refreshStrategies refreshStrategies
+ */
+ public final void refresh(DomainDTO domain, Map refreshBaseDatas, Map refreshMetadatas, Map refreshDataSources,
+ Map refreshStrategies) {
+ List scenePairs = engineRepositoryLoader.loadScenePairs(domain.getDomainCode());
+ if (CollectionUtils.isEmpty(scenePairs)) {
+ return;
+ }
+ EngineRepository repository = ENGINE_REPOSITORY_CONFIG.putIfAbsent(domain.getDomainCode(), new EngineRepository());
+ repository.setDomainRepository(domain);
+ repository.getBaseDataRepository().putAll(refreshBaseDatas);
+ repository.getMetadataRepository().putAll(refreshMetadatas);
+ repository.getDataSourceRepository().putAll(refreshDataSources);
+ repository.getStrategyRepository().putAll(refreshStrategies);
+ }
+
+ /**
+ * 清除缓存操作
+ *
+ * @param domainCodes domainCodes
+ */
+ public void clear(Set domainCodes) {
+ if (CollectionUtils.isEmpty(domainCodes)) {
+ return;
+ }
+ for (String domainCode : domainCodes) {
+ clear(domainCode);
+ }
+ }
+
+ /**
+ * 仓储清除
+ *
+ * @param domain domain
+ */
+ private void clear(String domain) {
+ // 1.先获取场景,如果场景都没有,则不处理
+ List scenePairs = engineRepositoryLoader.loadScenePairs(domain);
+ if (CollectionUtils.isEmpty(scenePairs)) {
+ return;
+ }
+ // 2.移除仓储
+ ENGINE_REPOSITORY_CONFIG.remove(domain);
+ }
+
/**
* 编译执行脚本
*