global-flybtb

飞猪国际机票 B2B 分销系统 — 架构全景分析 · Node.js 视角解读

10
Maven 模块
7
业务领域
2
SPI 渠道
15+
HSF 接口
4
DB 表
// System Overview

系统总览

连接外部供应商(Pkfare、Traveloka)和内部交易系统,处理搜索、下单、出票、改签、退票全链路

外部渠道 (Input)
🌐 Pkfare SPI
HTTP REST · AES加密
🌐 Traveloka SPI
OAuth 2.0 + REST
📨 MetaQ 消息
异步事件通知
global-flybtb 中台
✈️ 搜索 · 下单 · 验价
核心交易链路
🔄 改签 · 退票 · 出票
售后服务链路
⚙️ QPS限流 · 需供匹配
治理与质量控制
内部系统 (Output)
🏪 inside-search
内部搜索引擎
💰 inside-trade / flimmy
内部交易 / 资金
📋 flyrp / change-flight-tp
退票 / 改签 TP
// DDD Layered Architecture

DDD 分层架构

点击每层展开详细说明、关键文件和 Node.js 类比

🚀

Start 启动层

Spring Boot 入口 · HSF 服务发布
global-flybtb-start
≈ app.ts

Spring Boot Application 入口 + MainController 健康检查。相当于 Node.js 中的 app.listen() + 路由注册。

关键文件

Application.java — 启动类MainController.java — 健康检查application.properties — 配置META-INF/services/ — SPI声明
// Node.js 类比
import express from 'express'
const app = express()
app.get('/health', (req, res) => res.json({ status: 'UP' }))
app.listen(8080) // ← 相当于 Spring Boot 的 @SpringBootApplication
📡

Client 接口定义层

HSF 接口 + DTO 定义(零实现,纯类型)
global-flybtb-client
≈ types.d.ts

纯接口 + DTO 定义,不包含任何实现。其他应用通过此 jar 包调用本应用的 HSF 服务。相当于发一个 npm types 包或 OpenAPI schema。

核心接口(点击查看详情)

SearchQueryAppService — 搜索BookAppService — 下单PricingQueryAppService — 验价TicketingAppService — 出票ChangeAppService — 改签RefundAppService — 退票CancelAppService — 取消OrderQueryAppService — 订单查询AncillaryQueryAppService — 附加服务GfsQueryAppService — GFS搜索
// Java: HSF 接口定义
public interface SearchQueryAppService {
    ResultDO<SearchResDTO> search(SearchReqDTO req);
    ResultDO<EnrichResDTO> enrich(EnrichReqDTO req);
}

// Node.js 类比: TypeScript 接口
interface SearchQueryService {
    search(req: SearchReq): Promise<Result<SearchRes>>
    enrich(req: EnrichReq): Promise<Result<EnrichRes>>
}
🎯

Application 应用层

业务编排 — 组合 Domain + Adaptor,处理事务和异常
global-flybtb-application
≈ controller

实现 Client 层的接口,编排业务流程。不含核心逻辑,只做"指挥"。相当于 Express/Koa 的 controller。

关键实现类(点击查看方法结构)

SearchQueryAppServiceImplBookAppServiceImplSaleOrderAppServiceImplTicketingAppServiceImplChangeAppServiceImplChangeSaleOrderAppServiceImplRefundAppServiceImplRefundSaleOrderAppServiceImpl
💎

Domain 领域层(核心)

聚合根 · 实体 · 值对象 · 领域服务 · 仓储接口
global-flybtb-domain
≈ service/

系统的"大脑",所有核心业务规则。不依赖外部框架,纯业务逻辑。

业务子域(点击查看详情)

search/ 搜索控制buyagent/ 采购商change/ 改签refund/ 退票markuprule/ 加价message/ 消息推送common/ 公共

DDD 战术模式

Aggregate 聚合根Entity 实体Value 值对象DomainService 领域服务Repository 仓储接口
🔌

Adaptor 适配层(最大模块)

防腐层 — input 入口 / output 出口 / inner 内部工具
global-flybtb-adaptor
≈ gateway

最大的模块,隔离所有外部系统。分三个方向:input 接收请求、output 调用下游、inner 内部工具。

Input 入口(接收外部请求)

spi/pkfare/ — 7个HTTP接口spi/traveloka/ — 8个HTTP接口event/ — EventBus监听器bcp/ — BCP适配diamond/ — 动态配置

Output 出口(调用下游系统)

inside/ 内部系统change/ 改签TPrefund/ 退票TPaccount/ 账务order/ 订单查询search/ 搜索gfs/ GFS搜索common/ 锁/缓存/支付dingtalk/ 钉钉告警quality/ 质量统计ancillary/ 附加服务
🏗️

Infrastructure 基础设施层

Repository 实现 · MySQL(TDDL) · Redis · Rye配置
global-flybtb-infrastructure
≈ db/cache

实现 Domain 层的 Repository 接口,操作 MySQL(TDDL 分库分表)、Redis 缓存、Rye 配置。

Repository 实现(点击查看代码)

SaleOrderRepositoryImplChangeSaleOrderRepositoryImplRefundOrderRepositoryImplBuyAgentRepositoryImplMarkupRuleRepositoryImpl

MySQL DAO (MyBatis)

SaleOrderDAO + SaleOrderDAO.xmlChangeSaleOrderDAO + xmlRefundSaleOrderDAO + xmlMarkupRuleDAO + xml

Rye 配置缓存

BuyerAbilityControlConfigCacheSearchCacheStrategyConfigCacheBuyerSearchQpsRestrictConfigCacheAppKeyPermissionCacheSuezDemandSupplyConfigCache
📦

Model · Util · Monitor 辅助模块

共享数据模型 · 工具类 · 日志监控
3个模块
≈ shared/utils

跨模块共享的 DTO/Enum/常量,通用工具函数,日志监控。

global-flybtb-util

JsonUtilDateUtilMoneyConvertUtilHessianUtilPatternUtilLogContextUtilsLogInfo/LogReason

日志枚举体系

BizTypeEnumInterfaceTypeEnumEntranceTypeEnumClientTypeEnumEnrichModeEnumReasonEnum
// Business Domains

业务领域详解

点击卡片查看每个领域的完整链路、Domain 服务和关键约束

🔍

Search 搜索

航线搜索 + Enrich 询价 + GFS
RestrictControl需供匹配搜索过滤缓存
6 个 Domain 服务最复杂链路
💰

Trade 交易

下单 · 验价 · 取消 · 出票 · 生单
QPS限流证件校验分布式锁Inside下单销售单
5 个 AppService分布式锁300s
🔄

Change 改签

咨询 · 确认 · 支付重试 · 取消
咨询确认支付轮询(2s)更新
轮询间隔2秒失败钉钉告警
↩️

Refund 退票

咨询 · 申请 · 确认 · 详情
订单鉴权咨询标识flyrp退票单
订单鉴权必须咨询标识分流
🏢

BuyAgent 采购商

配置 · MockPricing · QPS · 权限
BuyAgentAggregateMockPricingDomainServiceQpsRestrictDomainService
📊

MarkupRule 加价 + Message 消息

加价计算 · 消息推送 · Webhook重试
MarkupCalculateDomainServicePushBuyAgentMessageDomainService
// Search Pipeline Detail

搜索链路详解

点击每个步骤查看详细说明和代码

01
SearchRestrict
账号+QPS检查
02
SearchControl
搜索控制规则
03
需供匹配
优质供给筛选
04
SearchCache
缓存命中策略
05
Inside搜索
调用搜索引擎
06
ResultFilter
黑名单过滤
07
EnrichControl
询价补充控制
08
异步+清理
缓存写入清理
// SPI Integrations

SPI 外部渠道

点击接口行查看请求/响应结构和调用链路

方法接口说明Controller → Service
POST/search航班搜索PkfareSearchController → PkfareSearchService
POST/qte询价 (Quote)PkfareQteController → PkfareQteService
POST/order创建订单PkfareOrderController → PkfareOrderService
POST/ticketing出票通知— → PkfareTicketIssuanceService
POST/unissuedOrder待出票查询PkfareUnissuedOrderController
POST/orderStatusPush状态推送PkfareOrderStatusPushController
POST/flightChange航变通知— → PkfareFlightChangeNotifyService
方法接口说明Controller → Service
POST/tokenOAuth TokenTravelokaTokenController → TravelokaTokenService
POST/search航班搜索TravelokaSearchController → TravelokaSearchService
POST/preOrderVerify预下单校验TravelokaPreOrderVerifyController
POST/order创建订单TravelokaOrderController → TravelokaOrderService
POST/pay支付TravelokaPayController → TravelokaPayService
GET/orderDetail订单详情TravelokaOrderDetailController
POST/cancel取消订单TravelokaCancelController → TravelokaCancelService
POST/ancillarySearch附加服务搜索TravelokaAncillarySearchController
🛡️

SpiAuthValidatorAdaptor

AES解密 + 签名校验
📊

SpiControllerMonitorAop

AOP 监控切面
⏱️

SpiQpsAdaptor

SPI 级 QPS 限流

SpiTimeoutControl

超时控制切面
🔐

SpiAesEncryptUtil

AES 加解密工具
🌐

SpiHttpClientUtil

HTTP 客户端工具
// Event-Driven Architecture

事件驱动架构

点击事件查看处理逻辑

💰

TradeEvent 交易事件

支付/出票/取消
TradePaidProcessorTradeSucceedProcessorTradeCanceledProcessor
🔄

ChangeEvent 改签事件

费用/成功/拒绝/超时
ChangeFeeProcessorChangeSuccessProcessorChangeCloseOfAgentRefuseChangeCloseOfTimeOut
↩️

RefundEvent 退票事件

成功/罚金/失败
RefundSuccessProcessorRefundPenaltyQuoteProcessorRefundFailProcessor
✈️

FlightChangeEvent 航变

航班变动通知
FlightChangeEventProcessor
💳

DeductionFundEvent 扣款

Flimmy 资金扣款
FlimmyDeductSuccessProcessorFlimmyDeductFailedProcessorFlimmyPaymentSuccessProcessor
📬

PushMessageRetry 消息重试

MetaQ 重试推送
PushBuyAgentMessageRetryProcessor
// Infrastructure

基础设施

点击查看技术细节

🗄️

MySQL (TDDL)

分库分表 · MyBatis
SaleOrderChangeSaleOrderRefundSaleOrderMarkupRule

Redis 双实例

核心 + 非核心分离
搜索缓存分布式锁QPS计数Session
📨

MetaQ

消息队列
交易消息改签消息消息重试
⚙️

Rye 配置

动态配置下发
采购商能力搜索策略QPS限制
📡

HSF RPC

≈ gRPC
@HSFProvider@HSFConsumerRPCFilter
📊

Hologres

OLAP 分析
HoloDataSourceHoloReadProcessor
🔔

EventBus

进程内事件
CacheDeleteCacheWriteSearchMonitorQualityMetric
🔐

分布式锁

Redis实现 300s
PAY_PREFIXCONFIRM_CHANGE
// Module Dependencies

模块依赖拓扑

start
applicationadaptorinfrastructure
application
clientdomainmodel
adaptor
clientinside-clientdomainmodelutil
infrastructure
domainmodelutil
domain
modelutil
client
model
model / util
无依赖(叶子节点)
// Code Patterns

代码模式对照 (Java ↔ Node.js)

☕ JAVA (HSF)
@HSFProvider(
  serviceInterface = SearchQueryAppService.class,
  serviceVersion = "${spring.hsf.version}",
  clientTimeout = 60000)
public class SearchQueryAppServiceImpl
    implements SearchQueryAppService {

  @Autowired
  private SearchRestrictDomainService restrictSvc;

  @Override
  public ResultDO<SearchResDTO> search(
      SearchReqDTO req) {
    // 业务编排...
    return ResultDO.buildSuccessResult(res);
  }
}
🟢 NODE.JS 等价
// tRPC / NestJS 风格
@Injectable()
export class SearchService {
  constructor(
    private restrictSvc: SearchRestrictService,
  ) {}

  async search(
      req: SearchReq): Promise<Result<SearchRes>> {
    // 业务编排...
    return { success: true, data: res }
  }
}

// HSF ≈ gRPC/tRPC: RPC 接口框架
// @HSFProvider ≈ @GrpcMethod / router
// @Autowired ≈ constructor DI
// ResultDO<T> ≈ { success, data, code }
☕ JAVA
public ResultDO<T> book(BookReqDTO req) {
  try {
    req.check();           // 参数校验
    qpsRestrict(...);      // QPS限流
    validateCred(...);    // 证件校验
    lock = lock(key, 300); // 分布式锁
    result = adaptor.book(...);
    return ResultDO.buildSuccess(result);
  } catch (MultiLanguageBizException e) {
    return ResultDO.buildFail(
      e.getErrorCode(),
      e.translate(req.getLanguage()));
  } finally {
    if (lock != null) unlock(lock);
    LogContextUtils.printLogAndRemove();
  }
}
🟢 NODE.JS
async book(req: BookReq): Promise<Result<T>> {
  try {
    validate(req, schema)     // zod
    await limiter.check(key) // rate-limit
    validatePassport(req.pax) // 证件
    const release = await redlock
      .lock(`book:${req.orderNum}`, 300_000)
    try {
      const result = await adaptor.book(...)
      return { success: true, data: result }
    } finally {
      await release()
    }
  } catch (e) {
    if (e instanceof BizError)
      return { success: false, code: e.code,
        msg: t(e.code, req.lang) }
    throw e
  }
}
☕ JAVA
public class ChangeSaleOrderAggregate
    extends BaseAggregate {
  private ChangeSaleOrderEntity entity;

  public void confirmPayment(
      PaySucceededParam param) {
    if (!canPay()) {
      throw new AggregateException(
        "状态不允许支付");
    }
    entity.setStatus(PAID);
    entity.setPayTime(new Date());
  }

  public void transferSuccess(...) { ... }
  public void refundFailed(...) { ... }
}
🟢 NODE.JS
// 聚合根 = 包含业务规则的实体
// 所有状态变更必须通过聚合根方法
class ChangeSaleOrder {
  private entity: ChangeOrderEntity

  confirmPayment(p: PaySucceeded) {
    if (!this.canPay())
      throw new DomainError('状态不允许')
    this.entity.status = 'PAID'
    this.entity.payTime = new Date()
  }

  transferSuccess(...) { ... }
  refundFailed(...) { ... }
}
// 核心:外部不能直接 order.status = X
// 必须走 order.confirmPayment() 方法
☕ JAVA
// Domain 层 - 接口
public interface SaleOrderRepository
    extends Repository {
  SaleOrderAggregate getById(Long id);
  void save(SaleOrderAggregate agg);
}

// Infrastructure 层 - 实现
public class SaleOrderRepositoryImpl
    implements SaleOrderRepository {
  @Autowired SaleOrderDAO dao; // MyBatis

  public SaleOrderAggregate getById(Long id) {
    SaleOrderPO po = dao.selectById(id);
    return converter.toAggregate(po);
  }
}
🟢 NODE.JS
// 接口定义
interface SaleOrderRepo {
  getById(id: number): Promise<SaleOrder>
  save(order: SaleOrder): Promise<void>
}

// Prisma/Drizzle 实现
class SaleOrderRepoImpl
    implements SaleOrderRepo {
  async getById(id: number) {
    const row = await db.saleOrder
      .findUnique({ where: { id } })
    return toAggregate(row)
  }
}
// PO(数据库行) ↔ Aggregate(业务对象)
// MyBatis XML ≈ Prisma schema
// TDDL 分库分表 ≈ Vitess/PlanetScale
// Concept Mapping

概念映射速查表

Spring BootNestJS / Express
HSF (RPC)gRPC / tRPC
@Autowired@Inject / tsyringe
Maven Modulepnpm workspace pkg
MyBatis+TDDLPrisma / Drizzle
ResultDO<T>Result<T,E> / Either
Diamondenv vars / ConfigMap
MetaQBullMQ / Kafka.js
EventBusEventEmitter / RxJS
ThreadLocalAsyncLocalStorage
Assemblermapper / transformer
Converterserializer / dto map
AOP @Aspectmiddleware / decorator
EagleEyeOpenTelemetry
Hessian 序列化protobuf / msgpack
Interface+Implinterface + class
Jedisioredis
Adaptor 防腐gateway / adapter
@HSFProvider@GrpcMethod
MapStructclass-transformer