项目背景

订单完成后,会由交易系统推送实时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. HDU5696:区间的价值——题解

    http://acm.hdu.edu.cn/showproblem.php?pid=5696 题面是中文的我就不粘贴过来了…… ———————————————————————— 这题垃圾题!!神tm卡 ...

  2. 高效率JavaScript代码的编写技巧

    使用DocumentFragment优化多次append 添加多个dom元素时,先将元素append到DocumentFragment中,最后统一将DocumentFragment添加到页面.该做法可 ...

  3. MyEclipse下项目的包层次结构调整

    新电脑安装完MyEclipse,导入项目后发现MyEclipse下项目的包层次结构变成了Flat,平面模式,这种模式感觉特别不好, 不能清晰地显示出项目的包层次结构.这样,显示出的包的结构不够明显,我 ...

  4. k好数 数位dp

    问题描述 如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数.求L位K进制数中K好数的数目.例如K = 4,L = 2的时候,所有K好数为11.13.20.22 ...

  5. HDU1542 扫描线+离散化

    Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  6. JAVA List集合转Page(分页对象)

    /** * @version 1.0 * @author: fwjia */ import java.util.List; public class PageModel<T> { /*** ...

  7. Moq/moq4

    moq The most popular and friendly mocking framework for .NET var mock = new Mock<ILoveThisFramewo ...

  8. springboot以jar包方式启动、关闭、重启脚本

    springboot以jar包方式启动.关闭.重启脚本 启动 编写启动脚本startup.sh #!/bin/bash echo Starting application nohup java -ja ...

  9. 前端多层回调问题解决方案之$.Deferred

    javascript引擎是单线程的,但是通过异步回调可以实现IO操作并行执行能力,当业务逻辑复杂的时候我们就进入回调地狱. 本文讲得ajax是在jquery1.5以前的版本,目的旨在让我们理解延迟对象 ...

  10. 省队集训 Day3 吴清华

    [题目大意] 给网格图,共有$n * n$个关键节点,横向.纵向距离均为$d$,那么网格总长度和宽度均为$(n+1) * d + 1$,最外围一圈除了四角是终止节点.要求每个关键节点都要通过线连向终止 ...