项目背景

订单完成后,会由交易系统推送实时MQ消息给订单清算系统,告诉清算系统此订单交易完成,可以进行给商家结算等后续操作。

财务要求在交易推送订单到清算系统时和订单清算系统接收到订单消息后,需要按照财务给定的校验公式,验证交易推送的数据是否正确。比如下面的财务公式:

  • 商品原价 = 商品活动价 + 活动价补贴
  • 在线支付=微信+支付宝+QQ钱包+会员卡支付+翼支付
  • 用户实付=在线支付+现金
  • 订单总额=在线支付金额+会员卡优惠+代金券优惠+公司活动补贴+商家活动价补贴+现金支付

财务同学明确告知,随着支付网关后续接入更多支付渠道整体公式都要随着变化。另外营销手段的丰富化(拼团返现等),相应结算相关的金额都要纳入财务公式计算和校验中。故公式整体是不断变化的。

方案

金额单元参数化

为了应对后续财务相关字段的随时扩充,项目组组长确定使用大Json,单元参数形式表示非订单基础信息。形如:

[{"K":10112,"V":"2100"},{"K":10113,"V":"10"},{"K":10115,"V":"2300"},{"K":10117,"V":"0"}]

具体Key字段(比如:10112、10113、10115)含义由枚举形式维护。并人为规定10000~20000段为金额相关字段。

字典表或配置中心存储校验公式

校验公式前期是存储在MySQL的一张字典表里。后期有计划迁移到架构部维护的统一配置中心里。部分校验公式形如下:

1. p10125-(p10101+p10106+p10108+p10131+p10113)
2. p10101-(p10102+p10103+p10104+p10105+p10130)
3. p10108-(p10109+p10110+p10111+p10133)
4. p10125-(p10131+p10124)
5. p10131-(p10112+p10132)
6. p10107-(p10132+p10133)
7. p10117-(p10134+p10135+p10136+p10140+p10137+p10138+p10139)

其实,通过看上面一大堆公式,也可以看出 使用这种编码化的单元参数,可读性太差了!!

公式校验

在订单清算系统接收到到订单MQ消息时,会遍历消息体内全部金额相关字段写入到FelContext内,为后面计算做准备。

校验前准备:

private FelContext getFelContext(OrderSalary orderSalary, FelEngine fel) {
FelContext ctx = fel.getContext();
ParamEnum[] enums = ParamEnum.values();
for (ParamEnum paramEnum : enums) {
if (paramEnum.getCode() < 20000) {
// 获取对应的Value值
String value = ParamapUtil.getSingleton().getEntityParamValueByParamID(orderSalary, paramEnum.getCode()) == null ? "0" : ParamapUtil.getSingleton().getEntityParamValueByParamID(orderSalary, paramEnum.getCode());
// 以 p11002 , 2000 的形式写入FelContext
ctx.set("p" + paramEnum.getCode(), Long.parseLong(value));
}
}
return ctx;
}

公式校验: 比如 ( 用户实付 - (在线支付+现金) = 0 )

// checkValue为全部检验公式,是从字典表或者配置中心获取的
for (String check : checkValue) {
Expression exp = fel.compile(check, ctx);
Object result = exp.eval(ctx);
Long checkNumber = NumberUtil.toLong(result);
if (checkNumber != 0) {
logger.error("orderId={} 规则校验错误:{}", new Object[]{orderSalary.getOrderId(), check});
return Boolean.FALSE;
}
}

其他场景

比如计算本单实发,也就是本单实际给商家结算金额,就可以表达式引擎。我们公司旧的计算规则:

本单实发=结算金额-退款金额+商家补贴撤回+现金退款-现金支付-商家信息服务费-商家承税;

最后

还是想吐槽这种编码形式的单元参数设计,可读性太差了。人为去维护code和具体含义的枚举,一旦维护乱了,后面的人就再也不知道这个编码的含义了。

Fel表达式实践的更多相关文章

  1. Fel表达式使用过程中需要注意的问题

    精度问题: 我们知道java中直接使用float和double参与的计算都可能会产生精度问题,比如0.1+0.3.1.0-0.9 等.所以一般财务系统,都会使用BigDecimal进行加减乘除. 在调 ...

  2. Fel表达式计算引擎学习

    转载原文地址:Fel是轻量级的高效的表达式计算引擎 Fel的问题 Fel的问题 Fel是轻量级的高效的表达式计算引擎 Fel在源自于企业项目,设计目标是为了满足不断变化的功能需求和性能需求. Fel是 ...

  3. FEL表达式的用法

    Fel是开放的,引擎执行中的多个模块都可以扩展或替换.Fel的执行主要是通过函数实现,运算符(+.-等都是Fel函数),所有这些函数都是可以替换的,扩展函数也非常简单. Fel有双引擎,同时支持解释执 ...

  4. Java8函数式接口以及lambda表达式实践

    罗列一下遇到可以转换成lamada表达式的场景,仅供参考,如有更好的方式,欢迎在评论区留言. 1.计算订单总金额 订单总金额一般是在后台循环叠加每个购买商品的金额已获取到,通常的方式如下 BigDec ...

  5. Shell编程学习(六)

    Shell 脚本条件测试与比较 条件测试方法综述 在Bash的各种条件结构和控制结构中都要进行各种测试,然后根据测试结果执行不同的操作,有时也会与if等条件语句相结合,来完成测试判断,以减少程序运行的 ...

  6. javascript之小积累-匿名函数表达式的最佳实践

    在写js的时候,还是经常会用的匿名函数表达式,比如 setTimeout(function() { console.log(110); }, 1000); 上面那个function()就是匿名函数表达 ...

  7. Atitit 表达式原理 语法分析 原理与实践 解析java的dsl  递归下降是现阶段主流的语法分析方法

    Atitit 表达式原理 语法分析 原理与实践 解析java的dsl  递归下降是现阶段主流的语法分析方法 于是我们可以把上面的语法改写成如下形式:1 合并前缀1 语法分析有自上而下和自下而上两种分析 ...

  8. 表达式树练习实践:C# 五类运算符的表达式树表达

    目录 表达式树练习实践:C# 运算符 一,算术运算符 + 与 Add() - 与 Subtract() 乘除.取模 自增自减 二,关系运算符 ==.!=.>.<.>=.<= 三 ...

  9. 表达式树练习实践:C# 循环与循环控制

    目录 表达式树练习实践:C# 循环 LabelTarget for / while 循环 无限循环 最简单的循环 多次循环 break 和 continue 一起 表达式树练习实践:C# 循环 C# ...

随机推荐

  1. BZOJ1179 [Apio2009]Atm 【tarjan缩点】

    1179: [Apio2009]Atm Time Limit: 15 Sec  Memory Limit: 162 MB Submit: 4048  Solved: 1762 [Submit][Sta ...

  2. Java实验报告(实验四)

    北京电子科技学院(BESTI) 实     验    报     告 课程:Java    班级:1352班      姓名:王国伊    学号:20135207 成绩:             指导 ...

  3. 【初级算法】2.买卖股票的最佳时机 II

    题目: 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票). 注意:你不能同时参与多笔交易(你必 ...

  4. 一维的Haar小波变换

    小波变换的基本思想是用一组小波函数或者基函数表示一个函数或者信号,例如图像信号.为了理解什么是小波变换,下面用一个具体的例子来说明小波变换的过程. 1. 求有限信号的均值和差值 [例] 假设有一幅分辨 ...

  5. bzoj 3212 Pku3468 A Simple Problem with Integers 线段树基本操作

    Pku3468 A Simple Problem with Integers Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 2173  Solved:  ...

  6. Android 自定义ListView实现底部分页刷新与顶部下拉刷新,androidlistview

    在项目开发中,由于数据过大时,需要进行分页加载或下拉刷新,来缓解一次性加载的过长等待.本篇博文实例讲解通过自定义的ListView实现底部分页加载和顶部下拉刷新的效果. 其效果图: 一.ListVie ...

  7. Android中禁止SlidingPaneLayout的侧滑功能

    Android中使用android.support.v4.widget.SlidingPaneLayout实现侧滑功能的时候,可能出现滑动屏幕时与SlidingPaneLayout的侧滑发生冲突,查看 ...

  8. mysql varchar到底能存多少字符。

    utf8编码的varchar Mysql记录行数据是有限的.大小为64k,即65535个字节,而varchar要用1-2字节来存储字段长度,小于255的1字节,大于255的2字节. Mysql 5.0 ...

  9. SourceTree for mac 注册过程(v2.7.6a)

    背景 为啥要自己注册呢,往上一堆一堆的老版本许可证偏不用,就愿意定制自己的账号style. 搞了半天,还是觉得pycharm自带的git工具就挺好用了,闲的没事记录一下. 要点 百度搜索的地址可以进入 ...

  10. Sass 条件-循环语句

    学习Sass中 @if...@else @for @while @each 一.条件判断 - @if @else 示例: @mixin blockOrHidden($boolean:true){ @i ...