Aviator规则引擎简单使用

文档:AviatorScript 编程指南(5.0) - 1. 介绍 - 《AviatorScript 文档 - 帮助手册 & 教程》 - 极客文档

规则引擎是一个更复杂的系统,它的核心是将业务规则与应用程序代码分离,并基于大量数据和规则进行自动化推理和决策。

市场上有很多规则引擎,比如Drools、EasyRules、QLExpress、Aviator、Groovy等

严格来说,aviator属于表达式计算框架,其语法和JavaScript很像。其具体语法可以参考上述的帮助文档。

本文通过一个具体的例子来学习Aviator使用。

网上看到一个案例“非VIP用户满200减30,VIP用户满150减40,且周二全场额外95折”,针对该需求,使用java的if语法进行硬编码也可以实现,本例使用aviator实现。

导入jar

<dependency>
            <groupId>com.googlecode.aviator</groupId>
            <artifactId>aviator</artifactId>
            <version>5.3.3</version>
        </dependency>

测试代码

package com.qfedu.aviatortes;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class DiscountRuleEngine {

    // 用户类型
    public enum UserType {
        VIP, NON_VIP
    }

    /**
     * 计算最终价格
     *
     * @param userType     用户类型
     * @param amount       原始金额
     * @param purchaseDate 购买日期(用于判断是否是周二)
     * @return 最终价格
     */
    public static double calculateFinalPrice(UserType userType, double amount, Date purchaseDate) {
        // 设置脚本中的参数的值
        Map<String, Object> env = new HashMap<>();
        env.put("userType", userType.name());
        env.put("amount", amount);
        env.put("isTuesday", isTuesday(purchaseDate));

        // 定义规则表达式
        String ruleExpression =
                "let discount = 0; " +
                        "if (userType == 'VIP') { " +
                        "   if (amount >= 150) { " +
                        "       discount = 40; " +
                        "   } " +
                        "} else { " +
                        "   if (amount >= 200) { " +
                        "       discount = 30; " +
                        "   } " +
                        "} " +
                        "let tempAmount = amount - discount; " +
                        "if (isTuesday) { " +
                        "   tempAmount * 0.95 " +
                        "} else { " +
                        "   tempAmount " +
                        "}";

        try {
            // 编译表达式
            Expression expression = AviatorEvaluator.compile(ruleExpression);
            // 执行表达式
            Object result = expression.execute(env);
            return Double.parseDouble(result.toString());
        } catch (Exception e) {
            throw new RuntimeException("规则计算失败", e);
        }
    }

    /**
     * 判断是否是周二
     */
    private static boolean isTuesday(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("u");
        String dayOfWeek = sdf.format(date);
        return "2".equals(dayOfWeek); // 周一是1,周二是2
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) {
        // 测试数据
        Date tuesday = parseDate("2025-08-19"); // 假设这是周二
        Date notTuesday = parseDate("2025-08-20"); // 假设这不是周二

        // 测试用例
        System.out.println("VIP用户,周二,金额160: " +
                calculateFinalPrice(UserType.VIP, 160, tuesday)); // 应输出: (160-40)*0.95=114

        System.out.println("VIP用户,非周二,金额160: " +
                calculateFinalPrice(UserType.VIP, 160, notTuesday)); // 应输出: 160-40=120

        System.out.println("非VIP用户,周二,金额210: " +
                calculateFinalPrice(UserType.NON_VIP, 210, tuesday)); // 应输出: (210-30)*0.95=171

        System.out.println("非VIP用户,非周二,金额210: " +
                calculateFinalPrice(UserType.NON_VIP, 210, notTuesday)); // 应输出: 210-30=180

        System.out.println("VIP用户,周二,金额140: " +
                calculateFinalPrice(UserType.VIP, 140, tuesday)); // 应输出: 140*0.95=133
    }

    private static Date parseDate(String dateStr) {
        try {
            return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
        } catch (Exception e) {
            return new Date();
        }
    }
}

注意:let是表达式中定义的变量,用于进行中间运算。userType、amount、isTuesday没有通过let进行定义,这些变量的值需要调用脚本时进行传递

直接通过字符串设置表达式,可读性不好,我们可以定义一个.av后缀的文件,比如在resources目录下新建discount.av

## 定义折扣规则
let discount = 0;

if (userType == 'VIP') {
    if (amount >= 150) {
        discount = 40;
    }
} else {
    if (amount >= 200) {
        discount = 30;
    }
}

let tempAmount = amount - discount;

if (isTuesday) {
    tempAmount * 0.95
} else {
    tempAmount
}

测试代码修改为:

package com.qfedu.aviatortes;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class DiscountRuleEngine2 {

    // 用户类型
    public enum UserType {
        VIP, NON_VIP
    }

    /**
     * 计算最终价格
     *
     * @param userType     用户类型
     * @param amount       原始金额
     * @param purchaseDate 购买日期(用于判断是否是周二)
     * @return 最终价格
     */
    public static double calculateFinalPrice(UserType userType, double amount, Date purchaseDate) {
        Map<String, Object> env = new HashMap<>();
        env.put("userType", userType.name());
        env.put("amount", amount);
        env.put("isTuesday", isTuesday(purchaseDate));

        try {
            // 获取文件路径
            Resource resource = new ClassPathResource("discount.av");
            String filePath = resource.getFile().getAbsolutePath();
            System.out.println("filePath: " + filePath);
            // 通过文件加载表达式规则
            Expression expression = AviatorEvaluator.getInstance().compileScript(
                    filePath);
            // 调用规则
            Object result = expression.execute(env);
            return Double.parseDouble(result.toString());
        } catch (Exception e) {
            throw new RuntimeException("规则计算失败", e);
        }
    }

    /**
     * 判断是否是周二
     */
    private static boolean isTuesday(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("u");
        String dayOfWeek = sdf.format(date);
        return "2".equals(dayOfWeek); // 周一是1,周二是2
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) {
        // 测试数据
        Date tuesday = parseDate("2025-08-19"); // 周二
        Date notTuesday = parseDate("2025-08-20"); // 不是周二

        // 测试用例
        System.out.println("VIP用户,周二,金额160: " +
                calculateFinalPrice(UserType.VIP, 160, tuesday)); // 应输出: (160-40)*0.95=114

        System.out.println("VIP用户,非周二,金额160: " +
                calculateFinalPrice(UserType.VIP, 160, notTuesday)); // 应输出: 160-40=120

        System.out.println("非VIP用户,周二,金额210: " +
                calculateFinalPrice(UserType.NON_VIP, 210, tuesday)); // 应输出: (210-30)*0.95=171

        System.out.println("非VIP用户,非周二,金额210: " +
                calculateFinalPrice(UserType.NON_VIP, 210, notTuesday)); // 应输出: 210-30=180

        System.out.println("VIP用户,周二,金额140: " +
                calculateFinalPrice(UserType.VIP, 140, tuesday)); // 应输出: 140*0.95=133
    }

    private static Date parseDate(String dateStr) {
        try {
            return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
        } catch (Exception e) {
            return new Date();
        }
    }
}

关于aviator的表达式更多语法,可以参考aviator源码中的examples学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值