SpringBoot 整合 QLExpress 教程:患者随访画像多条件适配案例
一、教程概述
QLExpress 是阿里开源的一款轻量级表达式引擎,支持动态解析和执行表达式,适用于规则配置、动态逻辑判断等场景。本教程将从 SpringBoot 整合 QLExpress 基础入手,通过患者随访画像 场景,实现从单一条件到多条件组合的规则适配,帮助你掌握 QLExpress 的核心使用方式。QLExpress参考教程
适用场景
患者随访画像:根据患者的年龄、性别、诊断结果、手术类型等维度,动态判断是否需要触发特定的随访规则(如术后 3 个月随访、高危人群月度随访等)。
技术栈
- SpringBoot 2.7.x(兼容 3.x)
- QLExpress 3.2.0
- Maven 3.6+
二、环境准备
1. 引入依赖
在 SpringBoot 项目的 pom.xml 中添加 QLExpress 依赖:
xml
<dependencies>
<!-- SpringBoot 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- QLExpress 表达式引擎 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 核心概念说明
ExpressRunner:QLExpress 核心执行器,负责解析和执行表达式。DefaultContext:表达式执行的上下文,用于存储变量(如患者的年龄、性别等)。- 表达式规则:基于 QLExpress 语法编写的逻辑判断规则(如
age > 60 && gender == '女')。
三、基础整合:QLExpress 配置类
首先创建 QLExpress 的配置类,将 ExpressRunner 注入 Spring 容器,方便全局使用:
java
import com.ql.util.express.ExpressRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* QLExpress 配置类
*/
@Configuration
public class QLExpressConfig {
/**
* 注入 ExpressRunner 执行器
* @return ExpressRunner 实例
*/
@Bean
public ExpressRunner expressRunner() {
// 构造参数说明:
// 1. isPrecise:是否高精度计算(默认false)
// 2. isShortCircuit:是否短路计算(默认true,类似Java && || 短路)
return new ExpressRunner(true, true);
}
}
四、场景实现:患者随访画像规则适配
1. 定义患者实体类
创建 Patient 实体,封装患者的核心属性:
java
import lombok.Data;
/**
* 患者实体类
*/
@Data
public class Patient {
/** 患者ID */
private Long id;
/** 年龄 */
private Integer age;
/** 性别(男/女) */
private String gender;
/** 诊断结果(如:高血压、糖尿病、胃癌) */
private String diagnosis;
/** 手术类型(如:胃癌根治术、无) */
private String surgeryType;
/** 是否术后(true/false) */
private Boolean postOperative;
}
2. 封装 QLExpress 执行工具类
创建工具类统一处理表达式执行逻辑,简化业务代码:
java
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.QLExpressException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
/**
* QLExpress 执行工具类
*/
@Component
public class QLExpressExecutor {
@Resource
private ExpressRunner expressRunner;
/**
* 执行表达式
* @param express 表达式字符串
* @param contextMap 上下文变量(键:变量名,值:变量值)
* @return 表达式执行结果(Boolean/Integer/String 等)
* @throws QLExpressException 表达式执行异常
*/
public Object execute(String express, Map<String, Object> contextMap) throws QLExpressException {
// 创建上下文
DefaultContext<String, Object> context = new DefaultContext<>();
// 填充上下文变量
if (contextMap != null) {
contextMap.forEach(context::put);
}
// 执行表达式(第三个参数:是否输出执行日志,第四个参数:异常是否中断)
return expressRunner.execute(express, context, null, true, false);
}
/**
* 执行布尔类型表达式(简化封装)
* @param express 布尔表达式
* @param contextMap 上下文变量
* @return 布尔结果
*/
public Boolean executeBoolean(String express, Map<String, Object> contextMap) {
try {
Object result = execute(express, contextMap);
return result instanceof Boolean ? (Boolean) result : false;
} catch (QLExpressException e) {
e.printStackTrace();
return false;
}
}
}
3. 案例 1:单一条件适配
需求:判断患者是否为「老年高血压患者」(年龄 ≥ 60 岁 且 诊断为高血压)。
编写测试代码
java
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class PatientFollowUpTest {
@Resource
private QLExpressExecutor qlExpressExecutor;
/**
* 案例1:单一条件组合(老年高血压患者)
*/
@Test
public void testSingleCondition() {
// 1. 构建患者数据
Patient patient = new Patient();
patient.setAge(65);
patient.setGender("男");
patient.setDiagnosis("高血压");
patient.setSurgeryType("无");
patient.setPostOperative(false);
// 2. 定义表达式规则
String express = "age >= 60 && diagnosis == '高血压'";
// 3. 构建上下文变量
Map<String, Object> contextMap = new HashMap<>();
contextMap.put("age", patient.getAge());
contextMap.put("diagnosis", patient.getDiagnosis());
// 4. 执行表达式
Boolean result = qlExpressExecutor.executeBoolean(express, contextMap);
// 5. 输出结果
System.out.println("是否为老年高血压患者:" + result); // 输出:true
}
}
扩展测试
修改患者年龄为 55 岁,诊断为「糖尿病」,执行结果会返回 false,验证单一条件的准确性。
4. 案例 2:多条件组合适配(进阶)
需求:判断患者是否需要「术后高危随访」,规则如下:
- 手术类型为「胃癌根治术」;
- 年龄 ≥ 50 岁 或 性别为女性;
- 术后状态为 true。
编写测试代码
java
/**
* 案例2:多条件组合(术后高危随访)
*/
@Test
public void testMultiCondition() {
// 1. 构建患者数据
Patient patient = new Patient();
patient.setAge(55);
patient.setGender("女");
patient.setDiagnosis("胃癌");
patient.setSurgeryType("胃癌根治术");
patient.setPostOperative(true);
// 2. 定义多条件表达式(QLExpress 支持 Java 语法的逻辑运算符)
String express = "postOperative == true " +
"&& surgeryType == '胃癌根治术' " +
"&& (age >= 50 || gender == '女')";
// 3. 构建上下文变量
Map<String, Object> contextMap = new HashMap<>();
contextMap.put("postOperative", patient.getPostOperative());
contextMap.put("surgeryType", patient.getSurgeryType());
contextMap.put("age", patient.getAge());
contextMap.put("gender", patient.getGender());
// 4. 执行表达式
Boolean result = qlExpressExecutor.executeBoolean(express, contextMap);
// 5. 输出结果
System.out.println("是否需要术后高危随访:" + result); // 输出:true
}
规则灵活调整
若后续规则变更(如年龄 ≥ 55 岁),只需修改表达式字符串,无需修改业务代码:
java
String express = "postOperative == true " +
"&& surgeryType == '胃癌根治术' " +
"&& (age >= 55 || gender == '女')";
5. 案例 3:动态规则配置(实战)
需求:从配置文件 / 数据库读取随访规则,动态执行(模拟生产环境的规则配置化)。
步骤 1:添加规则配置(application.yml)
java
follow-up:
rules:
high-risk: "postOperative == true && surgeryType == '胃癌根治术' && (age >= 50 || gender == '女')"
elderly-hypertension: "age >= 60 && diagnosis == '高血压'"
diabetes-follow: "diagnosis == '糖尿病' && age < 60 && postOperative == false"
步骤 2:读取配置并执行
java
import org.springframework.beans.factory.annotation.Value;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class DynamicRuleTest {
@Resource
private QLExpressExecutor qlExpressExecutor;
// 从配置文件读取高危随访规则
@Value("${follow-up.rules.high-risk}")
private String highRiskRule;
/**
* 案例3:动态读取规则配置执行
*/
@Test
public void testDynamicRule() {
// 1. 构建患者数据
Patient patient = new Patient();
patient.setAge(48);
patient.setGender("女");
patient.setDiagnosis("胃癌");
patient.setSurgeryType("胃癌根治术");
patient.setPostOperative(true);
// 2. 构建上下文变量
Map<String, Object> contextMap = new HashMap<>();
contextMap.put("postOperative", patient.getPostOperative());
contextMap.put("surgeryType", patient.getSurgeryType());
contextMap.put("age", patient.getAge());
contextMap.put("gender", patient.getGender());
// 3. 执行动态读取的规则
Boolean result = qlExpressExecutor.executeBoolean(highRiskRule, contextMap);
// 4. 输出结果
System.out.println("动态规则执行结果(高危随访):" + result); // 输出:true
}
}
五、QLExpress 核心语法补充
| 语法类型 | 示例 | 说明 | ||
|---|---|---|---|---|
| 逻辑运算 | age > 60 && gender == '女' |
支持 &&、 | 、! 运算符 | |
| 比较运算 | surgeryType != '无' |
支持 ==、!=、>、<、>=、<= | ||
| 算术运算 | age + 5 >= 65 |
支持 +、-、*、/、% | ||
| 条件判断 | age >= 60 ? '老年' : '非老年' |
三元表达式 | ||
| 集合判断 | ['高血压','糖尿病'].contains(diagnosis) |
集合包含判断 |
六、注意事项
- 表达式安全 :生产环境需过滤表达式中的危险操作(如
System.exit(0)),可通过ExpressRunner的addFunctionFilter限制函数调用。 - 性能优化 :高频执行的表达式可通过
expressRunner.compileExpress(express)预编译,提升执行效率。 - 异常处理 :表达式语法错误、变量缺失会抛出
QLExpressException,需捕获并处理。 - 变量名规范:上下文变量名需与表达式中的变量名一致,避免大小写问题(QLExpress 变量名区分大小写)。
七、总结
本教程通过 SpringBoot 整合 QLExpress,以患者随访画像为场景,实现了从单一条件到多条件组合、动态规则配置的完整案例。QLExpress 的核心优势在于规则配置化,无需修改代码即可调整业务逻辑,适用于风控、医疗、电商等需要动态规则的场景。
后续可扩展方向:
- 将规则存储到数据库,提供后台管理界面配置规则;
- 增加表达式语法校验,避免非法规则配置;
- 结合缓存预编译常用表达式,提升执行性能。