0、前言
我们在体检时通常会做身高体重测量项目,这时体检报告上会有BMI这一体检项。BMI是国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。BMI的计算公式如下:
BMI的参考标准如下:
人体胖瘦程度 | 消瘦 | 正常值 | 超重 |
---|---|---|---|
BMI数值 | <18.5kg/m² | 18.5-24kg/m² | >24kg/m² |
下面,我们通过Drools规则引擎实现通过BMI衡量人体胖瘦程度的功能。
1、Drools概述
Drools是一个基于Java的开源规则引擎,用于管理和执行业务规则。
2、集成Drools
2.1、创建示例项目
使用 IDEA 创建 Spring Boot 项目,项目技术选型大体如下:
- spring boot 2.7.7
- drools 7.73.0.Final
- mybatis plus 3.5.3.1
- druid 1.2.16
- mysql 8
!!!假设项目中已经成功集成了 mybatis plus 。
如果不知道怎么在 Spring Boot 项目中集成 Mybatis Plus 可以参考笔者之前的文章:《「Spring Boot笔记」数据库持久化集成(Durid+MyBatisPlus)》。
2.2、引入Maven依赖
在 Spring Boot 中集成 Drools,需要在 pom.xml 文件中引入所需的依赖,具体代码如下:
xml
<properties>
<drools.version>7.73.0.Final</drools.version>
</properties>
<dependencies>
<!-- 其它依赖 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-mvel</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>${drools.version}</version>
</dependency>
</dependencies>
2.3、创建规则实体类
创建实体类Rule.java
,实体类代码如下:
arduino
@Data
@TableName("sys_rule")
public class Rule implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 规则名称
*/
private String name;
/**
* 规则内容
*/
private String content;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新人员
*/
private String updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
实体类对应数据表sys_rule
表结构:
less
CREATE TABLE `sys_rule` (
`id` varchar(36) NOT NULL COMMENT 'ID',
`name` varchar(256) NOT NULL COMMENT '规则名称',
`content` text COMMENT '规则内容',
`create_by` varchar(256) COMMENT '创建者',
`update_by` varchar(256) COMMENT '更新者',
`create_time` datetime COMMENT '创建日期',
`update_time` datetime COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT = '规则表';
主要字段是规则名称(name
)、规则内容(content
)。稍后,我们会在 Service 类中定义一个方法,用于根据规则名称获取规则信息。
2.4、创建规则数据访问对象
RuleDao.java ------ 数据访问对象
csharp
public interface RuleDao extends BaseMapper<Rule> {
}
RuleDao
接口继承 MyBatis Plus 的BaseMapper
接口,对规则的增、删、改、查主要是通过 MyBatis Plus 提供的增、删、改、查默认方法实现。
2.5、创建规则Service
RuleService.java ------ Service接口
csharp
public interface RuleService extends IService<Rule> {
/**
* 根据规则名称获取规则信息
* @param name 规则名称
*/
Rule getByName(String name);
}
在 Service接口 中,我们定义了一个方法:
getByName()
:根据规则名称获取规则信息;
RuleServiceImpl.java ------ Service实现类
scala
@Service
public class RuleServiceImpl extends ServiceImpl<RuleDao, Rule> implements RuleService {
/**
* 根据规则名称获取规则实体
* @param name 规则名称
* @return /
*/
@Override
public Rule getByName(String name) {
return getOne(Wrappers.<Rule>lambdaQuery().eq(Rule::getName, name));
}
}
getByName()
方法的实现没什么好说的,使用过 MyBatis Plus 的朋友一看就明白。
2.6、创建规则Controller
RuleController.java ------ 控制器类
less
@Slf4j
@RestController
@RequestMapping("/rule")
public class RuleController {
@Resource
private RuleService ruleService;
@PostMapping("/save")
public Object save(HttpServletRequest request) {
// 规则名称
String name = request.getParameter("name");
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// 规则文件
MultipartFile multipartFile = multipartRequest.getFile("file");
Rule rule = new Rule();
rule.setName(name);
rule.setContent(getContent(multipartFile));
rule.setCreateBy("wanggc");
rule.setCreateTime(new Date());
ruleService.save(rule);
return Dict.create().set("msg", "添加成功。");
}
private String getContent(MultipartFile multipartFile) {
String content = null;
try (InputStream inputStream = multipartFile.getInputStream()) {
content = IoUtil.read(inputStream, StandardCharsets.UTF_8);
} catch (IOException e) {
log.error("读取文件内容失败,异常信息:", e);
}
return content;
}
}
规则控制器中目前只实现了一个添加规则的接口。添加规则接口的流程大体可以概括为:从request
中获取到规则名称,读取上传文件的内容,然后一同保存到数据表中。
2.7、添加规则
使用接口工具(ApiPost7
)测试添加规则接口。
接口地址:http://127.0.0.1:10007/rule/save
。
请求参数:
name
- 规则名称,字符串类型,后续调用规则时也是使用这个名称;file
- 规则文件,drl文件;
响应结果:
json
{
"msg": "添加成功。"
}
bmi.drl 规则文件的具体内容如下:
ruby
package cn.ddcherry.springboot.demo;
dialect "java"
import cn.ddcherry.springboot.demo.fact.ExamData;
rule "bmi_1"
no-loop true
lock-on-active true
salience 1
when
$item:ExamData(bmi < 18.5);
then
$item.setConclusion("消瘦");
end
rule "bmi_2"
no-loop true
lock-on-active true
salience 1
when
$item:ExamData(bmi >= 18.5 && bmi <= 24);
then
$item.setConclusion("正常");
end
rule "bmi_3"
no-loop true
lock-on-active true
salience 1
when
$item:ExamData(bmi > 24);
then
$item.setConclusion("超重");
end
规则文件说明:
package
- 用于定义规则文件的命名空间,可以不和物理路径一致;import
- 用于导入其他Java类或包,以便在规则文件中使用它们;rule
- 规则名称,规则名称需要保持全局惟一;no-loop
- 规则的属性,表示该规则不会重复执行;lock-on-active
- 规则的属性,表示该规则不会重复执行;salience
- 规则的属性,用于设置规则的优先级,数值越大,优先级越高,默认值为0;when
- 条件语句,用于指定执行后续的动作语句需要满足什么条件;then
- 规则的动作语句,表示满足条件时需要执行的操作;end
- 规则结束;
2.8、创建规则事实对象
ExamData.java ------ 规则事实对象
arduino
@Data
public class ExamData {
/**
* BMI的测量值
*/
private Double bmi;
/**
* 规则名称
*/
private String ruleName;
/**
* 结论
*/
private String conclusion;
}
事实对象就是一个普通的JavaBean,规则引擎可以对事实对象进行任意的读写操作。
2.9、创建体检业务控制器类
ExamController.java ------ 体检业务控制器类
less
@Slf4j
@RestController
@RequestMapping("/exam")
public class ExamController {
@Resource
private RuleService ruleService;
@PostMapping("/getConclusion")
public Object save(@RequestBody ExamData examData) {
// 根据规则名称获取规则实体
Rule rule = ruleService.getByName(examData.getRuleName());
try {
// 初始化KieHelper,用于构建规则
KieHelper kieHelper = new KieHelper();
// 设置规则内容
kieHelper.addContent(rule.getContent(), ResourceType.DRL);
// 创建KieSession对象
KieSession kieSession = kieHelper.build().newKieSession();
// 插入事实对象
kieSession.insert(examData);
// 触发规则
kieSession.fireAllRules();
// 释放KieSession资源
kieSession.dispose();
} catch (Exception e) {
log.error("生成结论时发生异常,异常信息:", e);
}
Dict result = Dict.create();
return Objects.isNull(examData.getConclusion()) ? result.set("msg", "生成结论失败!") : result.set("result", examData.getConclusion());
}
}
上面的代码中已经给每一步都添加了注释,触发规则的整个流程图如下:
2.10 测试生成结论接口
接口地址:http://127.0.0.1:10007/exam/getConclusion
请求参数:
json
{
"ruleName": "bmi",
"bmi": 27.51
}
响应结果:
json
{
"result": "超重"
}