RuleEngine规则引擎底层改造AviatorScript 之函数执行

news2024/10/7 14:31:47

https://gitee.com/aizuda/rule-engine-open
需求:使用上述开源框架进行改造,底层更换成AviatorScript ,函数实现改造。
原本实现方式

    @Override
    public Object run(ExecuteFunctionRequest executeTestRequest) {
        Integer functionId = executeTestRequest.getId();
        RuleEngineFunction engineFunction = this.ruleEngineFunctionManager.getById(functionId);
        if (engineFunction == null) {
            throw new ApiException(ErrorCodeEnum.RULE9999404.getCode(),"不存在函数:{}", functionId);
        }
        String executor = engineFunction.getExecutor();
        if (this.applicationContext.containsBean(executor)) {
            Object abstractFunction = this.applicationContext.getBean(executor);
            // 函数测试均为固定值
            List<ParamValue> paramValues = executeTestRequest.getParamValues();
            Map<String, Value> param = new HashMap<>(paramValues.size());
            for (ParamValue paramValue : paramValues) {
                Constant constant = new Constant(paramValue.getValue(), ValueType.getByValue(paramValue.getValueType()));
                param.put(paramValue.getCode(), constant);
            }
            Function function = new Function(functionId, abstractFunction, ValueType.STRING, param);
            // 无规则参数 input==null
            return function.getValue(null, new RuleEngineConfiguration());
        } else {
            throw new ApiException("容器中找不到{}函数", executor);
        }
    }

可以看到的是,他们首先拿到函数id,然后判断函数是否存在,然后去已经预制好的数据库表中去读取固定的函数名称,然后根据入参进行入参的判断,然后是执行。
因为我们要进行底层改造,我也就是了解了他的实现方式,并没有深入的去了解他的底层是如何实现的。
我们的话,可能入参没有那么复杂,我是自己新建了一个表来存储入参的,具体的实现也是不一样。

@Function
public class LetterToLowerCaseFunction extends AbstractFunction {

    @Executor
    public String executor(@Param(value = "letter",required = false) String letter) {
        if (letter == null) {
            return null;
        }
        return letter.toLowerCase();
    }

    @Override
    public AviatorObject call(Map<String, Object> env, AviatorObject arg1) {
        String letter = String.valueOf(arg1.getValue(env));
        if (letter == null) {
            return null;
        }

        return new AviatorString(letter.toLowerCase());
    }
    @Override
    public String getName() {
        return "letterToLowerCaseFunction";
    }
}

这是他们原本的函数实现,是通过Function接口,将实体类注册到spring框架中,然后将类的名字预制到数据中,通过读取某个函数将函数名读取出来,处理问题。

public class TestAviator {
    public static void main(String[] args) {
            //注册函数
            AviatorEvaluator.addFunction(new AddFunction());
            System.out.println(AviatorEvaluator.execute("add(1, 2)"));           // 3.0
            System.out.println(AviatorEvaluator.execute("add(add(1, 2), 100)")); // 103.0
        }
    }
    class AddFunction extends AbstractFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, 
                                  AviatorObject arg1, AviatorObject arg2) {
            Number left = FunctionUtils.getNumberValue(arg1, env);
            Number right = FunctionUtils.getNumberValue(arg2, env);
            return new AviatorDouble(left.doubleValue() + right.doubleValue());
        }
        public String getName() {
            return "add";
        }
    }

AviatorScript 这个是他官方的一个实例文档,可以看的出来实现方式大大的不一样了
其实这个项目的首先的问题是,我们需要将rule-engine 的三个项目合到一个项目中,基于这块只有繁琐但是没有难度我这边就不再提了。

@Override
    public Object runFunction(ExecuteFunctionRequest executeTestRequest) throws Exception{
        Integer functionId = executeTestRequest.getId();
        RuleEngineFunction2 engineFunction = this.ruleEngineFunction2Manager.getById(functionId);

        if (engineFunction == null) {
            throw new ApiException(ErrorCodeEnum.RULE9999404.getCode(),"不存在函数:{}", functionId);
        }

        //获取设置对应的方法名
        String className = engineFunction.getClassName();
        String functionName = engineFunction.getFunctionName();

        if (this.applicationContext.containsBean(className)) {

            AviatorFunction abstractFunction = (AviatorFunction)this.applicationContext.getBean(className);

            QueryWrapper<RuleEngineFunctionParam2> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("function_id", engineFunction.getId());

            //拿到对应的参数集合,参数集合
            List<RuleEngineFunctionParam2> list = ruleEngineFunctionParam2Manager.list(queryWrapper);

            // 入参参数集合
            List<ParamValue> paramValues = executeTestRequest.getParamValues();

            HashMap hashMap = composeParams(list, paramValues);

            AviatorEvaluator.addFunction(abstractFunction);

            String params = "";

            for (RuleEngineFunctionParam2 ruleEngineFunctionParam2 : list) {
                params = params +"arg1,";
            }

            params = params.substring(0, params.length() - 1);

            String expression = functionName+"("+params+")";

            Object execute = AviatorEvaluator.execute(expression, hashMap);

            log.error(execute.toString());

            return execute;
        } else {
            throw new ApiException("容器中找不到{}函数", className+functionName);
        }
    }

    public HashMap composeParams(List<RuleEngineFunctionParam2> list,List<ParamValue> paramValues){

        HashMap params = new HashMap();
        for (int i = 0; i < list.size(); i++) {
            RuleEngineFunctionParam2 ruleEngineFunctionParam2 = list.get(i);
            String paramCode = ruleEngineFunctionParam2.getParamCode();

            for (int j = 0; j < paramValues.size(); j++) {
                ParamValue paramValue = paramValues.get(j);
                String code = paramValue.getCode();
                if (paramCode.equals(code)) {
                    if(ruleEngineFunctionParam2.getValueType().equals("STRING")){
                        params.put(paramCode, paramValue.getValue());
                    }else if(ruleEngineFunctionParam2.getValueType().equals("COLLECTION")){
                        String arr = paramValue.getValue();
                        List<String> items = Arrays.asList(arr.split(","));
                        params.put(paramCode, items);
                    }else if(ruleEngineFunctionParam2.getValueType().equals("NUMBER")){
                        int number = Integer.valueOf(paramValue.getValue());
                        params.put(paramCode, number);
                    }
                }
            }
        }
        return params;
    }

这一块代码是我写的函数执行的底层改造,唯一的问题就是在于传参,不过在当时看来是没有问题的

@Function
public class LetterToUpperCaseFunction extends AbstractFunction {

    @Executor
    public String executor(@Param(value = "letter",required = false) String letter) {
        if (letter == null) {
            return null;
        }
        return letter.toUpperCase();
    }

    @Override
    public AviatorObject call(Map<String, Object> env,AviatorObject arg1) {
        String letter = env.get("letter").toString();
        if (letter == null) {
            return null;
        }

        return new AviatorString(letter.toUpperCase());
    }

    @Override
    public String getName() {
        return "letterToUpperCaseFunction";
    }
}

在执行以上函数代码的时候一点问题都没有,那么问题出在了哪里呢,
String letter = env.get("letter").toString();这个取值,因为我这边取了一个巧,我在发现入参的类型我不好判断之后,我放弃了使用顺序确认入参这个形势,我直接使用的key-value的形势,这样可以在传参的时候一点问题都不会,如果前端传过来的入参的顺序出现问题的情况下,也会如我计划的一样执行,但是确实面临了一个问题,因为后面需要实现一个公式规则的功能
在这里插入图片描述
这个的具体研发可以放在下一篇进行讲述,然后就发现了,因为这个牵扯到给函数传值以及给代码块传值,所以陷入了一个死局,需要进行函数入参的调整,很麻烦,也不是不能做。
后面就转换了实现思路,不在使用String letter = env.get("letter").toString();方式去获取参数中的入参,尊重他人命运,前端传错了前端改吧,不过事实证明也没有出现问题,有点杞人忧天的意思了。

    @Override
    public Object runFunction(ExecuteFunctionRequest executeTestRequest) throws Exception{
        Integer functionId = executeTestRequest.getId();
        RuleEngineFunction2 engineFunction = this.ruleEngineFunction2Manager.getById(functionId);

        if (engineFunction == null) {
            throw new ApiException(ErrorCodeEnum.RULE9999404.getCode(),"不存在函数:{}", functionId);
        }

        //获取设置对应的方法名
        String className = engineFunction.getClassName();
        String functionName = engineFunction.getFunctionName();

        if (this.applicationContext.containsBean(className)) {

            AviatorFunction abstractFunction = (AviatorFunction)this.applicationContext.getBean(className);

            QueryWrapper<RuleEngineFunctionParam2> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("function_id", engineFunction.getId());

            // 入参参数集合
            List<ParamValue> paramValues = executeTestRequest.getParamValues();

            AviatorEvaluator.addFunction(abstractFunction);

            String params = "";

            for (int i = 0; i < paramValues.size(); i++) {
                ParamValue paramValue = paramValues.get(i);
                String value = paramValue.getValue();
                if (paramValue.getValueType().equals("STRING"))
                {
                    params = params + "'" + value +"'" + ",";
                }else if (paramValue.getValueType().equals("NUMBER")){
                    params = params + value + ",";
                }else if (paramValue.getValueType().equals("COLLECTION")){
                    params = params + "'" + value +"'" + ",";
                }
            }

            params = params.substring(0, params.length() - 1);

            String expression = functionName+"("+params+")";

            Object execute = AviatorEvaluator.execute(expression);

            log.error(execute.toString());

            return execute;
        } else {
            throw new ApiException("容器中找不到{}函数", className+functionName);
        }
    }

修改函数显示底层,使用直接参数的方式,后端直接处理参数,方便后面代码块执行,大概是组成add(1, 2)格式。

@Function
public class LetterToUpperCaseFunction extends AbstractFunction {

    @Executor
    public String executor(@Param(value = "letter",required = false) String letter) {
        if (letter == null) {
            return null;
        }
        return letter.toUpperCase();
    }

    @Override
    public AviatorObject call(Map<String, Object> env,AviatorObject arg1) {
        String letter = FunctionUtils.getStringValue(arg1, env);
        if (letter == null) {
            return null;
        }

        return new AviatorString(letter.toUpperCase());
    }

    @Override
    public String getName() {
        return "letterToUpperCaseFunction";
    }
}

函数获取参数底层也进行响应的修改。

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

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

相关文章

计算机是如何工作的6

因此&#xff0c;往往就把“并行”和“并发”统称为“并发” 对应的编程方式&#xff08;解决一个问题&#xff0c;同时搞多个任务来执行&#xff0c;共同协作解决&#xff09;就称为“并发” 此处cpu的百分数&#xff0c;就是你的进程在cpu舞台上消耗时间的百分比 如果有一…

java实现UDP数据交互

1、回显服务器 服务器端 import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException;public class UDP_Server {private DatagramSocket socketnull;public UDP_Server(int port) throws SocketExcepti…

Android图形显示架构概览

图形显示系统作为Android系统核心的子系统&#xff0c;掌握它对于理解Android系统很有帮助&#xff0c;下面从整体上简单介绍图形显示系统的架构&#xff0c;如下图所示。 这个框架只包含了用户空间的图形组件&#xff0c;不涉及底层的显示驱动。框架主要包括以下4个图形组件。…

【JavaWeb】Day37.MySQL概述——数据库设计-DML

数据库操作-DML DML英文全称是Data Manipulation Language(数据操作语言)&#xff0c;用来对数据库中表的数据记录进行增、删、改操作。 1.增加(insert) insert语法&#xff1a; 向指定字段添加数据 insert into 表名 (字段名1, 字段名2) values (值1, 值2); 全部字段添加数据…

CGAL的交叉编译-androidlinux-arm64

由于项目算法需要从Linux移植到android&#xff0c;原先的CGAL库也需要进行移植&#xff0c;先现对CGAL的移植过程做下记录&#xff0c;主要是其交叉编译的过程.。 前提条件&#xff1a; 1、主机已安装NDK编译器&#xff0c;版本大于19 2、主机已安装cmake 和 make 3、主机…

从零自制docker-8-【构建实现run命令的容器】

文章目录 log "github.com/sirupsen/logrus"args...go moduleimport第三方包失败package和 go import的导入go build . 和go runcli库log.SetFormatter(&log.JSONFormatter{})error和nil的关系cmd.Wait()和cmd.Start()arg……context.Args().Get(0)syscall.Exec和…

XC7A35T-2FGG484 嵌入式FPGA现场可编程门阵列 Xilinx

XC7A35T-2FGG484 是一款由Xilinx&#xff08;赛灵思&#xff09;制造的FPGA&#xff08;现场可编程门阵列&#xff09;芯片 以下是XC7A35T-2FGG484 的主要参数&#xff1a; 1. 系列&#xff1a;Artix-7 2. 逻辑单元数量&#xff1a;33280个 3. 工艺技术&#xff1a;28nm 4. …

element问题总结之el-table使用fixed中 header换行后固定行错位问题/固定列下陷问题

固定列下陷问题 效果图问题描述解决方案1、为table添加ref2、调用节点重新自适应方法doLayout3、在操作表头的时候触发的函数header-dragend绑定doLayout方法4、成功解决 效果图 问题描述 在使用el-table的fixed中&#xff0c;发现如果header拖拽文本折行的时候会出现下陷 解…

Go操作Kafka之kafka-go

Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;本文介绍了如何使用kafka-go这个库实现Go语言与kafka的交互。 Go社区中目前有三个比较常用的kafka客户端库 , 它们各有特点。 首先是IBM/sarama&#xff08;这个库已经由Shopify转给了IBM&#xff09;&#xff0c;之…

TCP/IP协议、HTTP协议和FTP协议等网络协议简介

文章目录 一、常见的网络协议二、TCP/IP协议1、TCP/IP协议模型被划分为四个层次2、TCP/IP五层模型3、TCP/IP七层模型 三、FTP网络协议四、Http网络协议1、Http网络协议简介2、Http网络协议的内容3、HTTP请求协议包组成4、HTTP响应协议包组成 一、常见的网络协议 常见的网络协议…

uniapp如何配置后使用uni.chooseLocation等地图位置api

在uniapp中想要使用uni.getLocation、uni.chooseLocation ……api的时候我们需要在小程序就开启配置&#xff0c;不然无法使用。 第一步&#xff1a;首先找到manifest.json 第二步&#xff1a;点击源码视图 第三步&#xff1a;在 mp-weixin 加入下面代码 "permission&…

DC-2靶机知识点

知识点总结 1.IP访问与域名访问 2.端口扫描 3.目录扫描 4.cewl密码生成器 5.指纹探测 6.爆破ssh 7.msf的使用 8.rbash逃逸 9.git提权 靶机&#xff0c;攻击机就不多说了&#xff0c;给个靶机地址 https://download.vulnhub.com/dc/DC-2.zip 环境配置 因为访问该靶机…

Python数学建模学习-莱斯利(Leslie)种群模型

Leslie模型是一种用于离散时间的生物种群增长模型&#xff0c;经常用于描述年龄结构对种群增长的影响。在1945年&#xff0c;人口生态学家Patrick H. Leslie&#xff08;莱斯利&#xff09;为了研究具有离散年龄结构的种群&#xff0c;特别是对于有不同年龄阶段的生物&#xff…

鸿蒙OS开发实战:【自动化测试框架】使用指南

概述 为支撑HarmonyOS操作系统的自动化测试活动开展&#xff0c;我们提供了支持JS/TS语言的单元及UI测试框架&#xff0c;支持开发者针对应用接口进行单元测试&#xff0c;并且可基于UI操作进行UI自动化脚本的编写。 本指南重点介绍自动化测试框架的主要功能&#xff0c;同时…

三行命令解决Ubuntu Linux联网问题

一开始我找到官方文档描述可以通过命令行连接到 WiFi 网络&#xff1a;https://cn.linux-console.net/?p10334#google_vignette 但是我的 $ ls /sys/class/net 命令无法显示无线网络接口&#xff0c;那么没有无线网卡&#xff1f; 所以我参照其他的博客在终端超级用户的权…

JUC:ScheduledThreadPoolExecutor 延迟任务线程池的使用

文章目录 ScheduledThreadPoolExecutortimer&#xff08;不建议用&#xff09;ScheduledThreadPoolExecutor处理异常应用 ScheduledThreadPoolExecutor timer&#xff08;不建议用&#xff09; timer也可以进行延迟运行&#xff0c;但是会有很多问题。 比如task1运行时间超过…

FOR循环

oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 前面两种循环都要根据条件是否成立而确定循环体的执行&#xff0c;具体循环体执行多少次事先并不知道。 FOR 循环可以控制循环执行的次数&#xff0c;由循环变量控制循环体的…

学透Spring Boot — 005. 深入理解 Spring Boot Starter 依赖管理

前面的文章直观的展示了&#xff0c;使用Spring Boot 集成 Hibernate 和手动集成 Hibernate 之间差距。 一个比喻 工作中使用过Spring Boot一段时间后&#xff0c;我越来越感觉到Spring Boot Starter带来的便利性。 使用手动集成 Hibernate&#xff0c; 就像去电脑城配电脑&…

二叉树的遍历的递归与非递归算法

一.二叉树的遍历&#xff1a; 按照一定规律对二叉树的每个结点进行访问且仅访问一次&#xff1b; 这里的访问&#xff1a;可以是计算二叉树中的结点数据&#xff0c;打印该结点的信息&#xff0c;也可以是对结点进行的任何其它操作&#xff01; 为什么需要遍历二叉树&#x…

EditPlus来啦(免费使用!)

hello&#xff0c;我是小索奇 今天推荐一款编辑器&#xff0c;是索奇学习JavaSE时入手滴&#xff0c;非常好用哈&#xff0c;小索奇还是通过老杜-杜老师入手滴&#xff0c;相信很多人也是通过老杜认识嘞&#xff0c;来寻找破解版或者准备入手这个间接使用的编辑器~ EditPlus是…