题目重点:从书籍浏览/搜索到库存预留、支付回调和订单状态机,解释批量价格读取与 POST/GET 的语义。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版平台卖实体书,单 SKU 可有库存,用户下单时可预留但支付异步完成;价格单位用整数最小货币单位。电子书、跨国税、优惠券和多仓不是题目固定要求。
- 浏览/搜索允许短暂最终一致;订单状态与库存扣减必须具备可追溯事务/补偿,外部支付调用可能超时或重试。
解题路线
- 读链分开:catalog(book_id,sku,title,metadata)、search index、price(price_version,amount,currency)、inventory(sku,available,reserved)、order/order_line/payment_attempt。搜索索引可以延迟更新,但 checkout 重新从权威价格和库存读取并给用户确认价,不依赖陈旧搜索结果。
- API:GET /books 和 GET /books/{id} 用于安全读取;批量价可 GET 带受限 ID 参数或 POST /prices:batchGet 以承载较大请求体,但 POST 批量读不意味着要产生副作用,须说明缓存/幂等语义。POST /orders 创建订单,幂等键绑定用户与购物车摘要;POST /orders/{id}/payment-attempts 触发支付。
- 订单事务内锁/条件更新库存,available 减数量、reserved 加数量,写 PENDING_PAYMENT 订单与 outbox;支付服务以幂等键调用外部 PSP。回调成功变 PAID 并从 reserved 扣除,失败/超时要查询 PSP 真相后取消或保持待处理,不能直接假设失败。
- 取消/退款是状态机而非删行:已付订单退款通过原 payment id,发货与库存回补按履约状态决定。重复 webhooks 用 provider_event_id 去重,订单更新按状态/版本比较,避免 PAID 被迟到失败回调覆盖。
- 监控库存负数、PENDING_PAYMENT 超时、PSP 回调延迟及订单/支付/库存对账。若扩到多仓,预留键变 (sku,warehouse),先定配送选择再谈分布式协调。
手动走一遍
SKU B 有 available=2。两个并发用户各买 2 本:第一个事务预留后 available=0,reserved=2,第二个条件更新失败并返回缺货,不能都创建可支付订单。若第一个支付回调重放两次,provider_event_id/状态版本使库存只确认一次。
规模与取舍
单次 checkout 与购物车行数 L 成比例,约 O(L) 事务操作加索引查找;搜索查询依索引与返回 K 条约 O(log N+K),N 为书数。真实容量由 SKU、订单 QPS、支付延迟决定,题目未明确数字。
易错点与追问
容易失分的边界
- 把 GET 用作创建订单/扣库存。
- 浏览缓存价格当结算权威价。
- 先调用支付后无事务预留库存,产生超卖与悬空支付。
- 把支付超时当确定失败,重复扣款/错误取消。
- 重复回调推进两次状态或库存。
- 直接从外链引用未知系统的细节当该轮原题。
继续深挖
- 为什么批量价格读取可能选 POST?
- 订单创建与支付间宕机如何恢复?
- 库存预留过期时遇到迟到成功回调怎么办?
- 高并发热门书如何减库存行争用?
- 搜索索引和结算价不一致时向用户怎样展示?
