交易订单优惠分配算法实践

玉门关、鬼门关,出关一步血流干。

1 前言

做过支付和订单的同学们都会遇到订单拆单的问题,特别是在处理订单优惠的时候,需要将具体的优惠分配到每一个具体的订单明细上(即将优惠拆分到每种商品上,一般都是按照每种商品金额的比例进行拆分),在本文中将结合作者的实践经验,分享自己在拆单上的一些经验和心得。

2 需求分析

在开始之前,先介绍一下需求的具体要求以及功能的设计。用户在下单时,可以使用积分抵扣一部分的支付金额,支付的积分还可以通过算法进行膨胀,膨胀的部分由平台进行抵扣,最后还有支付立减,可以减少一定的用户积分支付金额。可能说到这里还是不太清楚,这里举例说明一下:

bash 复制代码
# 订单需要支付的总金额为100元,其中20元可以使用用户的积分进行抵扣。
1 膨胀金部分,平台通过算法计算出膨胀的部分,假如膨胀金额为5元 ,则用户需要支付的金额为15元的积分, 
另外5元由平台抵扣,这个部分在用户支付时是知道的,此时用户的订单分为(80元现金+15元的用户积分+ 5 元的膨胀金,由平台抵扣)。
2 支付立减,经过平台的膨胀后,用户需要支付的金额为(80元现金+15元的用户积分),
在实际支付时平台可以通过算法对15元的用户积分进行随机立减,假设立减金额为5元。
则最后的支付金额为(80元现金+10元的用户积分+ 5元的膨胀金,由平台抵扣 + 5元的随机立减金额)

在介绍了支付的优惠后,需要对现金、实付积分、膨胀金、随机立减等进行拆分到子单上,这是一般的优惠金额和支付金额拆分逻辑,需要注意的是一般情况都是按照子单的金额比例进行拆分,最后一单还需要使用减法,防止比例拆分除不尽的情况,这是非常重要的认知

在拆单的过程中,按照比例计算一律向上取整,上图中红色背景的都是通过子单金额与订单总金额比例进行进行计算的,绿色背景的都是通过减法进行计算的,唯有通过此种方式,才能避免拆分出现尾差的问题。

在常用的订单支付中,还会有优惠券的拆分和计算,其计算逻辑都是类似的。但作者在工作中接到的业务场景有一个复杂的情况,可能有一个子单或者几个子单的膨胀金金额是指定的,不能通过比例进行计算,这个需要怎么实现呢,还是需要按照减法的方式来进行计算,剩余需要按照比例计算的,需要按照剩余的膨胀金额和剩余子单金额的比例进行计算。

3 功能实现

在前文已经分析了拆单的逻辑,在本节中需要进行代码的计算,首先需要创建一个订单子单计算对象 OrderCalNode

订单拆单逻辑计算时,需要先创建拆单对象并初始化订单金额以及优惠金额,然后添加子单对象,最后进行订单的计算,可以通过订单ID, 来获取每个订单明细的拆分结果。

ini 复制代码
// 初始化拆单对象,添加订单金额和优惠金额
OrderSplitCalUtils splitCal = OrderSplitCalUtils.init();
// 添加计算数据,添加每个子单的金额
splitCal.addRow("456", BigDecimal.valueOf(1));
// 开始订单拆分计算
splitCal.calculate();
// 获取订单拆单结果
OrderCalNode orderCalNode = splitCal.queryByKeyId("123");

订单金额初始化方式,主要是添加订单的总金额,包括积分、优惠和现金等每一项总额,主要是为了约束子单的每一项内容。

添加每一项子单明细的数据,并且通过子单id 获取订单明细信息。

订单计算的核心逻辑,需要计算每个子单每一项的金额,最后一项的计算要使用减法。

这里需要注意一下 handleIfNull 这个方法,如果需要计算的项为空则进行比例计算,否则就返回该项的数值。

4 拆单结果

通过以上的分析和编码,可以通过 main 方法进行拆单结果的验证,如下图所示:

5 总结

在本文中,主要介绍了订单拆单的需求分析和功能实现,通过核心的逻辑分析和实现,实现了订单优惠的逐项计算,再也不需要使用复杂的计算逻辑,这里的代码实现尽可能的脱离了业务抽离出来作为独立的拆单逻辑。 感兴趣的同学可以参考指正,本文中所涉及的代码已经上传至 github, 欢迎大家点赞关注。项目 github 地址 springboot-auth

相关推荐
it噩梦43 分钟前
Springboot 实现Server-Sent Events
java·spring boot·后端
鸽鸽程序猿43 分钟前
【JavaEE】Spring Boot 项目创建
java·spring boot·java-ee
运维&陈同学1 小时前
【kafka04】消息队列与微服务之Kafka 图形工具
后端·微服务·zookeeper·云原生·架构·kafka·消息队列·云计算
淘淘 小窝4 小时前
springboot配置多数据源mysql+TDengine保姆级教程
spring boot·mysql·tdengine
柴米油盐那点事儿4 小时前
springboot引入kafka
spring boot·kafka
ᝰꫝꪉꪯꫀ3617 小时前
JavaWeb——Maven高级
java·后端·maven·springboot
独泪了无痕8 小时前
探索 IntelliJ IDEA 中 Spring Boot 运行配置
spring boot·intellij idea
小张帅三代8 小时前
【spark-spring boot】学习笔记
spring boot·学习·spark
camellias_9 小时前
SpringBoot(三十九)SpringBoot集成RabbitMQ实现流量削峰添谷
spring boot·rabbitmq·java-rabbitmq
2301_793086879 小时前
springboot+redis+lua脚本实现滑动窗口限流
spring boot·redis·lua