使用QLExpress动态制定计算公式
QLExpress的使用
最近公司要统计一些数据,需要定义一些统计数据的计算公式,然后通过计算公式统计数据。于是我去找了找有没有什么好用的工具。说实话,现在做开发,网上能找到许多实用的工具,而且还是开源的,非常感谢大佬们的分享。今天要说的是QLExpress。
QLExpress的简单介绍
QLExpress由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。它具有以下特性:
- 线程安全。
- 高效执行。
- 弱类型脚本语言。
- 安全控制。
- 代码精简,依赖最小。
简单的例子
- 添加依赖
<dependency><groupId>com.alibaba</groupId><artifactId>QLExpress</artifactId><version>3.2.0</version>
</dependency>
- 添加代码
public static void main(String[] args) throws Exception{String express = "2 * 3 / 2 + 4 - 5";ExpressRunner runner = new ExpressRunner();Object result = runner.execute(express,null, null, false,false);// 输出结果,结果为2,使用的时候还是挺方便的System.out.println("计算公式的结果:" + result);
}/*** 以下复制于官网的说明* * 执行一段文本* @param expressString 程序文本* @param context 执行上下文,可以扩展为包含ApplicationContext* @param errorList 输出的错误信息List* @param isCache 是否使用Cache中的指令集,建议为true* @param isTrace 是否输出详细的执行指令信息,建议为false* @param aLog 输出的log* @return* @throws Exception*/
Object execute(String expressString, IExpressContext<String,Object> context,List<String> errorList, boolean isCache, boolean isTrace, Log aLog);
当然,这只是一个简单的例子,QLExpress可不止这样。
实战场景
背景之前也说过了,公式需要先设置一个计算公式,而计算公式可不是像"2 * 3 / 2 + 4 - 5"这样直接将需要计算的值直接写死在公式里的,而是像这样:“avg(item_code_f,item_code_b) +item_code_a ÷ item_code_k - 100”,item_code_f这类数据其实就是数据表里的唯一编码。
计算的时候需要先把通过编码去查找对应的数据值,然后带入公式。其中要注意的是avg是我们自定义的计算符号,意为计算平均值。而且为了用户方便理解,乘法计算的符号是用×而不是*,除法则是÷而不是/,所以这些东西都需要自己去处理。
于是我整理了一下思路,想出了解决的办法,下面是我写的一个demo,可能有些地方略显粗糙,但是我总不能把公司代码直接贴出来,将就着看吧 T_T。
- 表结构
CREATE TABLE `calculation_rules` (`id` varchar(32) CHARACTER SET utf8 NOT NULL COMMENT '主键',`module_id` varchar(32) CHARACTER SET utf8 NOT NULL COMMENT '数据板块id',`rule` varchar(255) CHARACTER SET utf8 NOT NULL COMMENT '计算公式',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='计算公式表';
CREATE TABLE `data_item_table` (`id` varchar(32) NOT NULL COMMENT '主键',`item_code` varchar(10) NOT NULL COMMENT '数据项编码',`item_name` varchar(255) NOT NULL COMMENT '数据项名称',PRIMARY KEY (`id`),UNIQUE KEY `uk_item_code` (`item_code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='数据项表';
CREATE TABLE `commodity_price` (`id` varchar(32) NOT NULL COMMENT '主键',`item_code` varchar(32) NOT NULL COMMENT '数据项编码',`insert_time` datetime NOT NULL COMMENT '日期',`amount` decimal(13,2) NOT NULL COMMENT '数量/金额,这里反正根据需求,就是一个计算要用到的值',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='商品价格表';
- 如何通过表id去查找对应的数据值,然后带入公式。
public static void main(String[] args) {// 统计时,会传递日期和数据板块id,数据板块id的用处就是获取计算公式// 假如这就是我们获取到的某一个数据板块的公式String str = "avg(item_code_f,item_code_b) +item_code_a ÷ item_code_k - 100";// 处理一下÷和×,毕竟是以*和/作为乘法除法符号的str = str.replace("÷","/");str = str.replace("×","*");// 定义一个正则表达式,过滤掉计算符号String regex = "[()*+/-]";Pattern p = Pattern.compile(regex);Matcher m = p.matcher(str);List<String> list = new ArrayList<>();// 这里注意要先转成Set然后再转成List,因为有可能计算的时候要使用一个数据项的值多次,但是其实都是同一个值list.addAll(Arrays.asList(m.replaceAll(" ").split(" ")).stream().filter(s->{// 通过流来单独过滤avg和数字return !s.equals("avg")&&!s.equals("")&&!isNumeric(s);}).collect(Collectors.toSet()));// 输出一下结果:[item_code_f,item_code_b,item_code_a,item_code_k],这样就成功的将数据项编码给分离出来System.out.println(list.toString());// 然后通过数据项编码可以把对应的统计日期下,对应的数据项编码的数量或者金额获取到,然后通过replace()方法替换调即可// 具体从数据库里取值和替换的代码省略。。。嘿嘿,偷个懒 >_<||| // 最后得到大概这样的数据:avg(1000,2000)+30/2-100String express = "avg(1000,2000)+30/2-100";// 接下来就是使用QLExpress了ExpressRunner runner = new ExpressRunner();// 先定义我们需要的avg函数runner.addFunction("avg",new Operator(){@Overridepublic Object executeInner(Object[] objArray) throw Exception{Double total=0.0;Double average=0.0;for(Double obj: objArray){Double num=Double.valueOf(obj.toString());total = total + num;}average = total/objArray.length;return average;}});// 计算结果,这里是一个重载方法,没有logObject result = runner.execute(express,null, null, false,false);// 顺利拿到结果:1415Double resultNum = Double.valueOf(result.toString());
}/*** 判断是否是数字*/
public static boolean isNumeric(String str){Pattern pattern = Pattern.compile("[0-9]*");Matcher isNum = pattern.matcher(str);if( !isNum.matches() ){return false;}return true;
}
QLExpress使用还是挺方便的,而且也很简单,而我上面展示的也只是它的一小部分。实战是最好的老师,有兴趣的同学可以自己去敲上一段代码,另附上官网地址:QLExpress官网。
使用QLExpress动态制定计算公式相关推荐
- QLExpress-阿里规则引擎
QLExpress:GitHub - alibaba/QLExpress: QLExpress is a powerful, lightweight, dynamic language for the ...
- 动态展开所有_库存与市场需求之间如何“动态”共舞?库存计划动态模型构建分享...
库存(Stock)是用来提高交货速度.缓冲需求到单高峰的常用手段,通过按库存生产(MTS)的方法,用储备库存来满足客户需求.并按一定规则补货,无需等待生产周期,可极快地交付.相比按订单生产(MTO)的 ...
- 【渝粤题库】陕西师范大学164111 Java及JSP动态网页编程与应用 作业 (高起专)
<JAVA与JSP动态网页编程与应用>作业 一.单选题 1.以下哪项都是关键字( ) A.package privati protect throw B. false final fina ...
- jdk和cglib动态代理介绍
动态代理介绍: 作用:功能增强:代理可以提供额外的功能控制访问:代理可以控制不让你访问目标 分类:静态代理:简单容易理解例如模拟用户购买U盘的行为用户是客户端,商家是代理,厂家是目标类客户端--代理- ...
- 数字IC笔试题---千题解,量大管饱,图文并茂
前言 出笔试题汇总,是为了总结秋招可能遇到的问题,做题不是目的,在做题的过程中发现自己的漏洞,巩固基础才是目的. 所有题目结果和解释由笔者给出,答案主观性较强,若有错误欢迎评论区指出,资料整理来自于& ...
- 携程如何从海量数据中构建精准用户画像?
摘要: 用户画像作为"大数据"的核心组成部分,在众多互联网公司中一直有其独特的地位. 作为国内旅游OTA的领头羊,携程也有着完善的用户画像平台体系.目前用户画像广泛用于个性化推荐, ...
- tensorflow实现基于LSTM的文本分类方法
http://blog.csdn.net/u010223750/article/details/53334313?locationNum=7&fps=1 引言 学习一段时间的tensor fl ...
- 设计模式学习笔记(六:责任链模式)
1.1概述 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系.将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止.这就是责任链模式. 责任链模式是使用多个对象 ...
- 系统分析与设计 复习
文章目录 系统分析与设计 复习 第 1 章 系统分析与设计概述 系统特性 DevOps 第 2 章 系统规划 **系统规划步骤** 规划模型 诺兰模型 **CMM 模型** 系统规划方法 战略集合转换 ...
最新文章
- 第十五届全国大学生智能车全国总决赛获奖信息-华北赛区
- 安卓高级6 拍照或者从相册获取图片 并检测旋转角度或者更新画册扫描
- linux下mkdir头文件_Linux部分常用命令学习记录
- 28、OSPF配置实验之负载均衡
- JavaScript实现数乘以二multiplyByTwo算法(附完整源码)
- 架构之:软件架构漫谈
- MVC 无法将带 [] 的索引应用于“System.Dynamic.DynamicObject”类型的表达式
- mysql5.7卸载语句_MySQL5.7完全卸载
- 常见的SQL错误和解决方法
- 中国省份城市列表(汉字+拼音)
- VS2010 旗舰版和专业版 下载
- JavaScript到底应该怎么用?
- 如何以CMMI或ISO为指导实施过程改进(黑纸系列一)
- VMware 安装WIN10 WIN7
- 智慧园区一体化信息管理平台设计方案
- 计算机应用基础2004版,计算机应用基础2004年上半年全国试题
- java 小数乘法_java复习题69151-_人人文库网
- 从指数构建原理看待A股的三千点魔咒
- Docker 高级篇
- 数据流图 visio
热门文章
- CAD初学者如何快速画圆?
- repo 错误contains uncommitted changes解决方法
- 《你要相信 没有到不了的明天》支撑我走过无数艰难岁月
- 火猴浏览器3.0的语义解析突破
- yolov4直接调用zed相机实现三维测距(免标定)
- 记数排序 桶排序 基数排序
- Ubuntu编译Seetaface
- 二世古新世界·羽·度假村将于2023年盛大揭幕
- Banana Pi BPI-M5 与 Raspberry Pi 4性能测试比较
- python串口通信的接收与发送_31.用python中的serial向串口发送和接收数据(案例一)...