JAVA订单优惠券、促销、费用等按商品行金额占比分摊算法

该文章介绍了一种将优惠券金额按照商品行金额占比进行分摊的算法,提供了具体的计算公式和Java代码实现。代码中使用了BigDecimal处理精确计算,避免浮点数误差。通过遍历商品行,根据商品金额占比和剩余需分摊金额动态计算每行的分摊额,确保总分摊额等于优惠券金额。
摘要由CSDN通过智能技术生成

例:订单总金额100,使用优惠券5元,需要把5元优惠券按照商品行金额占比分摊到商品行上去。

订单号:1000001,商品总金额:100元,优惠券:5元,实际支付金额:95元
商品名称单价优惠券分摊算法优惠券分摊金额
苹果60元5*(60/100)3元
香蕉30元(5-3)*(30/(100-60))1.5元
橘子10元(5-3-1.5)*(10/(100-60-30))0.5元

计算公式为:(需要分摊金额-已分摊金额) * (当前商品行金额 / (商品行总金额-已使用商品行金额))=当前行分摊金额

代码实例如下:

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class AmountShareService {

    /**
     * 处理商品行需要分摊的优惠券、促销、营销费用等,需要按照每一行商品金额占比来分摊
     * @param rowAmountList 分行商品金额集合
     * @param shareAmount 需要分摊的金额
     * @return List<BigDecimal> 返回每一行商品上的分摊占比金额
     */
    public static List<BigDecimal> handleAmountShare(List<BigDecimal> rowAmountList, BigDecimal shareAmount) {
        List<BigDecimal> amountShareList = new ArrayList<>();
        if (CollectionUtils.isEmpty(rowAmountList)) {
            log.error("参与分摊的商品集合不能为空,rowAmountList:{}", JSON.toJSONString(rowAmountList));
            return amountShareList;
        }
        //商品集合金额合计
        BigDecimal sum = rowAmountList.stream().reduce(BigDecimal.ZERO, BigDecimal::add);

        for (BigDecimal nowMoney : rowAmountList) {
            BigDecimal lastMoney = BigDecimal.ZERO;
            if (shareAmount.compareTo(BigDecimal.ZERO) == 0) {
                log.error("参与分摊金额不能为0!shareAmount: {}", JSON.toJSONString(shareAmount));
                break;
            }
            //分摊金额不能大于等于合计金额
            if (shareAmount.compareTo(sum) > -1) {
                log.error("参与分摊金额:{},不能大于商品总金额:{}", shareAmount, sum);
                break;
            }
            //当前商品行金额,在未使用商品行总金额中的占比,四舍五入保留两位小数
            BigDecimal oneMoneyScope = nowMoney.divide(sum, 2, BigDecimal.ROUND_UP);
            //占比 * 还未分摊金额,四舍五入保留两位小数
            lastMoney = oneMoneyScope.multiply(shareAmount).setScale(2, BigDecimal.ROUND_UP);
            //计算出来的当前行分摊金额不能大于当前行商品金额
            if (lastMoney.compareTo(nowMoney) == 1) {
                log.error("当前商品行分摊金额:{},不能大于当前行商品金额:{}", lastMoney, nowMoney);
                break;
            }
            amountShareList.add(lastMoney);
            //剩余分摊金额=剩余分摊金额-当前商品行已分摊金额
            shareAmount = shareAmount.subtract(lastMoney);
            //未参与分摊商品行总金额=未参与分摊商品行总金额-当前商品行金额
            sum = sum.subtract(nowMoney);
        }
        return amountShareList;
    }

    public static void main(String[] args) {
        List<BigDecimal> list = new ArrayList<>();
        list.add(new BigDecimal("60"));
        list.add(new BigDecimal("30"));
        list.add(new BigDecimal("10"));
        List<BigDecimal> result = handleAmountShare(list, new BigDecimal("5"));
        System.out.println(JSON.toJSONString(list));
        System.out.println(JSON.toJSONString(result));
    }
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值