【三十四】springboot+easyRule初识规则引擎

news2024/12/23 5:50:05

        代码场景:厂里有几个员工,现在厂长颁布了新的厂规关于薪资发放,如下:

  • 1、加班时长超过80小时的,一个小时10块钱;不满80小时的,不算加班。
  • 2、上班打卡迟到3次以下的不扣钱,3次以上的一次扣100。

        针对如上需求,是不是就可以通过写if-if判断来处理,但是如果规则变化呢,老板想只要迟到1次就扣1000,或者只要加班就100块钱一个小时呢,是不是只有改代码升级。 本章针对这个问题,通过规则引擎实现这个场景,实现规则配置化。

目录

一、准备工作

二、注解方式实现

三、yml配置方式实现


一、准备工作

1、表

2、依赖
<dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-core</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-mvel</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-spel</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.jeasy</groupId>
            <artifactId>easy-rules-jexl</artifactId>
            <version>4.1.0</version>
        </dependency>

二、注解方式实现

1、编写目的场景的规则
@Rule(name = "无效加班", description = "加班费的结算", priority = 1)
public class Rule1 {

    @Condition
    public boolean when(@Fact("time") double time) {
        return time<=80;
    }

    @Action
    public void then(@Fact("reason") StringBuffer reason) {
        reason.append("加班少于80小时,不发加班费;");
    }

}
@Rule(name = "有效加班", description = "加班费的结算", priority = 2)
public class Rule2 {

    @Condition
    public boolean when(@Fact("time") double time) {
        return time > 80;
    }

    @Action
    public void then(@Fact("time") double time,@Fact("reason") StringBuffer reason,@Fact("money") AtomicDouble money) {
        money.set(money.get()+10*(time-80));
        reason.append("加班费:").append(10*(time-80)).append(";");
    }

}
@Rule(name = "迟到警告", description = "迟到的惩罚", priority = 3)
public class Rule3 {

    @Condition
    public boolean when(@Fact("count") int count) {
        return count<=3;
    }

    @Action
    public void then(@Fact("count") int count, @Fact("money") AtomicDouble money, @Fact("reason") StringBuffer reason) {
        reason.append("迟到小于3次,暂时不扣钱;");
    }

}
@Rule(name = "迟到扣钱", description = "迟到的惩罚", priority = 3)
public class Rule4 {

    @Condition
    public boolean when(@Fact("count") int count) {
        return count>3;
    }

    @Action
    public void then(@Fact("count") int count, @Fact("money") AtomicDouble money, @Fact("reason") StringBuffer reason) {
        money.set(money.get() - (count-3)*100);
        reason.append("迟到大于3次,扣钱:").append((count - 3) * 100).append(";");
    }

}
  • @Rule标识该类为规则类
  • @Condition标识该方法为条件(一个rule类只能有一个) 
  • @Action标识该方法为条件满足为true后的执行方法
  • @Fact代表事实,标识入参,可以通过Fact的key值获取值
  • priority标识该rule的执行顺序
2、编写接口
@Slf4j
@Api(tags = "牛马管理接口")
@RestController
@RequestMapping("/staffController")
@AllArgsConstructor
public class StaffController {

    private final StaffMapper staffMapper;
    
    @ApiOperation(value = "计算工资")
    @PostMapping("/getSalary")
    public BaseResponse<Integer> getSalary() {
        // 初始化规则引擎
        RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(false);
        DefaultRulesEngine engine = new DefaultRulesEngine(parameters);
        engine.registerRuleListener(new MyRuleListener());

        // 注册规则进入引擎
        Rules rules = new Rules();
        rules.register(new Rule1());
        rules.register(new Rule2());
        rules.register(new Rule3());
        rules.register(new Rule4());
        //UnitRuleGroup unitRuleGroup = new UnitRuleGroup("myUnitRuleGroup", "myUnitRuleGroup");
        //unitRuleGroup.addRule(new Rule1());
        //unitRuleGroup.addRule(new Rule2());
        //unitRuleGroup.addRule(new Rule3());
        //unitRuleGroup.addRule(new Rule4());
        //rules.register(unitRuleGroup);

        List<Staff> list = staffMapper.selectList(new QueryWrapper<>());

        for (Staff staff : list) {
            AtomicDouble money = new AtomicDouble((Double.parseDouble(staff.getMoney())));
            double beforeMoney = money.get();
            StringBuffer reason = new StringBuffer();
            Facts facts = new Facts();
            facts.put("time", Double.parseDouble(staff.getTime()));
            facts.put("count", staff.getCount());
            facts.put("money", money);
            facts.put("reason", reason);
            engine.fire(rules, facts);

            Staff staffNew = staffMapper.selectById(staff.getId());
            staffNew.setFinalMoney(facts.get("money").toString());
            staffNew.setDetail(reason.toString());
            staffMapper.updateById(staffNew);

        }

        return RespGenerator.returnOK("成功");
    }

}
3、测试

根据结果可以看到数据正确。

现在假设我们厂长更改了需求:现在只要迟到超过3次,都没有加班费

        如上代码可以根据实际情况改造一下,当有需求所有规则要么全部执行,要么全部不执行时(只要有一个不满足就全部跳过执行),可以选用另一种方式UnitRuleGroup,改造如下:

@ApiOperation(value = "计算工资")
    @PostMapping("/getSalary")
    public BaseResponse<Integer> getSalary() {
        // 初始化规则引擎
        RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(false);
        DefaultRulesEngine engine = new DefaultRulesEngine(parameters);
        engine.registerRuleListener(new MyRuleListener());

        // 注册规则进入引擎
        Rules rules = new Rules();
        //rules.register(new Rule1());
        //rules.register(new Rule2());
        //rules.register(new Rule3());
        //rules.register(new Rule4());
        UnitRuleGroup unitRuleGroup = new UnitRuleGroup("myUnitRuleGroup", "myUnitRuleGroup");
        //unitRuleGroup.addRule(new Rule1());
        unitRuleGroup.addRule(new Rule2());
        unitRuleGroup.addRule(new Rule3());
        //unitRuleGroup.addRule(new Rule4());
        rules.register(unitRuleGroup);

        List<Staff> list = staffMapper.selectList(new QueryWrapper<>());

        for (Staff staff : list) {
            AtomicDouble money = new AtomicDouble((Double.parseDouble(staff.getMoney())));
            double beforeMoney = money.get();
            StringBuffer reason = new StringBuffer();
            Facts facts = new Facts();
            facts.put("time", Double.parseDouble(staff.getTime()));
            facts.put("count", staff.getCount());
            facts.put("money", money);
            facts.put("reason", reason);
            engine.fire(rules, facts);

            Staff staffNew = staffMapper.selectById(staff.getId());
            staffNew.setFinalMoney(facts.get("money").toString());
            staffNew.setDetail(reason.toString());
            staffMapper.updateById(staffNew);

        }

        return RespGenerator.returnOK("成功");
    }

结果如下:

 可以看到赵四加班了一个小时也没有加班费了。

三、yml配置方式实现

1、增加配置文件
---
name: '无效加班'
description: '加班费的结算'
priority: 1
condition: "time<=80"
actions:
  - "reason.append('加班少于80小时,不发加班费;');"
---
name: '有效加班'
description: '加班费的结算'
priority: 2
condition: "time>80"
actions:
  - "money.set(money.get()+10*(time-80));reason.append('加班费:').append(10*(time-80)).append(';');"
---
name: '迟到警告'
description: '迟到的惩罚'
priority: 1
condition: "count<=3"
actions:
  - "reason.append('迟到小于3次,暂时不扣钱;');"
---
name: '迟到扣钱'
description: '迟到的惩罚'
priority: 2
condition: "count>3"
actions:
  - "money.set(money.get() - (count-3)*1000);reason.append('迟到大于3次,扣钱:').append((count - 3) * 1000).append(';');"

每个rule之间通过---进行分割,可以在condition和actions下写java代码。 

2、改造接口
@ApiOperation(value = "计算工资")
    @PostMapping("/getSalary")
    public BaseResponse getSalary() throws Exception {
        // 初始化规则引擎
        RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(false);
        DefaultRulesEngine engine = new DefaultRulesEngine(parameters);
        engine.registerRuleListener(new MyRuleListener());

        // 注册规则进入引擎
        MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
        Rules rules = ruleFactory.createRules(new BufferedReader(new InputStreamReader(
                Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream("rules.yml")))));

        List<Staff> list = staffMapper.selectList(new QueryWrapper<>());

        for (Staff staff : list) {
            AtomicDouble money = new AtomicDouble((Double.parseDouble(staff.getMoney())));
            double beforeMoney = money.get();
            StringBuffer reason = new StringBuffer();
            Facts facts = new Facts();
            facts.put("time", Double.parseDouble(staff.getTime()));
            facts.put("count", staff.getCount());
            facts.put("money", money);
            facts.put("reason", reason);
            engine.fire(rules, facts);

            Staff staffNew = staffMapper.selectById(staff.getId());
            staffNew.setFinalMoney(facts.get("money").toString());
            staffNew.setDetail(reason.toString());

            staffMapper.updateById(staffNew);

        }

        return RespGenerator.returnOK("成功");
    }

 

假设当我们调整加班费时(1块钱一个小时),修改配置即可。

name: '有效加班'
description: '加班费的结算'
priority: 2
condition: "time>80"
actions:
  - "money.set(money.get()+1*(time-80));reason.append('加班费:').append(1*(time-80)).append(';');"

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

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

相关文章

期权交易误区分享:喜欢重仓!

今天带你了解期权交易误区分享&#xff1a;喜欢重仓&#xff01;期权交易虽然吸引人&#xff0c;但也有不少容易掉进去的坑。 有的投资者被单个期权的百倍利润吸引&#xff0c;喜欢“一口吃成胖子”。 重仓买入虚值和重度虚值的期权&#xff0c;当标的有大涨或大跌时&#xf…

零基础Opencv学习(一)

一、显示图片 #include "opencv2/opencv.hpp" #include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp"cv::Mat image cv::imread("E:/OpencvStudyTest/1.png"…

量化投资策略与技术学习PART1.1:量化选股之再谈多因子模型(二)

在上一个多因子模型中&#xff0c;我手动对各个因子进行了回测&#xff0c;但是数据结果并不是十分理想&#xff0c;难道基本面指标真的和股票走势关系不大么&#xff1f; 这里我还是准备再测试一下&#xff0c;策略如下&#xff1a; &#xff08;1&#xff09;首先我获取了一下…

Doped code 介绍

doped是一款Python软件&#xff0c;用于缺陷超单元计算的生成、前/后处理和分析&#xff0c;以高效、可重复、用户友好、功能强大且完全可定制的方式实施缺陷模拟工作流程。 https://doped.readthedocs.io/en/latest/ 教程页面提供了演示代码功能和用法&#xff0c; 该软件包的…

考试评分系统设计与实现/基于django的在线考试系统

摘要 随着互联网技术的不断发展&#xff0c;各行各业的工作学习的模式都发生了不小的变化&#xff0c;们通过互联网技术不仅能够提高工作效率还能够降低出错的几率。而对于考试评分&#xff0c;一个专业的系统可以帮助管理者更加有效管理在考试评分&#xff0c;可以帮助提高克服…

vaspup2.0介绍

实时软件库:https://github.com/kavanase/vaspup2.0 vaspup是一个bash脚本集合&#xff0c;可以有效地生成和分析VASP收敛测试计算。 最初的vaspup是由Alex Ganose开发&#xff0c;用于基态能量收敛测试和POTCAR生成。 vaspup2.0的功能包括: 基态能量相对于ENCUT和k点密度的收敛…

Linux 配置wireshark 分析thread 使用nRF-Sniffer dongle

Linux 配置wireshark nRF-Sniffer-for-802.15.4 1.下载固件和配置文件 https://github.com/NordicSemiconductor/nRF-Sniffer-for-802.15.4 2.烧写固件 使用nRF Connect for Desktop 中的 programmer 4.3烧写 https://www.nordicsemi.com/Products/Development-tools/nrf-conne…

python07-单元测试框架unittest1-1

前言 单元测试是软件开发中不可或缺的一部分&#xff0c;可以帮助开发人员确保代码的正确性、可靠性和稳定性&#xff0c;python是一种广泛使用的程序语言&#xff0c;提供了多种单元测试工具&#xff0c;最常用的是unittest。本文将介绍unittest package, 包括如何编写测试Tes…

干货分享|分享一款高效的文件搜索工具 Everything

介绍&#xff1a;Everything软件是一款高效的文件搜索工具&#xff0c;主要用于快速定位计算机中的文件和文件夹。 官网地址&#xff1a;voidtools 下载方法&#xff1a;只需依据电脑配置与个人需求选择合适的版本下载&#xff0c;安装过程中一路默认即可轻松完成设置。 注&…

C语言刷题日记(附详解)(3)

一、选填部分 第一题: 以下的变量定义语句中&#xff0c;合法的是( ) A. byte a 128; B. boolean b null; C. long c 123L; D. float d 0.9239; 思路提示&#xff1a;观察选项时不要马虎&#xff0c;思考一下各种类型变量的取值范围&#xff0c;以及其初始化的形式是…

基于yolov8的课堂行为检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的课堂行为检测系统是现代教育技术的创新应用&#xff0c;该系统利用YOLOv8这一先进的深度学习算法&#xff0c;实现了对学生课堂行为的自动、高效和精准监测。YOLOv8在目标检测领域以其卓越的性能和速度著称&#xff0c;通过对学生上课视频或实时摄像…

深入学习AI大模型服务平台的选型应用相关技术和问诊咨询

AI大模型服务平台的选择和应用涉及到多个技术层面和业务需求的考量。下面我会详细介绍几个关键的技术点和应用场景&#xff0c;帮助您更好地理解和选择AI大模型服务平台。 技术选型 1.1 大模型种类 语言模型&#xff1a;如BERT、GPT-3、文心一言等&#xff0c;适用于自然语言…

自动驾驶---什么是Frenet坐标系?

1 背景 为什么提出Frenet坐标系&#xff1f;Frenet坐标系的提出主要是为了解决自动驾驶系统在路径规划的问题&#xff0c;它基于以下几个原因&#xff1a; 符合人类的驾驶习惯&#xff1a; 人类驾驶员在驾驶过程中&#xff0c;通常不会关心自己距离起点的横向和纵向距离&#x…

C++学习笔记——约瑟夫问题

一、题目描述 二、代码 #include<iostream>using namespace std;int main() {int n;//新建变量n int m;//新建变量m cin >>n;//键盘输入n cin >>m;//键盘输入m int a[n];//初始化数组 for(int i0;i<n;i){a[i] i1;}int* p &a[0];//指针指向数组的第一…

AIGC大师秘籍:六步法打造精准文字提示词

&#x1f31f; 引言&#xff1a; 在AIGC&#xff08;人工智能生成内容&#xff09;的奇幻世界里&#xff0c;编写优质的文字提示词&#xff08;Prompt&#xff09;就像是掌握了一门魔法&#xff0c;能够召唤出高质量的内容。今天&#xff0c;我将向你揭露一个六步法的秘密&…

【LeetCode】918. 环形子数组的最大和

1. 题目 2. 分析 单调队列的经典应用。 3. 代码 class Solution:def maxSubarraySumCircular(self, nums: List[int]) -> int:# 使用单调队列的解法# 转换为求区间长度不超过len(nums)内的最大和k len(nums)nums nums nums# 求出前缀和prefixSum [0] * len(nums) pre…

打印单据时每次都弹出对话框,如何取消对话框,实现快速打印?

打印管家婆单据时&#xff0c;每次都打印单据时都弹出一个打印对话框&#xff0c;可不可以跳过一步&#xff0c;实现快速打印呢&#xff1f;答案是可以的&#xff0c;具体操作步骤如下&#xff1a; 1、随意打开一张单据&#xff0c;点击打印按钮旁边的小三角&#xff0c;在菜单…

二刷代码随想录训练营Day 46|647. 回文子串、516.最长回文子序列、动态规划总结篇

1.回文子串 代码随想录 (programmercarl.com) 视频&#xff1a;动态规划&#xff0c;字符串性质决定了DP数组的定义 | LeetCode&#xff1a;647.回文子串_哔哩哔哩_bilibili 代码&#xff1a; class Solution { public:int countSubstrings(string s) {vector<vector<bo…

Unet改进12:添加PCONV||减少冗余计算和同时存储访问

本文内容:添加PCONV 目录 论文简介 1.步骤一 2.步骤二 3.步骤三 4.步骤四 论文简介 为了设计快速的神经网络,许多工作都集中在减少浮点运算(FLOPs)的数量上。然而,我们观察到FLOPs的这种减少并不一定会导致类似程度的延迟减少。这主要源于低效率的每秒浮点操作数(FLOP…

[数据集][目标检测]课堂行行为检测数据集VOC+YOLO格式4065张12类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4065 标注数量(xml文件个数)&#xff1a;4065 标注数量(txt文件个数)&#xff1a;4065 标注…