项目全景与业务主线

00 · 总览

项目全景:先把业务讲明白

在讲任何技术之前,先能讲清楚一次快递从下单到签收的完整业务链路——这是所有技术选型的根因。

涉及模块:全项目 · 26 个模块 / 903 个 Java 文件 / 51,173 行

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

一次下单的真实代码路径

这段是最值得背下来的调用链,因为它是"你真的跑过这个项目"的最好证据:

java
// 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-237 chain-cloud-ms-oms-service/src/main/java/com/itheima/ms/oms/service/impl/CrudOrderServiceImpl.java:64-85


三、为什么要拆成 22 个微服务(服务边界怎么划)

面试官问"为什么这么拆"的时候,千万不要说"为了微服务化而微服务化"。 正确的答法是:按"数据归属"和"变更频率"划,而不是按"表数量"划。

text
客户端(四端 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-serviceMongoDB 2dsphere业务是多边形(网点服务范围),需要空间索引;MySQL 8 的 GIS 能力弱且生态差
chain-cloud-ms-transport-serviceNeo4j + Redis业务是有向图(网点/中转场/线路),要做多跳路径搜索;关系库递归 CTE 在 3 跳以上性能不可接受
chain-cloud-ms-track-serviceMongoDB业务是轨迹折线(GeoJSON LineString),单条文档可能很大,且需要 GeoJSON 原生支持

四、诚实边界:这个项目哪些是"真的",哪些是"README 写了但没有"

这是整本手册最重要的一章。 面试时把没做的说成做了,是当场翻车的最快方式。 下面这张表是我逐文件核对后的结论。

能力README 的说法源码实际状态面试该怎么说
AI 登录风控已实现✅ 真实实现(RiskControlServiceImpl),AIGC 调 Agent 1001可以讲,但要主动说"目前只落了 2 个 Agent"
AI 成本因子已实现✅ 真实实现(CostFactorServiceImpl → Agent 1006)可以讲,这是全项目最好的 AI 工程化案例
AI 其余 8 个 Agent1002~1010 已实现❌ DTO 定义了,Feign 方法没有,Controller 没有只能说"参与了方案设计"
运单智能合并 ZSetXGBoost + 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.0Nacos 做注册+配置,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失败消息补偿、交易对账、排班历史、运力调度
AIPython 服务 + OpenFeign HTTPLangChain / 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 — 一条轨迹几万个点,怎么存怎么画