开发成本利润率的算法(分享一个实际开发过程中)
我们在日常开发中,经常会涉及到一些数学计算,尤其是在金融交易系统中,对手续费,积分的计算更是家常便饭。今天我们以计算订单手续费为例简单介绍一下,日常开发中都是如何进行费用计算的。
写在前面的话本篇并不是讨论在一个复杂的系统中,如何合理地设计一个高效而复杂的计费系统。只是为了日后大家在日常开发遇到同类问题时,可以多一种选择而已。
假设现在要对支付完成的订单进行手续费计算,系统支持按单笔收取,也支持按百分比收取,并且对单笔最低收取的费用有限制,对单笔上限收取的费用也有限制。
对于上面描述的问题,其实并不是十分复杂,即使是我们为了赶进度,不使用任何其他的第三方类库,硬编码实现也是可以的,无非就是多写一些 if else 而已。当然了,如果你是一个技术大牛,对一些规则引擎比较熟悉,比如 drools 规则引擎,也是可以很优雅地解决上面的问题。
但是,你们知道的,资深北漂假码农是一个假码农。我对那些复杂的业务引擎规则又不是很了解,但是呢又不想写一大堆if else 的判断。一腔热血献技术(装X)的我,从老前辈那里偷了点东西,今天就毫无保留地分享给大家。
下面就来分享下如何通过ONGL(不了解的,请自行搜索)表达式来进行费用的计算。
添加依赖如果使用maven项目,添加下面的依赖即可,如果是其他打包方式的项目,请自行搜索如何引入第三方类库。
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.3.2</version>
</dependency>
编译表达式方法compile,方法接收一个String 类型的参数,即为计算的表达式。
public static Object compile(String expression){
try {
return Ognl.parseExpression(expression);
} catch (OgnlException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
执行表达式方法execute,方法接收两个参数,String类型的计算表达式,Object类型的对象图根(可以简单理解为计算表达式时需要的key-value,实际调用时传入的是Map)。
public static Object execute(String expression,Object root){
logger.info("使用OGNL计算 表达式:{} 参数:{}",expression,root);
Map context = Ognl.createDefaultContext(root);
try {
return Ognl.getValue(compile(expression),context,root);
} catch (OgnlException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
现在我们来实现文中开头处所说到的需求,这个时候我们需要两个表达式才能满足我们的需求的,一个是计算费用的表达式:amount*percentage single,其中amount表示本次需要计算金额(如订单金额100元),percentage 表示计费的百分比(如2%),single表示单笔订单收取的费用(如每笔额外收取1.5元),另一个是单笔限额的表达式:fee<min?min:fee<max?fee:max,其中 fee 是根据第一个表达式计算出来的手续费,min表示单笔订单收取的最低费用(如一笔订单最低收取2元,不足2元按2元收取),max表示单笔订单最高收取的费用(如一笔订单最高收取15元,超出15元按15元收取)。
final static String EXPRESSION = "amount*percentage single";
final static String EXPRESSION2 = "fee<min?min:fee<max?fee:max";
@Test
public void test(){
Map<String,Object> params = new HashMap<>();
//订单金额 100元
params.put("amount", BigDecimal.valueOf(100L));
//单笔订单收取百2的手续费
params.put("percentage", BigDecimal.valueOf(0.02));
//每笔额外收取1.5元
params.put("single", BigDecimal.valueOf(1.5D));
//计算费用 = 3.5 元
Object o = OgnlUtil.execute(EXPRESSION, params);
//计算单笔限额
params.put("fee",o);
//每笔最低费用 2 元
params.put("min",BigDecimal.valueOf(2L));
//每笔最高费用15 元
params.put("max",BigDecimal.valueOf(15L));
//计算费用 = 3.5
Object fee = OgnlUtil.execute(EXPRESSION2, params);
logger.info("fee:{}",fee);
}
以下为计算结果,读者可以自行修改测试数据,以验证表达式的准确性。
测试用例执行结果
优化对于相同的表达式,没有必要每次都进行编译,我们可以将表达式的编译结果进行缓存起来,最终修改如下:
private static final Map<String,Object> EXPRESSIONS = new HashMap<>();
public static Object compile(String expression){
synchronized (EXPRESSIONS){
return EXPRESSIONS.computeIfAbsent(expression,e ->{
try {
return Ognl.parseExpression(expression);
} catch (OgnlException ex) {
ex.printStackTrace();
throw new RuntimeException(e);
}
});
}
}
好了,本次分享就到这里,感谢大家的阅读。
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com