一、基础
<!-- Drools 核心依赖 -->
<!--drools-core:这是Drools规则引擎的核心包,包含了Drools规则引擎的基本功能,如规则存储、规则执行等核心机制-->
<dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>8.44.0.Final</version>
</dependency>
<!--drools-compiler:这个包提供了对Drools规则文件(如DRL文件)的编译功能,将规则文件编译成可以在运行时执行的规则-->
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>8.44.0.Final</version>
</dependency>
<!--drools-decisiontables:支持从Excel或CSV文件中加载规则,这些文件通常被称为决策表,它们提供了一种直观的方式来定义复杂的业务规则-->
<dependency><groupId>org.drools</groupId><artifactId>drools-decisiontables</artifactId><version>8.44.0.Final</version>
</dependency>
<!--drools-mvel:Drools所需的脚本语言包,MVEL(MVFLEX Expression Language)是一个动态的表达式语言,用于在Drools规则中编写更复杂的逻辑。-->
<dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>8.44.0.Final</version>
</dependency>
<dependency><groupId>org.kie</groupId><artifactId>kie-api</artifactId><version>8.44.0.Final</version>
</dependency>
CREATE TABLE `rule_definitions` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`version` varchar(3) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '1',`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`description` text COLLATE utf8mb4_unicode_ci,`content` longtext COLLATE utf8mb4_unicode_ci,`enabled` tinyint(1) DEFAULT '1',`creator` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updater` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',`tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '租户编号',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;INSERT INTO `rule_definitions` (`version`, `name`, `description`, `content`, `enabled`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES ('1', 'rule1', NULL, 'package rules;\r\nimport cn.iocoder.yudao.module.iscs.dal.dataobject.Person;\r\n\r\ndialect \"mvel\" // 默认就是MVEL表达式语言\r\n\r\nrule \"checkHighSalary11\"\r\nwhen\r\n $p : Person(age > 40 && salary >= 40000)\r\nthen\r\n System.out.println(\"rule1警告:[\" + $p.getName() + \"]薪资过高sqll!\");\r\nend\r\n\r\nrule \"checkHighSalary12\"\r\nwhen\r\n $p : Person(age > 30 && salary >= 30000)\r\nthen\r\n System.out.println(\"rule1警告:[\" + $p.getName() + \"]薪资过高2sqll!\");\r\nend', 1, '', '2025-10-21 14:04:01', '', '2025-10-21 16:19:18', b'0', 1);
INSERT INTO `rule_definitions` (`version`, `name`, `description`, `content`, `enabled`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES ('2', 'rule2', NULL, 'package rules;\r\nimport cn.iocoder.yudao.module.iscs.dal.dataobject.Person;\r\n\r\ndialect \"mvel\" // 默认就是MVEL表达式语言\r\n\r\nrule \"checkHighSalary\"\r\nwhen\r\n $p : Person(age > 20 && salary >= 20000)\r\nthen\r\n System.out.println(\"rule2警告:[\" + $p.getName() + \"]薪资过高sqll!\");\r\nend\r\n\r\nrule \"checkHighSalary2\"\r\nwhen\r\n $p : Person(age > 10 && salary >= 10000)\r\nthen\r\n System.out.println(\"rule2警告:[\" + $p.getName() + \"]薪资过高2sqll!\");\r\nend', 1, '', '2025-10-21 14:04:01', '', '2025-10-22 09:18:04', b'0', 1);
package cn.iocoder.yudao.module.iscs.dal.dataobject.rule;import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@TableName("rule_definitions")
@KeySequence("rule_definitions_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RuleDefinitionsDO extends BaseDO {@TableIdprivate Long id;private String version;private String name;private String description;private String content;private Integer enabled;}
@Override
public RuleDefinitionsDO getOneByName(String name) {Assert.notBlank(name, "名称不能为空!");return getOne(Wrappers.<RuleDefinitionsDO>lambdaQuery().eq(RuleDefinitionsDO::getName, name));
}@Override
public List<RuleDefinitionsDO> getAllValidRules() {return list();
}
drl方式
package cn.iocoder.yudao.module.iscs.config;import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DroolsConfig {@Beanpublic KieContainer kieContainer() {KieServices kieServices = KieServices.Factory.get();KieFileSystem kfs = kieServices.newKieFileSystem();kfs.write(ResourceFactory.newClassPathResource("rules/sample.drl", getClass().getClassLoader()));KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();Results results = kieBuilder.getResults();if (results.hasMessages()) {results.getMessages().forEach(msg -> System.err.println("⚠️ Rules compilation error: " + msg));}KieModule kieModule = kieBuilder.getKieModule();ReleaseId releaseId = kieModule.getReleaseId(); return kieServices.newKieContainer(releaseId); }
}
- 新增drl文件

package rules;
import cn.iocoder.yudao.module.iscs.dal.dataobject.Person;dialect "mvel" rule "checkHighSalary"
when$p : Person(age > 30 && salary >= 10000)
thenSystem.out.println("警告:[" + $p.getName() + "]薪资过高drl!");
endrule "checkHighSalary2"
when$p : Person(age > 20 && salary >= 12000)
thenSystem.out.println("警告:[" + $p.getName() + "]薪资过高2drl!");
end
@Operation(summary = "规则--------")@PostMapping("/execute")public Person executeRule(@RequestBody Person person) {KieSession session = null; try {session = kieContainer.newKieSession();session.insert(person); session.fireAllRules(); return person; } catch (Exception e) {throw new RuntimeException("Failed to execute rules", e);} finally {if (session != null) {session.dispose(); }}}
三、mysql方式
package cn.iocoder.yudao.module.iscs.config;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 {@Autowiredprivate RuleDefinitionsService ruleDefinitionsService;private final AtomicReference<KieContainer> currentKieContainer = new AtomicReference<>();private Map<String, String> ruleVersionMap = new HashMap<>();public DynamicRuleManager(RuleDefinitionsService ruleDefinitionsService) {this.ruleDefinitionsService = ruleDefinitionsService;loadRuleFromDb();}public void loadRuleFromDb() {System.out.println("开始加载所有drools规则");try {List<RuleDefinitionsDO> allRules = ruleDefinitionsService.getAllValidRules();if (allRules.isEmpty()) {throw new RuntimeException("数据库中未找到任何有效规则");}if (!isNeedUpdate(allRules)) {System.out.println("所有规则版本未更新,无需重新加载");return;}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);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() + ")");}KieBuilder kieBuilder = kieServices.newKieBuilder(kfs).buildAll();Results results = kieBuilder.getResults();if (results.hasMessages(Level.ERROR)) {throw new RuntimeException("规则编译失败:" + results.getMessages());}KieModule kieModule = kieBuilder.getKieModule();KieContainer newKieContainer = kieServices.newKieContainer(kieModule.getReleaseId());currentKieContainer.set(newKieContainer);updateRuleVersionMap(allRules);System.out.println("所有规则更新成功,共加载 " + allRules.size() + " 条规则");} catch (Exception e) {System.err.println("规则加载失败,使用旧规则:" + e.getMessage());}}private boolean isNeedUpdate(List<RuleDefinitionsDO> newRules) {if (ruleVersionMap.isEmpty()) {return true;}for (RuleDefinitionsDO rule : newRules) {String ruleName = rule.getName();String newVersion = rule.getVersion();String currentVersion = ruleVersionMap.get(ruleName);if (currentVersion == null || !currentVersion.equals(newVersion)) {return true;}}for (String existingRuleName : ruleVersionMap.keySet()) {boolean existsInNew = newRules.stream().anyMatch(rule -> rule.getName().equals(existingRuleName));if (!existsInNew) {return true; }}return false;}private void updateRuleVersionMap(List<RuleDefinitionsDO> newRules) {Map<String, String> newVersionMap = new HashMap<>();for (RuleDefinitionsDO rule : newRules) {newVersionMap.put(rule.getName(), rule.getVersion());}ruleVersionMap = newVersionMap;}private String cleanDrlContent(String drlContent) {if (drlContent == null) {return null;}return drlContent.trim().replaceAll("^\"", "") .replaceAll("\"$", "") .replaceAll("\\\\n", "\n"); }public KieContainer getCurrentKieContainer() {KieContainer container = currentKieContainer.get();if (container == null) {throw new RuntimeException("规则容器未初始化");}return container;}
}
package cn.iocoder.yudao.module.iscs.config;import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@EnableScheduling
@Component
public class RuleUpdateScheduler {private final DynamicRuleManager ruleManager;public RuleUpdateScheduler(DynamicRuleManager ruleManager) {this.ruleManager = ruleManager;}@TenantJob@Scheduled(fixedRate = 600000)public void checkAndUpdateRule() {ruleManager.loadRuleFromDb();}
}
@Operation(summary = "规则2--------")@PostMapping("/execute2")public Person executeRule2(@RequestBody Person person) {KieContainer currentKieContainer = dynamicRuleManager.getCurrentKieContainer();KieSession session = null; try {session = currentKieContainer.newKieSession();session.insert(person); session.fireAllRules(); return person; } catch (Exception e) {throw new RuntimeException("Failed to execute rules", e);} finally {if (session != null) {session.dispose(); }}}@Operation(summary = "规则3--------")@PostMapping("/execute3")public Person executeRule3(@RequestBody Person person) {KieContainer currentKieContainer = dynamicRuleManager.getCurrentKieContainer();KieSession session = null;try {session = currentKieContainer.newKieSession();session.insert(person); session.fireAllRules(match -> {String ruleName = match.getRule().getName();return ruleName.contains("checkHighSalary1");});return person;} catch (Exception e) {throw new RuntimeException("Failed to execute rules", e);} finally {if (session != null) {session.dispose(); }}}