跨店满减动态计算核心是聚合、匹配、分摊三步线性推进:先按店铺聚合商品金额,再降序匹配首条满减规则,最后区分店铺与跨店路径分摊减免,并全程校验与BigDecimal精度控制。

跨店满减金额的动态计算,关键不在“流程控制”的复杂编排,而在于用清晰、可预测的步骤把聚合、匹配、分摊三件事串起来。轻量级系统不靠状态机或工作流引擎驱动,而是靠代码逻辑的线性推进+关键节点校验,做到快、稳、可调试。
按店铺聚合商品金额是第一步也是最稳的起点
购物车里商品来自不同店铺,必须先归集再算优惠。用 ConcurrentHashMap<String, BigDecimal> 存 shopId → 折扣后金额:
- 遍历每一项商品,提取
shopId和已扣单品优惠后的discountedAmount - 用
map.compute(shopId, (k, v) -> v == null ? amount : v.add(amount))累加,一行解决空值与并发安全 - 聚合后得到类似
{"shop-a": 268.50, "shop-b": 142.00}的结构,后续所有计算都基于此
规则匹配要降序+首命中,避免多档叠加错乱
满减规则不是越多越好,而是要按门槛从高到低排序,取第一个能触发的:
- 规则列表如
[{"threshold":500,"discount":60},{"threshold":300,"discount":30},{"threshold":100,"discount":10}] - 对每个店铺金额,用
rules.stream().filter(r -> amount.compareTo(r.threshold) >= 0).findFirst()找首个达标规则 - 找不到就返回 0 减免,不中断流程,同时记录日志(方便运营查“为什么这家店没减”)
跨店累计减免需单独走全局路径,不能直接加总店铺减免
店铺独立满减和跨店合并满减是两套逻辑,不能混用:
- 先汇总所有店铺金额:
total = shopAmounts.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add) - 查平台级规则(如“满300减30”),仅当
total.compareTo(threshold) >= 0时才启用跨店减免 - 分摊方式按业务定:简单场景用“首店承担”,利润敏感场景用“按实付金额比例”(如
shop-a承担(268.5 / 410.5) * 30) - 分摊结果写入新 Map
<String, BigDecimal> shopMinusMap,后续开票、分账、记账全以它为准
兜底校验必须嵌在每步输出之后
动态计算不怕逻辑多,怕中间值异常导致最终结果失真:
- 每个店铺金额必须 > 0,否则抛异常或归入“平台店”统一处理,不静默吞掉空值
- 最终减免额不能超过原始总金额,若
totalMinus.compareTo(totalBeforeMinus) > 0,强制截断为totalBeforeMinus - 所有金额运算全程用
BigDecimal,构造时传字符串(如new BigDecimal("300.00")),杜绝浮点误差
不复杂但容易忽略。

















