|
|
@@ -0,0 +1,171 @@
|
|
|
+package cn.iocoder.yudao.module.iscs.controller.admin.rule;
|
|
|
+
|
|
|
+import cn.iocoder.yudao.module.iscs.dal.dataobject.rule.RuleDefinitionsDO;
|
|
|
+import cn.iocoder.yudao.module.iscs.service.rule.RuleDefinitionsService;
|
|
|
+import org.kie.api.KieServices;
|
|
|
+import org.kie.api.builder.KieBuilder;
|
|
|
+import org.kie.api.builder.KieFileSystem;
|
|
|
+import org.kie.api.builder.KieModule;
|
|
|
+import org.kie.api.builder.Message.Level;
|
|
|
+import org.kie.api.builder.Results;
|
|
|
+import org.kie.api.runtime.KieContainer;
|
|
|
+import org.kie.internal.io.ResourceFactory;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.atomic.AtomicReference;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class DynamicRuleManager {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RuleDefinitionsService ruleDefinitionsService;
|
|
|
+
|
|
|
+ // 原子引用存储当前KieContainer(线程安全)
|
|
|
+ private final AtomicReference<KieContainer> currentKieContainer = new AtomicReference<>();
|
|
|
+
|
|
|
+ // 记录每条规则的当前版本(key:规则唯一标识,如名称或ID;value:版本号)
|
|
|
+ private Map<String, Integer> ruleVersionMap = new HashMap<>();
|
|
|
+
|
|
|
+ public DynamicRuleManager(RuleDefinitionsService ruleDefinitionsService) {
|
|
|
+ this.ruleDefinitionsService = ruleDefinitionsService;
|
|
|
+ // 初始化时加载所有规则
|
|
|
+ loadRuleFromDb();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从数据库加载所有规则并更新KieContainer
|
|
|
+ */
|
|
|
+ public void loadRuleFromDb() {
|
|
|
+ System.out.println("开始加载所有drools规则");
|
|
|
+ try {
|
|
|
+ // 1. 查询所有需要加载的规则(从Service获取多条规则)
|
|
|
+ List<RuleDefinitionsDO> allRules = ruleDefinitionsService.getAllValidRules();
|
|
|
+ if (allRules.isEmpty()) {
|
|
|
+ throw new RuntimeException("数据库中未找到任何有效规则");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 检查是否有规则需要更新(版本变化、新增或删除)
|
|
|
+ if (!isNeedUpdate(allRules)) {
|
|
|
+ System.out.println("所有规则版本未更新,无需重新加载");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 构建KieFileSystem,批量写入所有规则
|
|
|
+ KieServices kieServices = KieServices.Factory.get();
|
|
|
+ KieFileSystem kfs = kieServices.newKieFileSystem();
|
|
|
+
|
|
|
+ for (RuleDefinitionsDO rule : allRules) {
|
|
|
+ String ruleName = rule.getName(); // 规则唯一标识(假设名称唯一)
|
|
|
+ String drlContent = rule.getContent(); // 规则内容
|
|
|
+
|
|
|
+ // 校验规则内容
|
|
|
+ if (drlContent == null || drlContent.trim().isEmpty()) {
|
|
|
+ throw new RuntimeException("规则[" + ruleName + "]内容为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清洗规则内容(去除可能的多余引号、转义符,避免编译错误)
|
|
|
+ String cleanedContent = cleanDrlContent(drlContent);
|
|
|
+
|
|
|
+ // 生成唯一虚拟路径(用规则ID+名称确保不重复)
|
|
|
+ String virtualPath = "src/main/resources/rules/dynamic-" + rule.getId() + "-" + ruleName + ".drl";
|
|
|
+ kfs.write(virtualPath, ResourceFactory.newByteArrayResource(cleanedContent.getBytes(StandardCharsets.UTF_8)));
|
|
|
+ System.out.println("已加载规则:" + ruleName + "(版本:" + rule.getVersion() + ")");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 编译所有规则
|
|
|
+ KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();
|
|
|
+ Results results = kieBuilder.getResults();
|
|
|
+ if (results.hasMessages(Level.ERROR)) {
|
|
|
+ throw new RuntimeException("规则编译失败:" + results.getMessages());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 创建新的KieContainer并更新版本记录
|
|
|
+ KieModule kieModule = kieBuilder.getKieModule();
|
|
|
+ KieContainer newKieContainer = kieServices.newKieContainer(kieModule.getReleaseId());
|
|
|
+
|
|
|
+ // 原子更新容器(线程安全)
|
|
|
+ currentKieContainer.set(newKieContainer);
|
|
|
+ // 更新版本Map(记录当前所有规则的最新版本)
|
|
|
+ updateRuleVersionMap(allRules);
|
|
|
+
|
|
|
+ System.out.println("所有规则更新成功,共加载 " + allRules.size() + " 条规则");
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("规则加载失败,使用旧规则:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查是否需要更新规则(任一规则版本变化、新增或删除)
|
|
|
+ */
|
|
|
+ private boolean isNeedUpdate(List<RuleDefinitionsDO> newRules) {
|
|
|
+ // 首次加载(版本Map为空),需要更新
|
|
|
+ if (ruleVersionMap.isEmpty()) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查新规则中是否有版本变化或新增规则
|
|
|
+ for (RuleDefinitionsDO rule : newRules) {
|
|
|
+ String ruleName = rule.getName();
|
|
|
+ Integer newVersion = rule.getVersion();
|
|
|
+ Integer currentVersion = ruleVersionMap.get(ruleName);
|
|
|
+
|
|
|
+ // 规则新增(当前版本Map中无此规则)或版本提升,需要更新
|
|
|
+ if (currentVersion == null || !currentVersion.equals(newVersion)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否有规则被删除(当前版本Map中有,但新规则中无)
|
|
|
+ for (String existingRuleName : ruleVersionMap.keySet()) {
|
|
|
+ boolean existsInNew = newRules.stream()
|
|
|
+ .anyMatch(rule -> rule.getName().equals(existingRuleName));
|
|
|
+ if (!existsInNew) {
|
|
|
+ return true; // 有规则被删除,需要更新
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新规则版本Map(存储当前所有规则的最新版本)
|
|
|
+ */
|
|
|
+ private void updateRuleVersionMap(List<RuleDefinitionsDO> newRules) {
|
|
|
+ Map<String, Integer> newVersionMap = new HashMap<>();
|
|
|
+ for (RuleDefinitionsDO rule : newRules) {
|
|
|
+ newVersionMap.put(rule.getName(), rule.getVersion());
|
|
|
+ }
|
|
|
+ ruleVersionMap = newVersionMap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清洗DRL内容(去除多余引号、转义符,避免语法错误)
|
|
|
+ */
|
|
|
+ private String cleanDrlContent(String drlContent) {
|
|
|
+ if (drlContent == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return drlContent
|
|
|
+ .trim()
|
|
|
+ .replaceAll("^\"", "") // 去除开头的双引号
|
|
|
+ .replaceAll("\"$", "") // 去除结尾的双引号
|
|
|
+ .replaceAll("\\\\n", "\n"); // 替换转义换行符为实际换行
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提供给业务代码获取当前KieContainer的方法
|
|
|
+ */
|
|
|
+ public KieContainer getCurrentKieContainer() {
|
|
|
+ KieContainer container = currentKieContainer.get();
|
|
|
+ if (container == null) {
|
|
|
+ throw new RuntimeException("规则容器未初始化");
|
|
|
+ }
|
|
|
+ return container;
|
|
|
+ }
|
|
|
+}
|