0. 项目全景:先把业务讲明白
面试的前 30 秒决定面试官还愿不愿意继续问。 而决定这 30 秒效果的,不是你能背出多少中间件名字,而是你能不能让对方相信: 你真的理解这个业务,而不只是把需求写成了代码。
一、这个系统到底在解决什么商业问题
智链云途是一个快递/物流履约平台。把它翻译成一句话:
客户在线下单 → 系统判断这个地址归哪个网点管、值多少钱 → 派快递员上门揽收 → 包裹进入中转网络 → 司机拉货跑干线 → 到目的网点 → 派件送达 → 全程可查。
它不是"电商",也不是"管理系统",而是履约链路的中枢。这决定了三件事:
| 业务性质 | 技术含义 |
|---|---|
| 强链路 —— 一单快递要穿过 7 个环节、5 个微服务 | 任何一环断了,整单卡死,所以可靠性 > 性能 |
| 强空间性 —— 网点是地理实体,线路是地理网络 | 必须引入地图能力(高德)+ 空间数据库(MongoDB 2dsphere)+ 图数据库(Neo4j) |
| 强金额性 —— 每单都要算钱,算错就是资损 | 金额计算必须可追溯、可解释、可回溯 |
面试话术模板:
"这个项目是一个快递履约平台。它和普通电商最大的区别是履约是跨地理、跨组织、多角色协作的长链路, 所以技术上不是 CRUD,而是要解决空间计算、跨服务一致性、长链路可靠性这三类问题。 我们做的每一个技术选型——Neo4j 做线路规划、MongoDB 做服务范围、RabbitMQ 做异步解耦—— 都是被业务逼出来的,不是为了用而用。"
二、一次快递的完整旅程(全书骨架)
后面每一章都会回到这张图。记住每一步用到了哪个亮点。
| 步骤 | 业务动作 | 系统做了什么 | 对应亮点 |
|---|---|---|---|
| ① | 客户下单 | 寄收地址 → 高德地理编码 → 查服务范围反查归属网点 → 匹配运费模板算钱 → 事务落库 → 发延迟消息 | 亮点 1、3 |
| ② | 调度取件 | dispatch 消费订单消息 → 查排班和覆盖范围筛候选人 → AI 选人 → 算提前量决定实时还是延迟推送 → 发取件任务 | 亮点 4 |
| ③ | 作业执行 | work 服务落取派件任务 → 发消息建 ES 索引供搜索 | — |
| ④ | 揽收支付 | 快递员上门 → Seata 保证任务和订单状态一致 → 客户支付 → 回调幂等改支付状态 | 亮点 7、8 |
| ⑤ | 中转运输 | 订单转运单 → Neo4j 按"跳数最少/成本最低"规划路径 → 合并同向运单 → 生成运输任务 → 司机出入库 | 亮点 2 |
| ⑥ | 追踪展示 | 高德算折线轨迹 → 存 MongoDB GeoJSON → 司机/快递员上报最新点 → 四端查询 | 亮点 9 |
| ⑦ | 签收 / 逆向 | 签收关单;拒收退回重派(重新走 ⑤);退款走 trade | 亮点 5、8 |
一次下单的真实代码路径
这段是最值得背下来的调用链,因为它是"你真的跑过这个项目"的最好证据:
// chain-cloud-ms-oms-service OrderServiceImpl#mailingSave
1. getOrderAddress(...) // → user-service 查地址簿
2. buildOrder(...) // 组装订单,同城/跨城打标
3. buildOrderLocation(...) // → area 查省市名 + 高德 geoCode + service-scope 反查网点
4. computeCarriage(...) // → carriage-service 计算运费
5. buildOrderCargo(...) // 组装货物信息
6. appendOtherInfo(...) // → 高德 driving 算距离和预计到达时间
7. crudOrderService.saveOrder(...) // @Transactional 落库
8. noticeOrderStatusChange(...) // → MQ 延迟 5 秒发消息给 dispatch值得主动讲的细节:第 7 步落库用了独立 bean(CrudOrderService)而不是同类内调用, 因为 @Transactional 走代理,同类自调用会失效。这是真实项目里非常典型的坑。
源码定位
chain-cloud-ms-oms-service/src/main/java/com/itheima/ms/oms/service/impl/OrderServiceImpl.java:93-237chain-cloud-ms-oms-service/src/main/java/com/itheima/ms/oms/service/impl/CrudOrderServiceImpl.java:64-85
三、为什么要拆成 22 个微服务(服务边界怎么划)
面试官问"为什么这么拆"的时候,千万不要说"为了微服务化而微服务化"。 正确的答法是:按"数据归属"和"变更频率"划,而不是按"表数量"划。
客户端(四端 App / 后台)
│ 统一入口
▼
Spring Cloud Gateway :9527 ── JWT 鉴权 / 路由 / 限流
│
├── /courier/** → web-courier :18090 (BFF 聚合层)
├── /customer/** → web-customer :18091
├── /driver/** → web-driver :18092
└── /manager/** → web-manager :18093
│
│ Feign(内部 40 个 @FeignClient)
▼
┌────────────────────────────────────────┐
│ 业务微服务层(各自持库,互不直连 DB) │
│ auth 认证 base 基础 oms 订单 │
│ user 会员 work 作业 dispatch 调度 │
│ transport 线路 carriage 运费 │
│ courier 快递员 scope 服务范围 │
│ track 轨迹 driver 司机 search 搜索 │
│ trade 交易 sms 短信 │
└────────────────────────────────────────┘
│
┌────────┴────────┬──────────────┐
▼ ▼ ▼
MySQL / Redis MongoDB / Neo4j RabbitMQ / AI 服务三个值得讲的边界设计
1. BFF 层的存在意义 web-courier / web-customer / web-driver / web-manager 叫 BFF(Backend For Frontend)。 它们不持有业务数据,只做面向端的接口聚合。业务价值很实在:
- 客户端从 5 次请求变成 1 次,减少移动网络往返(快递员在地下室信号差,一次往返 3 秒很致命)
- 四端的字段裁剪、权限点、错误码可以各自定义,不互相污染
2. 数据归属原则(避坑核心) 跨服务调用只走 Feign 接口,绝不跨库直连。 所以你会看到 oms 要查订单定位,要通过 areaFeign 去 base 查区域,而不能自己 select from cc_area。 这就是为什么有 40 个 @FeignClient。
3. 强隔离的三个"另类"服务 这三个服务没有 MySQL,这在传统 Java 项目里很少见,也最能体现"业务倒逼技术选型":
| 服务 | 存储 | 为什么不用 MySQL |
|---|---|---|
chain-cloud-ms-service-scope-service | MongoDB 2dsphere | 业务是多边形(网点服务范围),需要空间索引;MySQL 8 的 GIS 能力弱且生态差 |
chain-cloud-ms-transport-service | Neo4j + Redis | 业务是有向图(网点/中转场/线路),要做多跳路径搜索;关系库递归 CTE 在 3 跳以上性能不可接受 |
chain-cloud-ms-track-service | MongoDB | 业务是轨迹折线(GeoJSON LineString),单条文档可能很大,且需要 GeoJSON 原生支持 |
四、诚实边界:这个项目哪些是"真的",哪些是"README 写了但没有"
这是整本手册最重要的一章。 面试时把没做的说成做了,是当场翻车的最快方式。 下面这张表是我逐文件核对后的结论。
| 能力 | README 的说法 | 源码实际状态 | 面试该怎么说 |
|---|---|---|---|
| AI 登录风控 | 已实现 | ✅ 真实实现(RiskControlServiceImpl),AIGC 调 Agent 1001 | 可以讲,但要主动说"目前只落了 2 个 Agent" |
| AI 成本因子 | 已实现 | ✅ 真实实现(CostFactorServiceImpl → Agent 1006) | 可以讲,这是全项目最好的 AI 工程化案例 |
| AI 其余 8 个 Agent | 1002~1010 已实现 | ❌ DTO 定义了,Feign 方法没有,Controller 没有 | 只能说"参与了方案设计" |
| 运单智能合并 ZSet | XGBoost + Redis ZSet,AI 80% + 等待 20% | ❌ 全仓库零个 ZSet 调用,只有注释和文档 | 绝对不能说"我实现了" |
| Caffeine + Redis 多级缓存 | 已落地 | ❌ 只有 TransportInfoApplication 一个 @EnableCaching,没有任何 @Cacheable | 说"了解设计,生产项目里用过" |
| 双 Token 三验证 | 已实现 | ⚠️ C 端 access/refresh 双 token 是真的;"三验证"这个说法只在文档里,代码里没有 | 按实际讲,别自己发明"三验证" |
| Seata 分布式事务 | 已在 OMS 使用 | ⚠️ OMS 里零个 @GlobalTransactional;在 web-customer / web-courier / driver / web-manager 里 | 说清"用在哪条链路、为什么放 BFF 层" |
| Redisson 分布式锁 | 全局使用 | ⚠️ dispatch 里只有 Bean 没调用;真正在用的是 driver 和 trade | 拿 driver 的公平锁讲,别拿 dispatch |
| 轨迹抽稀 | 已优化 | ❌ 代码里只有一行 TODO:节点多会 OOM | 这是最好的"你发现了一个问题"素材 |
正确说法示例: "这个项目里 AI 我完整落地了 2 个场景:登录风控和线路成本因子修正。 另外 8 个 Agent 的 DTO 契约我参与设计了,但 Java 侧的 Feign 和 Controller 没来得及接。 成本因子那个我印象比较深,因为它是唯一一个 LLM 只输出受约束的修正系数、真正的钱还是 Java 算的 的设计。"
五、技术栈全景与"为什么用它"
不要背版本号,要能回答"不用它会怎样"。
| 领域 | 选型 | 不选它会怎样 |
|---|---|---|
| 微服务框架 | Spring Cloud Alibaba 2021.0.4.0 | Nacos 做注册+配置,Seata 做分布式事务,Sentinel 做流控 |
| 网关 | Spring Cloud Gateway(WebFlux 响应式) | 四端入口统一;响应式是必须的,因为它是全链路非阻塞的 |
| 关系库 | MySQL 8.0 + MyBatis-Plus 3.5.5 | 单表 CRUD 用 MP 的 Wrapper,复杂统计才写 XML |
| 缓存 | Redis 3.27(Redisson 集成) | 分布式锁 + 配置中心(成本、调度配置)+ ZSet(本应做优先级队列) |
| 消息 | RabbitMQ + 延迟消息插件 | 业务需要"5 秒后再处理"(等本地事务提交),这是延迟队列的经典场景 |
| 文档型库 | MongoDB | 轨迹、跟踪信息、服务范围 |
| 图数据库 | Neo4j | 线路路径搜索 |
| 地图 | EagleMap SDK(封装高德) | 地理编码、驾车路径、折线轨迹 |
| 调度 | XXL-Job 2.4.1 | 失败消息补偿、交易对账、排班历史、运力调度 |
| AI | Python 服务 + OpenFeign HTTP | LangChain / LangGraph / XGBoost / BERT 都在 Python 侧 |
六、面试开场 90 秒(照着说)
"我参与的是一个快递履约平台的研发,叫智链云途,是 Spring Cloud Alibaba 的微服务架构,26 个模块。
我先讲业务,因为它决定了我所有的技术选型。这个系统解决的是跨地理的包裹履约: 客户下单后,系统要判断这个地址归哪个网点管、运费多少,然后派快递员上门, 包裹进中转网络之后要规划最优线路,再由司机拉到目的地网点,最后派件送达。
这个业务有三个特点决定了技术栈:第一是空间性,网点是地理实体, 所以我们用了 MongoDB 的 2dsphere 索引做服务范围反查,用 Neo4j 做线路的图路径搜索; 第二是金额敏感性,每单都要算运费和成本,算错就是资损,所以这块我做了最多细节; 第三是长链路可靠性,一单要穿过 7 个环节,任何一环消息丢了整单就卡死, 所以我们做了消息的确认、重试、落库补偿三层兜底。
我在里面主要负责三块:运费计算引擎、基于图数据库的线路成本规划(含 AI 因子修正)、 以及消息可靠性和分布式一致性。我挑其中最硬的一块展开讲——"
说完这句,直接翻到 亮点 2(线路成本与 AI 因子),因为那个故事最完整、最能体现你的判断力。
七、下一章预告
- 亮点 1 — 一句话说清运费:为什么不能直接
重量 × 单价 - 亮点 2 — 图数据库 + LLM:AI 只输出 9 个乘性因子,真正的钱还是 Java 算
- 亮点 3 — 用户输入"朝阳区建国路 88 号",系统怎么知道归哪个网点
- 亮点 4 — 下单之后谁去取?实时推还是延迟推
- 亮点 5 — 订单落库成功了,但消息没发出去 = 一张死单
- 亮点 6 — 22 个服务,怎么只在一处鉴权
- 亮点 7 — 取消一个订单要改两个库,怎么办
- 亮点 8 — 支付回调会重投,退款能分 20 次
- 亮点 9 — 一条轨迹几万个点,怎么存怎么画