SpringBoot 整合 QLExpress 教程:患者随访画像多条件适配案例

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) 集合包含判断

六、注意事项

  1. 表达式安全 :生产环境需过滤表达式中的危险操作(如 System.exit(0)),可通过 ExpressRunneraddFunctionFilter 限制函数调用。
  2. 性能优化 :高频执行的表达式可通过 expressRunner.compileExpress(express) 预编译,提升执行效率。
  3. 异常处理 :表达式语法错误、变量缺失会抛出 QLExpressException,需捕获并处理。
  4. 变量名规范:上下文变量名需与表达式中的变量名一致,避免大小写问题(QLExpress 变量名区分大小写)。

七、总结

本教程通过 SpringBoot 整合 QLExpress,以患者随访画像为场景,实现了从单一条件到多条件组合、动态规则配置的完整案例。QLExpress 的核心优势在于规则配置化,无需修改代码即可调整业务逻辑,适用于风控、医疗、电商等需要动态规则的场景。

后续可扩展方向:

  1. 将规则存储到数据库,提供后台管理界面配置规则;
  2. 增加表达式语法校验,避免非法规则配置;
  3. 结合缓存预编译常用表达式,提升执行性能。

八、参考文档

相关推荐
HelloReader1 天前
从 Rocket 0.4 升级到 0.5一份实战迁移指南
后端·rust
ChrisitineTX1 天前
Spring Boot 3 + GraalVM Native Image 原理:从启动 10秒 到 0.05秒,AOT 编译到底干了什么?
java·spring boot·后端
CodeSheep1 天前
华为又招天才少年了。。
前端·后端·程序员
武子康1 天前
大数据-179 Elasticsearch 倒排索引与读写流程全解析:从 Lucene 原理到 Query/Fetch 实战
大数据·后端·elasticsearch
回家路上绕了弯1 天前
微信抢红包深度解析:从算法原理到高并发工程实现
分布式·后端
Hui Baby1 天前
Mq扩充队列提高并发
开发语言·后端·ruby
程序员爱钓鱼1 天前
Node.js 编程实战:全面理解异步错误处理
后端·node.js·trae
IT_陈寒1 天前
从混乱到优雅:这5个现代JavaScript技巧让你的代码性能提升50%
前端·人工智能·后端
qq_348231851 天前
完整的 Spring Boot + Redisson 分布式锁示例
spring boot·分布式·后端