Skip to content

P59 项目场景面试:优惠券规则引擎与组合优惠设计 ​

面试题:优惠券系统怎么设计?多种优惠(满减、折扣、无门槛)同时可用时,怎么算最优组合?

1. 优惠券核心模型 ​

text
券模板(满减/折扣/无门槛/免邮)→ 发券(用户券实例,含有效期/使用状态)
→ 下单时选券 → 计算优惠 → 核销

字段要点:

  • 券模板:类型、面额、门槛、可用范围(商品/类目/店铺)、有效期、库存、限领次数;
  • 用户券:状态(未用/已用/过期/冻结)、来源、关联订单号。

2. 规则引擎设计(关键) ​

① 规则抽象 ​

把优惠规则抽象成可配置的规则对象,而不是散落的 if-else:

text
规则 = 条件(满 X 元 / 指定商品 / 新用户)+ 动作(减 Y / 打 Z 折)
  • 规则存储:DB 表 / 配置中心 / DSL(如 Groovy 脚本);
  • 引擎统一执行:加载规则 → 匹配 → 计算 → 校验上限。

② 组合优惠的计算 ​

几种模型:

  1. 互斥:同一订单只能用一张券(简单);
  2. 叠加:平台券 + 店铺券 + 商品券各一张叠加;
  3. 优先级/互斥组:满减与折扣不同时用(选更优的);
  4. 最优组合:多张券选实付最低的组合。

生产做法:把可用券枚举组合(组合数量有限时)或贪心/动态规划选优,再做收益校验。

③ 计算与校验 ​

text
商品金额 → 命中优惠条件(按类目/商品维度拆分)→ 应用优惠
→ 校验:优惠后金额 ≥ 0、不超券面额、满足最低消费
→ 锁定优惠(下单时预占,支付成功后核销,超时释放)

3. 防薅羊毛(关联 P08) ​

  • 领券频次限制、风控、黑名单;
  • 同一订单只能用一张券时校验"是否已用"(幂等);
  • 超卖券:发券量 = 券模板库存,Redis 原子扣减。

4. 加分点 ​

  • 提到优惠计算与金额拆分的精度:用分(整数)不用浮点;
  • 订单快照:下单时把优惠结果快照到订单,后续改规则不影响已下单;
  • 规则引擎的代价:可配置带来复杂度和测试成本,简单场景别过度设计;
  • 追问"规则变更怎么上线":配置中心灰度发布 + 计算单元测试。

一句话总结 ​

优惠券系统 = 券模板/用户券模型 + 规则引擎(条件+动作抽象)+ 组合优惠选优(互斥/叠加/最优计算)+ 锁定核销状态机;关键在规则可配置、计算精确、防超发防薅羊毛。

基于 VitePress 重建