Drools+自定义规则库

文章目录

  • 前言
  • 一、创建规则库
  • 二、SpringBoot+Drools程序
    • 1.Maven依赖
    • 2.application.yml
    • 3.Mapper.xml
    • 4.Drools配置类
    • 5.Service
    • 6.Contoller
    • 7.测试接口


前言

公司的技术方案想搭建Drools+自定义规则库配合大模型进行数据的校验。本篇用来记录使用SpringBoot配合Drools开发Demo程序。

初步设计的技术方案为:使用数据库存储DRL文件,在程序启动时将所有的DRL文件加载到程序。接口传入数据时要带有想要做规则判断的DRL文件key值,支持多个key。面对规则库的数据有修改或增添时,理想方案是监听数据库的修改,然后将增量或修改加载到程序。也可以通过定时任务的方式定时全量重新加载,但就没办法实现实时生效。也可以暴露接口手动重新加载。

以上为初步的技术方案,很多地方还比较粗糙,如果有比较成熟的方案欢迎大家交流。


一、创建规则库

我这里用的MySql数据库
建表语句如下

DROP TABLE IF EXISTS `drools_rules`;
CREATE TABLE `drools_rules`  (`id` int(11) NOT NULL AUTO_INCREMENT,`rule_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,`rule_content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,`version` int(11) NULL DEFAULT 1,`enabled` tinyint(1) NULL DEFAULT 1,`last_modified` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP(0),PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Compact;

主要就是将DRL文件以文本的格式存到数据库里
DRL例子如下:

rule "手术麻醉方式必填校验" @BusinessType("diagnosis_check")when$data: org.example.drools.domain.vo.ClinicalData(anesthesiaMethod == null || anesthesiaMethod.isEmpty())$result: org.example.drools.domain.vo.ValidationResult()then$result.addError("RULE_001", "手术记录必须包含麻醉方式");end

二、SpringBoot+Drools程序

1.Maven依赖

我这里Java用的11版本

		<!-- Drools --><dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>7.69.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.69.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>7.69.0.Final</version> <!-- 确保版本与 drools-core 一致 --></dependency>

2.application.yml

server:port: 9528
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://XXXX:3306/drools_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: rootpassword: root
logging:level:org.springframework: INFOcom.example.drools: DEBUG
mybatis-plus:type-aliases-package: com.example.drools.domainmapper-locations: classpath*:mapper/**/*Mapper.xml

3.Mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.drools.mapper.DroolsDemoMapper"><select id="findAllEnabledRules" resultType="org.example.drools.domain.entity.DroolsRules">SELECT *FROM drools_rules rWHERE r.enabled = 1ORDER BY r.version DESC</select>
</mapper>

获取数据库中所有生效的规则,按版本倒排

4.Drools配置类

package org.example.drools.config;import org.example.drools.domain.entity.DroolsRules;
import org.example.drools.mapper.DroolsDemoMapper;
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.runtime.KieContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.List;/*** Drools 动态配置*/
@Configuration
public class DroolsConfig {//操作规则库的Mapper@Autowiredprivate DroolsDemoMapper droolsDemoMapper;@Beanpublic KieContainer kieContainer() {KieServices kieServices = KieServices.get();KieFileSystem kieFileSystem = kieServices.newKieFileSystem();reloadRules(kieFileSystem); // 初始化加载规则KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);kieBuilder.buildAll();KieModule kieModule = kieBuilder.getKieModule();return kieServices.newKieContainer(kieModule.getReleaseId());}// 动态重新加载规则public void reloadRules(KieFileSystem kieFileSystem) {List<DroolsRules> rules = droolsDemoMapper.findAllEnabledRules();rules.forEach(rule ->kieFileSystem.write(String.format("src/main/resources/%s.drl", rule.getRuleName()),rule.getRuleContent()));}
}

初始化加载所有规则到Drools的容器中

5.Service

package org.example.drools.service;import org.drools.core.base.RuleNameEqualsAgendaFilter;
import org.example.drools.config.DroolsConfig;
import org.example.drools.domain.vo.ClinicalData;
import org.example.drools.domain.vo.ValidationResult;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.definition.KiePackage;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.AgendaFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;@Service
public class DroolsService {@Autowiredprivate KieContainer kieContainer;@Autowiredprivate DroolsConfig droolsConfig;private static Map<String, String> ruleMetadataMap = new HashMap<>();/*** 初始化元数据*/@PostConstructprivate void init() {KieBase kieBase = kieContainer.getKieBase();for (KiePackage pkg : kieBase.getKiePackages()) {for (Rule rule : pkg.getRules()) {org.drools.core.definitions.rule.impl.RuleImpl ruleImpl =(org.drools.core.definitions.rule.impl.RuleImpl) rule;// 提取元数据String businessType = (String) ruleImpl.getMetaData().get("BusinessType");ruleMetadataMap.put(rule.getName(), businessType);}}}public ValidationResult validate(ClinicalData request) {KieSession kieSession = kieContainer.newKieSession();ValidationResult result = new ValidationResult();//kieSession.setGlobal("errors", result.getErrors());kieSession.insert(request);kieSession.insert(result);AgendaFilter filter = activation -> {String ruleName = activation.getRule().getName();String businessType = ruleMetadataMap.get(ruleName);return request.getRuleTypes().contains(businessType);};kieSession.fireAllRules(filter);kieSession.dispose();return result;}}

当我们将所有的规则初始化加载到Drools的容器中后,如果直接使用接口参数执行规则判断,那么Drools将使用参数走过所有的规则判断,而实际的场景中我这些参数只想执行部分规则,那么就引出过滤规则的问题。

当时在处理这个问题的时候也是问了大模型的方案,当时大模型给出了一个比较麻烦的方案,就是在DRL文件中使用类似自定义注解的东西将DRL的一个业务编码写进去,具体写法可以看上面我展示的DRL文件例子,@BussinessType那个。然后后面就一直研究这个方案如何实现。

后面整理的时候发现其实有很简单的方法就能实现这个问题,我先说这个麻烦的方案是如何实现的,后面补充上简单的方案

麻烦的方案是:在将所有的规则加载到容器中后,遍历所有规则,将规则的name作为key,将DRL文件中的@BussinessType的字段取出作为value存在一个Map中,然后编写Drools的过滤器

 AgendaFilter filter = activation -> {String ruleName = activation.getRule().getName();String businessType = ruleMetadataMap.get(ruleName);return request.getRuleTypes().contains(businessType);};

简单的方案是:接口传参的时候直接传rule_name的集合,反正都是定死的,传name和bussinessType没区别,然后过滤器直接request.getRuleTypes().contains(ruleName)就行了。

6.Contoller

@RestController
@RequestMapping("/drools")
@Api(tags = "Drools测试 API", description = "提供Drools测试相关的 Rest API")
public class DroolsController {@Autowiredprivate DroolsService droolsService;@PostMapping("/test")public ValidationResult validateClinicalData(@RequestBody ClinicalData request) {return droolsService.validate(request);}
}
@Data
@ApiModel(value = "ClinicalData")
public class ClinicalData {@ApiModelProperty("麻醉方式")private String anesthesiaMethod;// 门(急)诊诊断编码(ICD-10)@ApiModelProperty("门(急)诊诊断编码")private String emergencyDiagnosisCode;@ApiModelProperty("规则类型集合")private Set<String> ruleTypes;public String getAnesthesiaMethod() {return anesthesiaMethod;}public void setAnesthesiaMethod(String anesthesiaMethod) {this.anesthesiaMethod = anesthesiaMethod;}public String getEmergencyDiagnosisCode() {return emergencyDiagnosisCode;}public void setEmergencyDiagnosisCode(String emergencyDiagnosisCode) {this.emergencyDiagnosisCode = emergencyDiagnosisCode;}public Set<String> getRuleTypes() {return ruleTypes;}public void setRuleTypes(Set<String> ruleTypes) {this.ruleTypes = ruleTypes;}
}

7.测试接口

在这里插入图片描述
附上结果返回的实体类结构以及我测试使用的两个DRL文件

@Data
public class ValidationResult {// 校验错误信息列表private List<String> errors = new ArrayList<>();// 校验通过标记public boolean isValid() {return errors.isEmpty();}// 添加带错误码的信息public void addError(String errorCode, String message) {errors.add(String.format("[%s] %s", errorCode, message));}public List<String> getErrors() {return errors;}public void setErrors(List<String> errors) {this.errors = errors;}
}
rule "手术麻醉方式必填校验" @BusinessType("diagnosis_check")when$data: org.example.drools.domain.vo.ClinicalData(anesthesiaMethod == null || anesthesiaMethod.isEmpty())$result: org.example.drools.domain.vo.ValidationResult()then$result.addError("RULE_001", "手术记录必须包含麻醉方式");end
function Boolean isValidDiagnosisCode(String code) {if (code == null || code.isEmpty()) return false;Character firstChar = Character.toUpperCase(code.charAt(0));return (firstChar >= 'A' && firstChar <= 'U') || firstChar == 'Z';
}
rule "诊断编码范围校验" @BusinessType("code_check")when$data: org.example.drools.domain.vo.ClinicalData(emergencyDiagnosisCode != null,!isValidDiagnosisCode(emergencyDiagnosisCode))$result: org.example.drools.domain.vo.ValidationResult()then$result.addError("RULE_002", "诊断编码各项编码范围应为:A~U开头和Z开头的编码");end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/902839.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

潮了 低配电脑6G显存生成60秒AI视频 本地部署/一键包/云算力部署/批量生成

最近发现了一个让人眼前一亮的工具——FramePack&#xff0c;它能用一块普通的6GB显存笔记本GPU&#xff0c;生成60秒电影级的高清视频画面&#xff0c;效果堪称炸裂&#xff01;那么我们就把他本地部署起来玩一玩、下载离线一键整合包&#xff0c;或者是用云算力快速上手。接下…

【蓝桥杯选拔赛真题104】Scratch回文数 第十五届蓝桥杯scratch图形化编程 少儿编程创意编程选拔赛真题解析

目录 scratch回文数 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 四、程序编写 五、考点分析 六、推荐资料 1、scratch资料 2、python资料 3、C++资料 scratch回文数 第十五届青少年蓝桥杯scratch编…

大厂面试-框架篇

前言 本章内容来自B站黑马程序员java大厂面试题和小林coding 博主学习笔记&#xff0c;如果有不对的地方&#xff0c;海涵。 如果这篇文章对你有帮助&#xff0c;可以点点关注&#xff0c;点点赞&#xff0c;谢谢你&#xff01; 1.Spring 1.1 Spring框架中的单例bean是线程…

【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)

文章目录 DIY 实战&#xff1a;从扫雷小游戏开发再探问题分解能力3 问题分解实战&#xff08;自顶向下&#xff09;3.2 页面渲染逻辑3.3 事件绑定逻辑 4 代码实现&#xff08;自底向上&#xff09;4.1 页面渲染部分4.2 事件绑定部分 写在前面 本篇将利用《Learn AI-assisted Py…

微信小程序开发1------微信小程序中的消息提示框总结

微信小程序中的消息提示框主要分为以下几种&#xff1a; 1. wx.showToast(Object object) 功能&#xff1a; 显示消息提示框&#xff0c;一般用于显示操作结果、状态等。 特点&#xff1a; 提示框显示在屏幕中间&#xff0c;持续一段时间后自动消失&#xff08;默认1.5秒&…

AI 场景落地:API 接口服务 VS 本地部署,哪种更适合?

在当前 AI 技术迅猛发展的背景下&#xff0c;企业在实现 AI 场景落地时&#xff0c;面临着一个关键抉择&#xff1a;是选择各大厂商提供的 API 接口服务&#xff0c;还是进行本地化部署&#xff1f;这不仅关乎成本、性能和安全性&#xff0c;还涉及到技术架构、数据治理和长期战…

Android 加壳应用运行流程 与 生命周期类处理方案

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ DexClassLoader DexClassLoader 可以加载任意路径下的 dex&#xff0c;或者 jar、apk、zip 文件&#xff08;包含classes.dex&#xff09;。常用于插件化、热…

c++进阶——类与继承

文章目录 继承继承的基本概念继承的基本定义继承方式继承的一些注意事项 继承类模板 基类和派生类之间的转换继承中的作用域派生类的默认成员函数默认构造函数拷贝构造赋值重载析构函数默认成员函数总结 不能被继承的类继承和友元继承与静态成员多继承及其菱形继承问题继承模型…

GAEA情感坐标背后的技术原理

基于GAEA的去中心化物理基础设施网络&#xff08;DePIN&#xff09;&#xff0c;用户有机会在GAEA平台上获得宝贵的数据共享积分。为了提升这些洞察的丰富性&#xff0c;用户必须花费一定数量的积分&#xff0c;将过去的网络数据与当前的情感数据绑定&#xff0c;从而产生一种新…

图形编辑器基于Paper.js教程27:对图像描摹的功能实现,以及参数调整

本篇文章来讲一下 图像描摹的功能的实现。 我们知道要雕刻图片可以通过分析图片的像素来生成相应的gcode进行雕刻&#xff0c;但如果你想要将图片转换为线稿进行雕刻&#xff0c;这个时候就要从图片中提取出 线稿。 例如下面的图片&#xff1a; 你想要获取到这个图片的线稿&…

人工智能与机器学习,谁是谁的子集 —— 再谈智能的边界与演进路径

人工智能&#xff08;Artificial Intelligence, AI&#xff09;作为当代最具影响力的前沿技术之一&#xff0c;常被大众简化为 “深度学习” 或 “大模型” 等标签。然而&#xff0c;这种简化认知往往掩盖了AI技术内部结构的复杂性与多样性。事实上&#xff0c;AI并非单一方法的…

Oracle_开启归档日志和重做日志

在Oracle中&#xff0c;类似于MySQL的binlog的机制是归档日志&#xff08;Archive Log&#xff09;和重做日志&#xff08;Redo Log&#xff09; 查询归档日志状态 SELECT log_mode FROM v$database; – 输出示例&#xff1a; – LOG_MODE – ARCHIVELOG (表示已开启) – NO…

IDEA编写flinkSQL(快速体验版本,--无需配置环境)

相关资料 文档内容链接地址datagen生成器https://nightlies.apache.org/flink/flink-docs-release-1.16/docs/connectors/table/datagen/print 生成器https://nightlies.apache.org/flink/flink-docs-release-1.16/docs/connectors/table/print/ 准备工作 优点就是下载个ide…

基于AI技术的高速公路交通引流系统设计与应用研究

基于AI技术的高速公路交通引流系统设计与应用研究 1. 研究背景与意义 1.1 交通系统演化脉络 1.1.1 发展阶段划分 机械化时代&#xff08;1950-1990&#xff09;&#xff1a;固定式信号控制信息化时代&#xff08;1991-2010&#xff09;&#xff1a;SCATS/SCOOT系统智能化时代…

NEGATIVE LABEL GUIDED OOD DETECTION WITH PRETRAINED VISION-LANGUAGE MODELS

1. 介绍: 这篇论文也是基于CLIP通过后处理的方法实现的OOD的检测,但是设计点在于,之前的方法是使用的ID的类别,这篇工作是通过添加一些在语义上非常不同于ID的类别的外分布类来做的OOD检测。 CLIP做OOD检测的这个系列里面我看的以及记录的第一篇就是MCM的方法,这也是确实是…

Linux 网络基础三 (数据链路层协议:以太网协议、ARP 协议)

一、以太网 两个不同局域网的主机传递数据并不是直接传递的&#xff0c;而是通过路由器 “一跳一跳” 的传递过去。 跨网络传输的本质&#xff1a;由无数个局域网&#xff08;子网&#xff09;转发的结果。 所以&#xff0c;要理解数据跨网络转发原理就要先理解一个局域网中数…

Azure Data Factory ETL设计与调度最佳实践

一、引言 在Azure Data Factory (ADF) 中&#xff0c;调度和设计ETL&#xff08;抽取、转换、加载&#xff09;过程需要综合考量多方面因素&#xff0c;以确保数据处理高效、可扩展、可靠且易于维护。以下将详细介绍相关关键考虑因素、最佳实践&#xff0c;并辅以具体示例说明…

非序列实现MEMS聚焦功能

zemax非序列模式下有MEMS,但是没有对应的代码。无法修改成自己需要的功能 以下是实现MEMS聚焦功能: #include <windows.h> #include <cmath> #include <stdio.h> #include <string.h> #include <algorithm> #undef max #undef min#define D…

android studio sdk unavailable和Android 安装时报错:SDK emulator directory is missing

md 网上说的都是更换proxy代理什么的&#xff0c;换网的&#xff0c;还有一些二其他乱七八糟的&#xff0c;根本没用&#xff0c;感觉很多就是解决不了问题&#xff0c;还贼多贼一致&#xff0c;同质化&#xff0c;感觉很坑人&#xff0c;让人觉得他们和我的一样的&#xff0c;…

三维重建模块VR,3DCursor,MPR与VR的坐标转换

MPR里的reslicecursor 的坐标与 vtkimage 坐标一致。 但三维窗格里的vtkvolume 的坐标是相对坐标&#xff0c;坐标值依然是MM单位。 用中心点的偏移量比较容易实现&#xff0c;交互中Reslicercursor中心点 距离 vtkimagedata 的中心点 的偏移量&#xff0c;用于vtkvolume即可…