美团三面:我在美团超市凑了 300 块满减,后台为什么要拆成 3 个单?答错这道题,我的 Offer 没了。

0 阅读6分钟

写在开头

有个粉丝跟我吐槽,说美团三面挂得莫名其妙。 面试官问:“用户在美团超市买了一堆东西:一箱水、两包薯片、一只冻鸡。为了凑满减,他是一起付的钱。请问后台系统会怎么处理?” 他自信回答:“生成一个订单啊,然后发货。” 面试官笑了笑:“那如果水在 A 仓,薯片在 B 仓,冻鸡在冷链仓,你怎么发?如果这箱水太重,骑手一次拿不下,怎么拆?”

其实,这道题考的不是“发货”,而是电商核心架构中的**“交易履约分离”“资金逆向分摊”。 今天带你拆解这个让无数后端开发和财务“撕逼”的千万级分单架构**。

一、 为什么必须拆?(别只盯着仓库看)

很多初级开发觉得拆单就是“按仓库拆”,这太肤浅了。在美团/京东/拼多多这种体量,拆单是为了解决**“履约”“算账”**的冲突。

图片

1. 物理维度的拆分(为了能送到)

  • 温层拆分: 冻鸡(-18°C)和薯片(常温)不能放一个箱子,否则薯片受潮,冻鸡化了。

  • 重量/体积拆分: 那箱水太重了,根据骑手配送模型,必须单独生成一个“重物单”,甚至要加收运费。

  • 商家拆分(POP): 比如你在美团优选买了东西,虽然一起付钱,但后台要分别结算给不同的供应商。

图片

2. 财务维度的拆分(为了能算账)

  • 税务与发票: 这一点 90% 的人答不上来!书是 9% 税点,百货是 13% 税点。不拆单,财务报税时会提刀来见你。

  • 毛利核算: 满减券必须“打散”分摊到每个商品上,否则不知道哪个单品在亏钱卖。

图片

二、 核心难点:那个“消失的 1 分钱”(分摊算法)

这是整道题的**“死穴”**。也是面试官最爱挖的坑。 场景:用户凑单买了 A(30元)、B(30元)、C(40元),总价 100 元,用了 10 元优惠券。实付 90 元。

面试官追问: “这 10 元优惠怎么分?如果 3 个商品价格除不尽怎么办?”

1. 为什么要锱铢必较?

因为资损(Financial Loss)。 铁律:Sum(子单实付金额) 必须严格等于 父单实付金额。 如果不相等,哪怕只差 1 分钱,财务系统对账时就会报错,导致无法结算,或者退款时多退/少退。

图片

2. 大厂标准算法:加权分摊 + 尾差兜底

别用除法!要用减法!

  • 前 N-1 个商品: 按比例计算,向下取整(或保留 2 位小数)。

  • 公式:子单优惠 = 总优惠 * (子单原价 / 总原价)

  • 最后一个商品(The Last One):绝对不能算比例! 直接用减法。

  • 公式:最后子单优惠 = 总优惠 - Sum(前 N-1 个子单优惠)

手撕案例:

  • A(30元):10 * 0.3 = 3 元

  • B(30元):10 * 0.3 = 3 元

  • C(40元):10 - 3 - 3 = 4 元 (哪怕前面算出来是 3.3333,这里也是减法兜底,把误差全吃进最后一个商品里)

💡互动话题: 你们公司的金额计算,到底是用 BigDecimal 还是把钱转成“分”用 Long 类型存?评论区聊聊,看看哪派人多?(我赌 Long 派赢)

三、 架构演进:交易与履约的“离婚”

很多系统挂就挂在**“父子不分”**。在订单表里硬塞 parent_order_id,后期字段多到数据库根本跑不动。

1. 更好的设计:交易单 vs 履约单

  • 交易层(Trade Order):

  • 只管收钱。 面向用户,状态只有 待支付已支付退款中

  • ID:T20260214xxxx

  • 履约层(Fulfillment Order):

  • 只管干活。 面向仓库和物流,状态细化到 拣货中打包中配送中

  • ID:S20260214xxxx-01

2. 异步拆单的“大坑”

用户付完钱,我们发个 MQ 去拆单。但如果 MQ 挂了怎么办? 一定要有兜底!

  • 方案: 本地消息表 + 定时轮询(At Least Once)。

  • 逻辑: 支付成功落库时,记一笔 task_status = 0。拆单成功改 1。后台有个 Job 专门捞那些 10分钟还没拆 的单子进行重试。

图片

四、 进阶博弈:合单与退款的“深水区”

1. 合单的风险(30秒等待窗口)

为了省运费,我们会在后台设置一个“30秒缓冲区”,把同地址、同商家的单子合并。 杠精面试官问: “如果 Redis 挂了,或者系统卡了,这单子是不是永远发不出去了?” 防御性回答: “我们设计了**‘双重触发’**机制:

  1. 数量触发: 攒够 5 单直接发。

  2. 时间触发(兜底): 利用 RocketMQ 的延时消息时间轮。不管合没合成功,30秒一到,必须强制下发履约,保时效优于保成本。”

2. 退款的“门槛陷阱”

拆单容易,退款想死。 如果你买了 A+B 凑够 100 减 10 块。现在你要退 B。

  • 初级做法: 按比例退 B 的钱。

  • 高级坑点: 退了 B 之后,剩下的 A 不满 100 元了!这时候是把优惠追回?还是睁一只眼闭一只眼?

  • 话术: “这取决于业务规则。通常为了体验,我们不追回优惠,按比例拆分实付金额退款即可。但如果是 B 端采购业务,必须重算价格。”

    图片

五、 面试标准答案模板(建议背诵)

面试官:请设计一个电商分单系统。

1. 定基调(结构解耦): “我会将系统拆分为交易层履约层。交易层只负责聚合支付,保证秒级响应;履约层通过 MQ 异步进行复杂的拆单逻辑,实现存储与计算分离。”

2. 讲算法(资金安全): “核心是优惠分摊。为了防止资损,我采用**‘加权分摊 + 尾差归集’算法。前 N-1 个商品按比例算,最后一个商品用减法兜底**,确保父子单金额严格一致,解决‘1分钱’对账问题。”

3. 讲细节(防坑): “在合单逻辑中,我会设计超时强制下发机制,防止订单卡死;在退款逻辑中,严格基于子单实付金额进行逆向操作。同时,利用本地消息表保证分布式环境下的最终一致性。”

写在最后

技术面试不仅考你会不会写代码,更考你对**“钱”“极端情况”**的敬畏感。 能算平账的系统,才是好系统。

关于那个“1分钱”的问题,你们公司的财务找过你麻烦吗?评论区说出你的故事!

觉得有用的兄弟,点个赞,收藏起来,万一下次面试就用上了呢!

**关注公众号【Fox爱分享】,**带你看不一样的技术世界。

> 文章首发地址