面灵AI→

华测一面:交易链路一致性、Spring AI 与状态机

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. 介绍一下实习相关的项目。
  2. 说一下交易链路是怎么设计的,有没有优化?
  3. 这条链路是强一致性吗?
  4. 如果错误情况下,有什么兜底?
  5. 如果兜底方案出问题了,你有什么高可用方案?
  6. 如果用户下单以后直接把软件杀端了,你们怎么处理?
  7. 你们纯依靠 MQ?
  8. 你了解 Spring AI 框架为了能够开发 Agent 做了一些什么设计?
  9. 你对设计模式有什么了解?大概说一下。
  10. 状态机你用到了吗?举一个具体的例子。
  11. Git 过程说一下,冲突了怎么处理的?

《参考解析》

交易链路的强一致、兜底与高可用

先给结论再展开:交易链路通常是「资金/库存强一致,下游履约最终一致」。核心的几步是创建订单、锁库存、发起支付、回调更新订单状态、通知履约。订单与库存如果在同一个库,就用本地事务加行锁或乐观锁版本号;跨服务时用 TCC 或事务消息(RocketMQ 半消息 + 本地事务回查)保证「扣减」和「下单」不会只成功一半。

兜底要按失败点分别讲:支付回调丢失——靠定时对账任务主动查询支付网关的订单状态,把「等回调」变成「回调 + 轮询」双保险;下游通知失败——本地消息表 + 重试队列,重试到上限进人工处理池;超时未支付——延时消息或定时任务关单并回补库存,且回补必须幂等(以订单号 + 状态机流转为准,防止重复回补);金额与账目不一致——T+1 与支付渠道、账务系统三方对账,差额自动挂账、人工介入。所谓高可用方案,本质是让每个补偿动作都幂等、可重试、有对账兜底,再配合 MQ 集群、数据库主从与故障转移、限流降级(高峰期关闭非核心链路,比如积分、推荐),避免局部故障放大成全站不可用。

用户下单后直接杀端,为什么不能只靠 MQ

客户端杀端意味着本地不会有任何回调或重试,所以订单状态的真相必须在服务端。正确设计是:客户端点击下单携带一个唯一幂等键(requestId/bizNo),服务端以状态机记录订单状态;客户端的职责只剩「发起 + 查询」,无论它死在哪一步,用户重进 App 时先按幂等键/订单号查一次服务端状态,是「已创建待支付」就继续支付,是「支付中」就查支付网关,是「已支付」就直接进结果页。

「纯依靠 MQ」的风险在于:消息可能重复(至少一次语义,需要消费端幂等)、可能丢失(要开同步刷盘 + 主从复制 + 发送端本地消息表兜底)、也可能延迟(用户等不及)。所以 MQ 只适合做异步解耦与最终一致性的通道,不适合承载「用户能否看到订单」这类强需求——强需求要么走同步接口 + 事务,要么靠「主动查询 + 对账」补齐。这套「状态机 + 幂等 + 主动查询」的组合也顺带解决了重复提交、前端超时重试导致的重复下单问题。

Spring AI 为 Agent 开发提供了什么

它把大模型接入抽象成了一套 Spring 风格的组件,值得说的有几层:统一的模型抽象(ChatModel/EmbeddingModel/ImageModel 的接口与 ChatClient 的流式 API,换厂商只换依赖和配置);结构化的 Prompt 与消息角色管理(PromptTemplate、System/User/Assistant 消息、ChatMemory 做多轮上下文与窗口裁剪);Tool Calling / Function Callback——把 Java 方法注册成工具,模型返回调用意图,框架负责参数反序列化、执行、把结果回灌给模型,这是 Agent 能「动手」的基础;Advisor 机制——在调用链前后插入可复用的横切逻辑,比如 RAG 检索增强、对话记忆、日志与内容审核,等价于把 Agent 的固定套路做成了可组合的拦截器;RAG 的 ETL 流水线——DocumentReader → TextSplitter → VectorStore 写入与相似度检索(支持 Milvus、Redis、PGVector 等);还有结构化输出转换(把模型回复映射成 POJO/JSON)、多模态支持,以及通过 MCP 客户端把外部工具/服务接进模型。

面试里可以补一句自己的判断:框架解决的是「标准件和胶水」,但 Agent 的可靠性仍然要靠自己设计——工具调用的幂等与超时、上下文预算管理、循环终止条件、失败重试与降级,这些框架只能给钩子,不能替你决定。

状态机的实际应用

最典型的例子就是订单/支付:把订单抽象成状态(待支付、支付中、已支付、已发货、已完成、已取消、已退款)和事件(创建、支付成功、支付超时、发货、取消、退款),在代码里用一张「当前状态 × 事件 → 目标状态 + 动作」的转移表驱动,而不是到处写 if (status == ...)。

好处有三点,正好对应面试官想听的:① 非法流转直接被拒绝(已完成的订单不能再取消),把业务规则集中到一处,避免并发下的状态错乱;② 每次流转可落一条状态流水,配合乐观锁版本号(UPDATE ... WHERE id=? AND status=?)做幂等,MQ 重复消费也不会重复发货;③ 新状态只是加一行配置,容易扩展和画图给产品确认。实现上小项目手写枚举 + 转移表就够,复杂流程可以用 Spring StateMachine 或 Cola StateMachine;关键配套是状态变更与业务动作放在同一个事务里,并保证「状态先落库、再发消息」。

设计模式怎么答

不要背 23 种名字,挑交易链路里真实用到的讲:策略模式——不同支付渠道/不同优惠计算各自实现同一接口,由工厂或 Spring 容器按类型路由,新增渠道不改主流程;状态模式——订单状态驱动行为,替代大量条件分支;责任链模式——下单前的风控校验、参数校验、限流逐层过滤,可以动态增删节点;模板方法——下单主流程固定(校验 → 锁库存 → 创建订单 → 通知),差异步骤交给子类或回调;工厂/建造者——复杂订单对象的组装;单例——无状态的服务与配置(Spring 默认单例,要注意别在里面存可变状态,否则会有线程安全问题);观察者/发布订阅——订单状态变更后通知积分、通知、履约等下游,配合 MQ 解耦。

答这类题最好的结构是「模式 + 我解决的具体问题 + 不用它会怎样」,再补一个反例:过度设计比不设计更糟,比如只有两种类型就上抽象工厂,会显著增加阅读成本。