P59 项目场景面试:优惠券规则引擎与组合优惠设计
面试题:优惠券系统怎么设计?多种优惠(满减、折扣、无门槛)同时可用时,怎么算最优组合?
1. 优惠券核心模型
text
券模板(满减/折扣/无门槛/免邮)→ 发券(用户券实例,含有效期/使用状态)
→ 下单时选券 → 计算优惠 → 核销字段要点:
- 券模板:类型、面额、门槛、可用范围(商品/类目/店铺)、有效期、库存、限领次数;
- 用户券:状态(未用/已用/过期/冻结)、来源、关联订单号。
2. 规则引擎设计(关键)
① 规则抽象
把优惠规则抽象成可配置的规则对象,而不是散落的 if-else:
text
规则 = 条件(满 X 元 / 指定商品 / 新用户)+ 动作(减 Y / 打 Z 折)- 规则存储:DB 表 / 配置中心 / DSL(如 Groovy 脚本);
- 引擎统一执行:加载规则 → 匹配 → 计算 → 校验上限。
② 组合优惠的计算
几种模型:
- 互斥:同一订单只能用一张券(简单);
- 叠加:平台券 + 店铺券 + 商品券各一张叠加;
- 优先级/互斥组:满减与折扣不同时用(选更优的);
- 最优组合:多张券选实付最低的组合。
生产做法:把可用券枚举组合(组合数量有限时)或贪心/动态规划选优,再做收益校验。
③ 计算与校验
text
商品金额 → 命中优惠条件(按类目/商品维度拆分)→ 应用优惠
→ 校验:优惠后金额 ≥ 0、不超券面额、满足最低消费
→ 锁定优惠(下单时预占,支付成功后核销,超时释放)3. 防薅羊毛(关联 P08)
- 领券频次限制、风控、黑名单;
- 同一订单只能用一张券时校验"是否已用"(幂等);
- 超卖券:发券量 = 券模板库存,Redis 原子扣减。
4. 加分点
- 提到优惠计算与金额拆分的精度:用分(整数)不用浮点;
- 订单快照:下单时把优惠结果快照到订单,后续改规则不影响已下单;
- 规则引擎的代价:可配置带来复杂度和测试成本,简单场景别过度设计;
- 追问"规则变更怎么上线":配置中心灰度发布 + 计算单元测试。
一句话总结
优惠券系统 = 券模板/用户券模型 + 规则引擎(条件+动作抽象)+ 组合优惠选优(互斥/叠加/最优计算)+ 锁定核销状态机;关键在规则可配置、计算精确、防超发防薅羊毛。