题目重点:围绕购物车商品、优惠资格、互斥组和结算快照设计可扩展折扣规则,避免总价计算受顺序或浮点误差影响。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版 CartLine 保存 sku、数量、单价和币种;金额用整数最小货币单位或 Decimal,不混合币种。
- 折扣例子包括固定金额、百分比和商品范围优惠,但真实类型和相互叠加规则题目未明确,必须在面试中澄清。
- 结算报价有有效期与规则版本;最终支付前重新确认库存、价格与优惠资格。
解题路线
- 定义 Cart、CartLine、PriceSnapshot、DiscountRule、EligibilityResult、DiscountApplication 与 CheckoutQuote;规则只返回可解释的候选折扣,不直接修改 Cart。
- 先把商品价格、数量和币种固定成快照,检查每条规则的时间窗、用户资格、SKU/品类、最低消费和使用次数;失败原因也记录,方便解释。
- 叠加策略独立成 Policy:互斥组最多选一个、可叠加组按明确优先级应用。固定金额和百分比在不同顺序会给不同总价,故不在代码遍历顺序里隐式决定。
- 对行级优惠按规则分摊到商品,舍入余数用稳定 SKU/行号分配,保证各行折扣之和等于总优惠且单行不为负。税费、运费、退款是否按折后金额重算须由业务给定。
- 创建 CheckoutQuote 时保存输入价格版本、所用规则版本、行级金额和合计;支付请求用幂等键并重新校验报价,避免重复优惠或过期价格。
- 测试覆盖资格边界、互斥/叠加、同购物车行顺序变化、舍入、跨币种拒绝、报价到期与并发领取最后一张券。
手动走一遍
两件商品各 100 元,先打 10% 再减 30 元得到 150 元;若先减 30 再打 10% 得 153 元。规则遍历顺序不能任意选择其一。若满减 1 元分摊到三行,要把分的 33、33、34 分加起来恰为 100 分,并保持退款可追溯。
规模与取舍
L 条商品行、R 条规则:朴素资格判断 O(LR),可按 SKU/品类预索引减少无关规则评估;最终组合选择若规则可任意叠加并含互斥/封顶,可能成为组合优化,需业务定义优先级或搜索上限。金额计算空间约 O(L+R+应用记录)。
易错点与追问
容易失分的边界
- 金额用二进制浮点反复乘折扣,出现分币误差。
- 规则直接 mutate 购物车,难以解释和回放。
- 不定义折扣叠加顺序,却在实现中默认循环次序。
- 折扣总额大于商品金额或跨币种叠加。
- 结算页报价与支付时规则版本变化却仍使用旧价格。
继续深挖
- 互斥规则要取最优惠组合时如何控制搜索复杂度?
- 部分退货时如何用原分摊金额计算退款?
- 最后一张限量券并发领取如何防超发?
- 税前折扣和税后折扣怎样在规则模型中区分?
