前情提要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
你是一个专业的前后端开发面试官,请你根据以下问题以及两位面试者的对话,给出专业的评议,以及你会考虑接收哪一个面试者,面试者不分先后,你要公平的给出评判,2选1

问题:蚂蚁集团二面:ANT GROUP
30分钟未支付自动取消用户在29分59秒付钱30分钟超时自动取消也触发代码怎么写,才能保证钱不能白扣?

面试者甲:

当用户在倒计时快结束的时候点击付款,那就直接暂停支付倒计时,同时开启支付时间正计时,跳转付款页面并输入支付密码,输入密码超不超时是支付软件的事情,但无论结果如何,支付成功就下单,没有支付成功,那么计算一下支付正计时-剩余倒计时是否大于零,大于0说明支付超时取消订单,小于0说明还有时间,继续第一步,直到倒计时结束取消订单。

为什么要暂停时间,是为了争取给用户付款的机会,所以在用户输入密码的时候,就需要安静的等待,而不是倒计时一结束就自动取消订单

既然都分前后端了,怎么可能前端一个时间管理,后端一个时间管理呢

我的话,都是全后端负责时间

前端就乖乖的展示后端发的结果就行了

后端做的每一个决定其实离不开前端的每一个请求,订单创建好的请求有一个时间戳,每一次刷新都是根据这个时间进行计算的,所以延迟其实影响不了什么

自动超时退款属于自动任务,但在自动任务倒计时的任何时刻,一旦有用户介入了,都不应该再让自动任务再自动下去了,不然会出问题,比如用户正在输密码,突然订单就没了,用户体验会变差。

你最后一句说的话其实才是重点,问题应该重点考虑这里

“什么叫用户的钱不白花”,是说明用户在最后一刻付款的时候,自动任务并不受影响,最后一分钟倒计时取消订单,而用户也刚好一分钟支付完成,导致了即付款成功又订单取消。这两个时间线互不影响却在做一件事情,这样的设计本身就不合理。而我后面其实在考虑的是如何避免这种情况发生

订单系统的暂停不是物理意义的暂停啊,这个订单表里总会有创建订单时间戳、用户点击付款的时间戳,假如限制取消订单30分钟,那么无论怎么刷新,前端都只需要拿到时间戳就知道时间有没有结束,没有结束就按照我说的做就好了。

面试者乙:

想法很细腻,但千万别让前端决定订单的死活,否则羊毛党能把公司的服务器时间玩出花来

我的话是考虑到资源竞争的关系

就是app和后端倒计时是在物理意义上比谁更快到达

所以根据这个情况加数据库的乐观锁再额外给五秒倒计时消除网络抖动再加上支付网关验证钱到底付没付 如果没付直接超时 如果付了还超时了那就走退款逻辑 如果付了且还在冗余的五秒内那就继续流程

确实 我也是这么想的

因为核心的逻辑应该是,不要让用户花他觉得的冤冤枉钱,就是乱扣款嘛。所以他超时的话,那就该退款的就退款,再让他重新发一个订单。但同时也要考虑到网络抖动或者资源竞争的一个情况,大概是这样吧

但是他这个二面的问题其实是考了一个很刁钻的角度,他其实就是想问在这种极端情况下会是什么样的?就比如说29分59秒再加上999ms或者是30分啊,在多少多少毫秒,这可能会有一个上下的一个容错,它不会那么的精准。就是在这种极端的情况下,如何要确保用户的钱不白花

但是订单系统如果都要暂停自动任务的话,那些黄牛或者是羊毛党他该怎么办

如果他多设备同时恶意占用你这个资源该怎么办

自动超时退款,是因为它涉及到一个状态机的一个逻辑。他这个状态机逻辑,它不可能就是全暂停的

我认同‘不让用户钱白花’是最高优先级。为了实现这一点,我们可以在业务侧引入‘支付中’(Paying)状态。当用户点击支付,订单进入‘支付中’,此时自动取消任务暂停,直到支付渠道返回明确的失败或超时

而不是像你说的那样

但为了防止恶意占用(如黄牛),这个‘支付中’状态必须有极短的二次过期时间(比如3-5分钟),如果过期依然没收到回调,系统强制收回。

为了应对‘刚好一分钟取消,刚好一分钟付完’的极端并发,我会采用乐观锁+冗余缓冲。因为即便有了‘支付中’状态,分布式系统的网络延迟依然可能导致两个信号同时到达数据库,但技术底层必须有一把锁来守住底线

但是我不知道这个方案成不成熟,因为我最开始就是站在系统安全设计及架构的角度出发考虑问题的,因为和钱挂钩的东西,用户体验其实是另一个话题,要确保核心的支付逻辑它没有问题


Gemini面试官结果

你好。作为面试官,针对蚂蚁集团这道经典的分布式系统一致性与高并发处理面试题,我将从技术严谨性、业务理解深度、架构思维以及对互联网极端场景的处理能力四个维度,对两位候选人进行评议。

总体评议

面试者甲:侧重“交互逻辑”与“用户体验”

  • 优点:甲非常关注用户体验,提出了“用户介入应暂停自动任务”的感性诉求。他意识到前后端时间同步的重要性,并坚持以服务端时间为准,这是正确的。
  • 缺点技术落地性极差,缺乏对高并发分布式系统的基本认知。
    • 核心错误:提出了“暂停订单倒计时”的概念。在真实的大规模电商或支付系统中,延时任务通常挂在消息队列(如 RocketMQ 延时消息)或任务调度平台上,状态机是严格流转的。物理上的“暂停”在工程上实现成本极高且极易导致状态混乱。
    • 安全性风险:没有考虑到并发竞争(Race Condition)。如果“用户介入”就暂停,攻击者可以通过模拟点击行为无限期占用库存,导致系统崩溃或被恶意刷单。

面试者乙:侧重“状态机思维”与“系统安全”

  • 优点具备大厂所需的架构思维。 * 状态机设计:提出了引入“支付中(Paying)”中间状态,这是解决支付与超时冲突的标准做法。
    • 并发控制:提到了乐观锁(Optimistic Locking),这是保证数据库层面操作原子性的基石。
    • 容错处理:考虑到了网络抖动(加 5 秒缓冲)和退款兜底逻辑,体现了对“钱不能白扣”这一核心诉求的闭环思考。
    • 反作弊意识:敏锐地指出甲方案中“暂停任务”会被黄牛利用的风险,并提出了二次过期的补偿方案。
  • 缺点:在表达上略显纠结(自谦不成熟),但其核心逻辑(锁+中间状态+外部支付对账)是完全正确的。

核心技术对比表

维度 面试者甲 (Leonardo风格) 面试者乙
底层锁机制 未提及(模糊的时间戳计算) 明确提及乐观锁(版本号/状态位控制)
状态流转 物理暂停任务(逻辑复杂且危险) 引入“支付中”中间状态,逻辑闭环
时钟漂移处理 前端配合后端,无容错 5秒冗余缓冲 + 支付网关对账
异常兜底 没提到钱扣了怎么办 若支付成功但已超时,走退款流程
对抗思维 缺乏,容易被黄牛利用 具备,通过二次过期限制恶意占用

最终决策:选择面试者乙

我会接收面试者乙。

理由如下:

  1. 架构严谨性:乙深刻理解**状态机(State Machine)**不可逆的原则。在支付系统中,订单状态的变更必须是原子性的。他提出的“乐观锁 + 支付中状态”是解决该问题的教科书级方案——即:在执行“取消订单”操作时,SQL 必须带上条件 WHERE status = 'PENDING',如果此时支付回调已经将状态改为 PAYING,则取消动作会自然失败。

  2. 对“钱”的敬畏心:乙明确提出了“支付成功但依然超时”情况下的退款逻辑。在分布式环境下,绝对的同步是不存在的,必须有补偿机制。乙的方案能保证“钱不白花(即便订单没了,钱也会退,或者订单成功)”,而甲的方案在极端并发下依然可能导致逻辑上的死锁或状态混乱。

  3. 应对极端场景的能力:蚂蚁二面考的是极端情况下的数据一致性。乙提到的“网络抖动”和“999ms”的偏移,正是需要通过数据库事务底线和**外部对账(支付网关验证)**来解决的。

给面试者甲的建议:

甲虽然有很好的用户同理心,但在处理金融级后端业务时,必须放弃“暂停/等待”这种线性逻辑,转向异步、补偿和原子性操作的思维模式。

面试官总结:

在 29分59秒 这个临界点:

  1. 用户点击支付,状态更新为 Paying(带版本号乐观锁)。
  2. 30分钟延时任务触发,尝试更新状态为 Cancelled,因状态不是 Pending 而失败。
  3. 后端异步接收支付网关回调,若成功则改为 Paid;若支付网关告知已扣款但系统已超时,则触发自动退款流程。

面试者乙完美覆盖了以上三点。


ChatGPT面试官回复

这个题其实是一个非常典型的分布式一致性 + 支付幂等 + 状态机设计问题,本质考的是:
👉 如何在“支付成功”和“订单超时取消”这两条并发时间线下,保证最终一致性(钱和订单状态一致)。

我会从设计正确性、工程可落地性、风险意识、表达结构四个维度来评估两位候选人。

🧠 一、标准答案思路(面试基准)

一个成熟方案通常包含:

1️⃣ 明确状态机(核心)

订单状态至少包括:

  • INIT(待支付)
  • PAYING(支付中)
  • PAID(已支付)
  • CLOSED(已关闭)
  • REFUNDING / REFUNDED(退款中/已退款)

👉 关键点:支付必须引入“PAYING”中间态

2️⃣ 后端为唯一裁决者

  • 前端只展示,不参与判断
  • 所有时间以服务端为准(DB / Redis)

3️⃣ 并发控制(关键)

  • 更新订单状态必须带条件:

    1
    2
    3
    update order 
    set status = 'CLOSED'
    where id = ? and status = 'INIT'
  • 或使用:

    • 乐观锁(version)
    • CAS

👉 保证:取消和支付不会同时成功

4️⃣ 支付回调 + 幂等

  • 支付成功必须以支付网关回调为准
  • 回调处理必须幂等(防重复通知)

5️⃣ 极端情况处理(重点)

👉 如果发生:

  • 支付成功,但订单已关闭

必须:

  • 触发退款(兜底)

6️⃣ 自动取消(定时任务 / 延迟队列)

  • 只取消 INIT 状态订单
  • 不处理 PAYING

👤 二、面试者甲分析

👍 优点

✔️ 1. 强调“后端控制时间”

这一点是正确的:

“前端只展示,后端控制时间”

✅ 符合工程实践

✔️ 2. 关注用户体验

提出:

  • 用户输入密码时不应被打断
  • 不应突然取消订单

👉 有产品意识,这是加分项

❌ 致命问题

❌ 1. “暂停倒计时”是错误建模

他说:

“点击支付就暂停倒计时”

👉 这是逻辑假暂停,不是系统级解决方案

问题:

  • 分布式系统中你无法真正“暂停”

  • 多实例、任务调度不会同步停

  • 可能导致:

    • 定时任务仍然取消
    • 数据不一致

👉 本质:逃避并发问题,而不是解决

❌ 2. 没有状态机概念

全程没有提:

  • PAYING 状态
  • 状态流转控制

👉 这在支付系统是硬伤

❌ 3. 没有并发控制

没有提:

  • 乐观锁
  • CAS
  • 条件更新

👉 无法解决“同时支付 + 取消”的核心问题

❌ 4. 没有支付回调 / 幂等

完全没提:

  • 支付网关回调
  • 幂等处理

👉 在真实系统中不可接受

❌ 5. 逻辑偏“单机思维”

整体方案:
👉 更像前端/单线程逻辑推演
👉 不符合分布式支付系统设计

📉 总体评价(甲)

  • 思维方向:⚠️ 偏产品/体验
  • 系统设计:❌ 不达标
  • 分布式意识:❌ 缺失
  • 可落地性:❌ 很低

👉 结论:不推荐录用

👤 三、面试者乙分析

👍 优点(很明显更强)

✔️ 1. 强调后端权威

“不要让前端决定订单死活”

✅ 完全正确

✔️ 2. 引入“PAYING 状态” ✅(关键加分)

“引入支付中状态”

👉 这是标准解法核心点

✔️ 3. 并发控制意识

提到:

  • 乐观锁
  • 资源竞争

👉 说明理解:
👉 支付 vs 取消 是竞争关系

✔️ 4. 有兜底思维(退款)

“如果付了但超时 → 退款”

👉 非常关键

👉 这是支付系统必须有的“最终一致性兜底”

✔️ 5. 考虑攻击场景(羊毛党)

“恶意占用资源怎么办”

👉 有风控意识(非常加分)

✔️ 6. 状态机意识(隐含)

提到:

  • 状态流转
  • 自动任务不能随意暂停

👉 比甲高一个层级

⚠️ 不足

⚠️ 1. “额外5秒缓冲”不够严谨

  • 这是经验策略,不是本质解决方案

  • 真正关键是:

    • 状态控制
    • 原子更新

⚠️ 2. 表达略散

  • 有点边想边说
  • 结构不够清晰

👉 但这是“表达问题”,不是能力问题

📈 总体评价(乙)

  • 架构理解:✅ 较好
  • 分布式意识:✅ 有
  • 风险控制:✅ 强
  • 可落地性:✅ 中高

👉 结论:可以录用(中级偏上水平)

⚖️ 四、最终对比

维度 面试者甲 面试者乙
状态机
并发控制
分布式意识
支付体系理解
用户体验
风控意识
可落地性

🏁 最终结论(2选1)

👉 我会选择:面试者乙

💬 面试官总结(很关键)

这道题本质不是“时间怎么暂停”,而是:

👉 在不可控并发下,如何保证状态一致性

面试者甲的问题本质是:

👉 用“逻辑暂停”逃避并发问题

面试者乙的优势是:

👉 已经进入“分布式系统设计思维”

如果这是蚂蚁二面(确实是这个级别的问题):

  • 甲:一面可能能过,二面基本挂
  • 乙:有机会进入三面