> For the complete documentation index, see [llms.txt](https://resources.atriptech.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://resources.atriptech.com/api-wen-dang/product-guides/extensions-and-integrations/atlas-shu-ju-tui-song/flight-data-feed.md).

# Atlas 数据推送 — 技术接入指南

本指南帮助您的技术团队完成 Atlas 航班价格数据的接收和处理。

{% hint style="info" %}
💬 **需要帮助？** 如果遇到问题，请在帮助中心咨询 Eva，快速获取诊断建议。

<a href="https://www.atriptech.com/" class="button primary" data-icon="comments">咨询 Eva</a>
{% endhint %}

本指南说明如何接收、处理和运营 Atlas 数据推送。

有关产品适用性、客户使用场景和推荐预订模式，请参阅 [Atlas 数据推送](/api-wen-dang/product-guides/extensions-and-integrations/atlas-shu-ju-tui-song.md)。

### 开始前

使用数据推送构建本地航班价格数据层。

它适用于以下场景：

* 低延迟搜索、展示、比较和排序
* 批量定价、分析和动态打包
* 基于结构化票价数据的 AI 检索

以下业务尤其适合使用数据推送：

* 航班聚合比价平台、OTA 和 AI 旅行助手
* 本地价格计算和动态打包
* 需要降低展示阶段 API 调用量的产品

### 数据推送与 Transaction API 配合使用

数据推送不是预订 API，也不是最终预订的真实来源。它用于展示阶段的本地查询。

从本地数据库读取报价，以支持搜索、比较和行程展示。

结账时调用 `getOffers.do`，获取最新可订价格和库存。

Transaction API 还负责创建订单、支付和出票。

推荐流程如下：

1. 从本地数据中搜索航班报价。
2. 用户选择行程。
3. 调用 `getOffers.do`，获取可订报价和 `OfferId`。
4. 创建订单，然后完成支付和出票。

### 第 1 步：准备 SFTP 服务器

Atlas 将全量和增量数据文件推送至客户提供的 SFTP 服务器。请向 Atlas 提供以下信息：

| 参数    | 说明            | 示例                           |
| ----- | ------------- | ---------------------------- |
| 服务器地址 | SFTP 主机名或 IP  | `sftp.yourcompany.com`       |
| 端口    | SFTP 端口，默认 22 | `22`                         |
| 用户名   | Atlas 用于推送的账号 | `atlas_push`                 |
| 认证方式  | 推荐 SSH 公钥认证   | SSH Key                      |
| 全量目录  | 全量文件目标目录      | `/data/atlas/pricing/full/`  |
| 增量目录  | 增量文件目标目录      | `/data/atlas/pricing/incre/` |

#### 认证方式

推荐使用 SSH 公钥认证，密钥算法使用 **ed25519**（首选）或 **RSA-4096**。

* 客户在 SFTP 推送账号的 `~/.ssh/authorized_keys` 中部署 Atlas 提供的公钥。
* Atlas 妥善保管相应私钥，并使用该私钥连接客户 SFTP。
* 如暂时使用密码认证，密码应不少于 16 位，包含大小写字母、数字和符号，并至少每 90 天轮换一次。密码认证将在后续版本下线，请尽早迁移至 SSH Key。

典型的公钥部署方式如下（密钥的生成、交付及保管责任以双方最终约定为准）：

```bash
# 生成 ed25519 密钥
ssh-keygen -t ed25519 -f atlas_push_ed25519 -N ""
# 将公钥部署到 SFTP 推送账号
cat atlas_push_ed25519.pub >> /home/atlas_push/.ssh/authorized_keys
chmod 600 /home/atlas_push/.ssh/authorized_keys
```

#### IP 白名单、目录与审计

* 请仅允许 Atlas 提供的正式推送源 IP 访问指定 SFTP 端口；正式 IP 清单以客户经理通知为准。若源 IP 变更，Atlas 将提前至少 14 个自然日书面通知。
* 全量与增量目录应分别配置；可指向同一物理目录，通过文件名前缀区分。两个目录均须存在，并授予推送账号写入权限。
* 文件采用原子写入：推送期间先写临时文件，完成后才改名为最终文件名。扫描目录时只处理最终文件名，忽略临时文件。
* 建议按数据范围预留磁盘空间；大数据集可按约 15 GB/天的未压缩文件量（约 2 GB/天压缩后）评估。
* 客户侧建议保留至少 180 天的 SFTP 登录事件和文件操作审计日志。Atlas 侧也会记录推送时间、文件名、大小、记录数和成功/失败结果；需要核查时可向客户经理索取。

### 第 2 步：确认数据需求

| 配置项  | 说明                    | 默认值        |
| ---- | --------------------- | ---------- |
| 航司范围 | 逗号分隔的航司 IATA 代码       | 所有可用航司     |
| 同步天数 | 推送未来航班的覆盖天数           | 360 天      |
| 压缩格式 | `zip` 或 `gz`          | `zip`      |
| 增量推送 | 是否启用增量文件              | 启用         |
| 增量间隔 | 增量文件推送频率              | 2 分钟       |
| 接入模式 | `SHARED` 或 `SEPARATE` | `SEPARATE` |

**接入模式：SHARED 与 SEPARATE**

| 维度     | SHARED           | SEPARATE（默认）                   |
| ------ | ---------------- | ------------------------------ |
| 调度     | 全量、增量串行；全量期间增量排队 | 全量、增量并行                        |
| 到达顺序   | 有序               | 可能乱序                           |
| 客户合并规则 | 按顺序处理即可          | 同一 ID 必须保留 `update_time` 最新的数据 |
| 适用场景   | 可接受全量期间增量滞后      | 绝大多数生产场景                       |

两种模式的文件格式、列结构和删除语义完全一致，差异仅在调度方式。SEPARATE 模式中，全量快照生成后到达前，更新更晚的增量可能已先被处理，因此合并时必须比较 `update_time`，避免旧快照覆盖新数据。

```mermaid
sequenceDiagram
    participant A as Atlas 推送
    participant S as 客户 SFTP
    rect rgb(240,240,240)
    Note over A,S: SHARED：增量在全量期间排队
    A->>S: 全量（10:00 开始生成，10:15 推送）
    Note over A: 10:02–10:14 的增量排队等待
    A->>S: 增量（10:15 后补发）
    end
    rect rgb(235,245,255)
    Note over A,S: SEPARATE：并行，文件可能乱序
    A->>S: 增量 10:04（先于全量到达）
    A->>S: 全量（快照生成于 10:00，10:15 到达）
    Note over S: 必须按 update_time 判新旧，否则旧快照覆盖新增量
    end
```

接入模式由 Atlas 侧配置，默认 `SEPARATE`。如需启用 `SHARED` 或切换模式，请联系客户经理；切换后客户应同步调整合并逻辑。

**如何配置？**

请向 Atlas 确认航司范围、同步天数、压缩格式、全量和增量目录、增量间隔及接入模式。后续需要调整上述配置时，联系客户经理即可；变更后请同步校验本地扫描策略与合并逻辑。

### 第 3 步：接收并解析数据文件

#### 文件命名规则

时间戳 `yyyyMMddHHmmss` 为文件生成时间，使用 UTC 时区，与记录的 `update_time` 同时区，可用于水位线比较。

| 文件类型 | 名称模板                                                                     |
| ---- | ------------------------------------------------------------------------ |
| 全量   | `atlas_yyyyMMddHHmmss.csv.zip` 或 `atlas_yyyyMMddHHmmss.csv.gz`           |
| 增量   | `atlas_incr_yyyyMMddHHmmss.csv.zip` 或 `atlas_incr_yyyyMMddHHmmss.csv.gz` |

文件名前缀 `atlas_` 与 `atlas_incr_` 用于区分全量和增量文件。

例如，`atlas_20260601153045.csv.zip` 表示 2026-06-01 15:30:45 UTC 生成的全量文件；`atlas_incr_20260601153245.csv.zip` 表示随后生成的增量文件。即使两类文件落在同一目录，也应按此前缀分别识别和处理。

#### 全量推送 vs 增量推送

| 维度     | 全量推送                      | 增量推送                             |
| ------ | ------------------------- | -------------------------------- |
| 时机     | 首次接入后，以及默认每 24 小时刷新       | 默认每 2 分钟一次，可配置                   |
| 内容     | 当前有效价格记录的完整快照             | 自上次交付后发生变化的记录                    |
| 文件格式   | 63 列 CSV，记录均为 `deleted=0` | 同样的 63 列 CSV，可包含 `deleted=1`     |
| 文件名    | `atlas_<时间戳>.csv.zip/.gz` | `atlas_incr_<时间戳>.csv.zip/.gz`   |
| 客户处理方式 | 重建/刷新本地基线，并按快照缺失规则处理旧记录   | 按 `atlas_flight_fare_id` 合并至本地基线 |

全量与增量可复用同一套 CSV 解析代码；区别仅在记录范围和合并语义。

#### 编码、分隔符与时区

* 文件格式：CSV，首行必须为表头。
* 字符编码：UTF-8，无 BOM。
* 分隔和引号：逗号分隔；包含逗号或换行的字段按 RFC 4180 使用双引号。
* `update_time`：UTC，ISO 8601 格式 `yyyy-MM-dd'T'HH:mm:ss'Z'`。
* 航班出发、到达日期和时间：对应机场当地时间，不转换为 UTC。

#### 解压文件

**Zip（默认）：**

```bash
unzip -p atlas_20260601153045.csv.zip > data.csv
```

**Gzip：**

```bash
gunzip -c atlas_20260601153045.csv.gz > data.csv
```

#### CSV Schema v2

全量和增量文件使用**相同的 63 列表头、列顺序和数据类型**。请按字段名解析，并校验列数和列顺序。

```csv
atlas_flight_fare_id,segment_1_origin_airport,segment_1_origin_terminal_airport,segment_1_date_local_takeoff,segment_1_hours_local_takeoff,segment_1_destination_airport,segment_1_destination_terminal_airport,segment_1_date_local_landing,segment_1_hours_local_landing,segment_1_via_airport,segment_1_airline,segment_1_flight_number,segment_1_aircraft_code,segment_1_cabin_class,segment_1_rbd,segment_1_operating_airline,segment_1_operating_flight_number,segment_1_fare_family,segment_2_origin_airport,segment_2_origin_terminal_airport,segment_2_date_local_takeoff,segment_2_hours_local_takeoff,segment_2_destination_airport,segment_2_destination_terminal_airport,segment_2_date_local_landing,segment_2_hours_local_landing,segment_2_via_airport,segment_2_airline,segment_2_flight_number,segment_2_aircraft_code,segment_2_cabin_class,segment_2_rbd,segment_2_operating_airline,segment_2_operating_flight_number,segment_2_fare_family,segment_3_origin_airport,segment_3_origin_terminal_airport,segment_3_date_local_takeoff,segment_3_hours_local_takeoff,segment_3_destination_airport,segment_3_destination_terminal_airport,segment_3_date_local_landing,segment_3_hours_local_landing,segment_3_via_airport,segment_3_airline,segment_3_flight_number,segment_3_aircraft_code,segment_3_cabin_class,segment_3_rbd,segment_3_operating_airline,segment_3_operating_flight_number,segment_3_fare_family,luggage_options,available_seats,currency,price,tax,transaction_fee,infant_price,infant_tax,need_apis,deleted,update_time
```

**标识字段**

| 列序号 | 字段                     | 类型     | 是否为空 | 说明                                              |
| --- | ---------------------- | ------ | ---- | ----------------------------------------------- |
| 1   | `atlas_flight_fare_id` | String | 否    | 唯一价格记录 ID；用于新增、更新和删除。不得以航班号作为主键；建议预留至少 100 个字符。 |

**航段字段**

每个行程最多包含三个航段；不存在的航段其字段为空。三个航段使用完全相同的字段结构，字段名以 `segment_1_`、`segment_2_` 或 `segment_3_` 为前缀。

| 列序号（第 1/2/3 航段） | 字段后缀                           | 格式           | 是否为空     | 说明                         |
| --------------- | ------------------------------ | ------------ | -------- | -------------------------- |
| 2 / 19 / 36     | `origin_airport`               | String       | 航段不存在时为空 | 出发机场 IATA 三字码。             |
| 3 / 20 / 37     | `origin_terminal_airport`      | String       | 是        | 出发航站楼。                     |
| 4 / 21 / 38     | `date_local_takeoff`           | `yyyy-MM-dd` | 航段不存在时为空 | 出发机场当地日期。                  |
| 5 / 22 / 39     | `hours_local_takeoff`          | `HH:mm`      | 航段不存在时为空 | 出发机场当地时间。                  |
| 6 / 23 / 40     | `destination_airport`          | String       | 航段不存在时为空 | 到达机场 IATA 三字码。             |
| 7 / 24 / 41     | `destination_terminal_airport` | String       | 是        | 到达航站楼。                     |
| 8 / 25 / 42     | `date_local_landing`           | `yyyy-MM-dd` | 航段不存在时为空 | 到达机场当地日期。                  |
| 9 / 26 / 43     | `hours_local_landing`          | `HH:mm`      | 航段不存在时为空 | 到达机场当地时间。                  |
| 10 / 27 / 44    | `via_airport`                  | String       | 是        | 同一航班号的经停机场；不是不同航段之间的中转机场。  |
| 11 / 28 / 45    | `airline`                      | String       | 航段不存在时为空 | 市场航司 IATA 代码。              |
| 12 / 29 / 46    | `flight_number`                | String       | 航段不存在时为空 | 市场航班号的数字部分，不含航司前缀。         |
| 13 / 30 / 47    | `aircraft_code`                | String       | 是        | 机型代码。                      |
| 14 / 31 / 48    | `cabin_class`                  | Integer      | 是        | `1` 经济舱、`2` 商务舱、`3` 头等舱。   |
| 15 / 32 / 49    | `rbd`                          | String       | 是        | 订座舱位代码，例如 `Y`、`J`、`C`。     |
| 16 / 33 / 50    | `operating_airline`            | String       | 是        | 实际承运航司 IATA 代码。            |
| 17 / 34 / 51    | `operating_flight_number`      | String       | 是        | 实际承运航班号的数字部分，不含航司前缀。       |
| 18 / 35 / 52    | `fare_family`                  | String       | 是        | 运价品牌或产品名称，例如 `ECO`、`FLEX`。 |

**航班号格式**

`flight_number` 和 `operating_flight_number` 均不包含航司前缀，前导零规范化如下：

| 原始航班号   | 文件中的航班号 |
| ------- | ------- |
| `VZ123` | `123`   |
| `VZ5`   | `05`    |
| `VZ005` | `05`    |
| `OD810` | `810`   |

完整市场航班号应拼接 `airline + flight_number`；完整实际承运航班号应拼接 `operating_airline + operating_flight_number`。

**行李、库存、价格与状态字段**

| 列序号 | 字段                | 格式             | 是否为空    | 说明                                                                      |
| --- | ----------------- | -------------- | ------- | ----------------------------------------------------------------------- |
| 53  | `luggage_options` | String         | 否       | 成人托运行李，格式为 `件数-每件公斤数-价格`；例如 `1-20-0` 表示 1 件、每件 20kg、免费。无行李信息时为 `0-0-0`。 |
| 54  | `available_seats` | Integer        | 否       | 整个行程当前可售座位数。                                                            |
| 55  | `currency`        | String         | 有效记录必填  | ISO 4217 三位币种代码，例如 `CNY`、`USD`、`THB`。                                   |
| 56  | `price`           | Decimal String | 删除记录可为空 | 成人票面价，不含税费和服务费，固定两位小数。                                                  |
| 57  | `tax`             | Decimal String | 删除记录可为空 | 成人税费，固定两位小数。                                                            |
| 58  | `transaction_fee` | Decimal String | 删除记录可为空 | 每位乘客服务费，固定两位小数。                                                         |
| 59  | `infant_price`    | Decimal String | 是       | 婴儿票面价，固定两位小数；不支持婴儿票价时为空。                                                |
| 60  | `infant_tax`      | Decimal String | 是       | 婴儿税费，固定两位小数；不支持婴儿票价时为空。                                                 |
| 61  | `need_apis`       | `0` / `1`      | 否       | 是否需要 APIS / 旅行证件信息：`1` 需要，`0` 不需要。                                      |
| 62  | `deleted`         | `0` / `1`      | 否       | `0` 有效，`1` 已删除或失效。                                                      |
| 63  | `update_time`     | UTC ISO 8601   | 否       | 记录更新时间，例如 `2026-08-18T08:30:15Z`。                                       |

成人应付总价：`price + tax + transaction_fee`。

如支持婴儿票价，婴儿应付总价：`infant_price + infant_tax + transaction_fee`。

**数据行示例**

下列示例说明字段的填充方式。`_local_` 日期时间为航班当地时间，`update_time` 为 UTC；不存在的后续航段以空字段表示。实际文件必须始终包含完整的 63 列表头。

**单段直飞：** `atlas_flight_fare_id=217159401`，`segment_1_origin_airport=DUB`，`segment_1_date_local_takeoff=2027-03-23`，`segment_1_hours_local_takeoff=19:50`，`segment_1_destination_airport=ORY`，`segment_1_date_local_landing=2027-03-23`，`segment_1_hours_local_landing=22:45`，`segment_1_airline=TO`，`segment_1_flight_number=7909`；第 2、3 航段字段为空。行李为 `1-15-40.99`，余座为 `9`，币种为 `EUR`；若成人 `price=39.67`、`tax=12.00`、`transaction_fee=3.00`，则成人应付总价为 `54.67`。

**两段联程：** 第 1 航段可为 `DUS` → `AYT`（`XQ189`），第 2 航段为 `AYT` → `ADB`（`XQ7292`），第 3 航段字段为空。行李 `1-20-0` 表示 1 件、每件 20kg、免费；`0-0-0` 表示未提供任何行李信息。空航站楼、经停机场或运价品牌表示该信息未提供，解析时应按空串处理，而非自行补零或推断。

#### 处理新增、修改与删除

**全量文件与隐含删除**

* 全量文件是当前**有效价格记录**的完整快照。
* 全量文件中的记录均为 `deleted=0`。
* 客户应使用全量文件替换或重建本地价格基线。

全量文件采用快照语义：本地存在、但本次全量文件中不存在的记录，应在完成该全量文件处理后视为失效并删除或标记为不可售。

在 SEPARATE 模式下，为避免晚到的全量快照误删更晚到达的增量记录，请以全量文件名中的 UTC 生成时间为水位线：仅删除 `update_time` 早于该水位线、且不在全量文件中的本地记录；晚于水位线的记录应保留。SHARED 模式下可直接按全量快照重建本地基线。

**增量文件**

* 增量文件仅包含自上一次增量交付以来发生变化的记录。
* `deleted=0`：按 `atlas_flight_fare_id` 新增或更新。
* `deleted=1`：删除或将对应记录标记为失效。
* 删除记录的 `available_seats` 为 `0`，价格字段可能为空。
* 同一 ID 出现多次时，仅保留 `update_time` 最新的记录。

**客户处理原则**

1. 以 `atlas_flight_fare_id` 作为唯一键，不使用航班号、OD 或航段组合键。
2. 空值表示数据不可用或航段不存在；不要自动将空金额转为 `0`。
3. 使用 Decimal 类型处理金额，不使用浮点数计算。
4. 在 SEPARATE 模式下，对同一 ID 仅保留 `update_time` 最新的数据，避免乱序文件覆盖新数据。
5. 全量和增量可共用一套 CSV 解析逻辑。

对每条增量记录，可按以下顺序处理：以 `atlas_flight_fare_id` 查询本地记录；本地不存在且 `deleted=0` 时插入；本地已存在时仅在收到记录的 `update_time` 不早于本地值时更新；`deleted=1` 时将对应记录删除或标记为不可售。全量文件则应先完成记录校验和 upsert，再按 5.1 的快照规则处理本地未出现的旧记录。

**航司上线与下线**

* **航司上线**：新航司进入推送范围后，会在后续增量文件中补推其有效记录（`deleted=0`）；按普通新增或更新处理即可。
* **航司下线**：航司停飞或移出推送范围后，会在后续增量文件中下发其失效记录（`deleted=1`）；按删除或不可售处理即可。

客户仅需依赖 `atlas_flight_fare_id` 和 `deleted` 处理，无需额外通知接口。

### 第 4 步：测试验证

#### 完整接入流程

```mermaid
flowchart TD
    A[SFTP 拉取] --> B[解压 zip 或 gz]
    B --> C[CSV 解析 63 列]
    C --> D[按主键 upsert按 update_time 判新旧处理 deleted]
    D --> E{是否已有价格缓存或查询服务}
    E -->|已有| F1[字段映射并写入现有系统]
    E -->|没有| F2[存储建模、索引与查询服务开发]
    F1 --> G[业务系统查询航班价格]
    F2 --> G
```

#### 您需要检查

* 可连接 SFTP 并读取最终文件名的文件。
* 可解压 zip 或 gzip 文件。
* CSV 使用 UTF-8 无 BOM 解析，表头顺序正确且恰为 63 列。
* 能将解析后的数据正确加载到本地数据库、缓存或查询服务。
* 可按 `atlas_flight_fare_id` 正确新增、更新、删除。
* 可按 `update_time` 防止旧数据覆盖新数据。
* 可正确处理 `deleted=1`、空金额和缺失航段。
* 全量文件处理完成后，可正确移除快照中不存在的旧记录，并且不会误删晚于全量水位线的增量记录。
* 推送的价格和税费字段可与 Atlas 搜索结果核对一致。

#### 常见问题

| 问题          | 原因                  | 解决方案                                                                                             |
| ----------- | ------------------- | ------------------------------------------------------------------------------------------------ |
| SFTP 上找不到文件 | 目录路径错误或推送账号没有写入权限   | 核对全量/增量目录配置、目录存在性和写入权限。                                                                          |
| 解压失败        | 文件传输未完成或文件被截断       | 等待最终文件名出现后再处理；仅读取最终文件名。                                                                          |
| 列数不匹配       | CSV 解析器、编码或字段顺序配置错误 | 确保首行表头存在，按 UTF-8 无 BOM、逗号分隔及 63 列固定顺序解析。                                                         |
| 旧数据覆盖新数据    | 全量和增量乱序到达但未比较时间     | 对同一 ID 比较 `update_time`，只保留较新的记录。                                                                |
| 价格与页面不一致    | 未将税费或服务费纳入应付总价      | 成人总价按 `price + tax + transaction_fee` 计算；婴儿总价按 `infant_price + infant_tax + transaction_fee` 计算。 |

### 第 5 步：正式上线

#### 正式上线

验证通过后，请完成以下步骤：

1. 如测试与生产环境不同，切换至生产 SFTP 凭据。
2. 与 Atlas 客户经理确认生产环境的航司范围、目录、压缩格式、接入模式和推送频率。
3. 接收并处理首次全量文件，建立本地价格基线。
4. 持续处理增量文件；默认推送频率为每 2 分钟一次，可配置。
5. 建立文件缺失、空文件、列数异常和解析失败的告警。

#### 监控建议

* 超过两倍预期间隔未收到文件时告警。
* 记录每个文件的文件名、到达时间、行数、解析成功数和失败数。
* 监控列数、币种、金额格式和主键重复率。
* 保留原始文件及处理日志不少于 180 天，便于重放和排查。

### 接入难度与开发周期

#### 这件事难吗？

标准接入需要完成三项工作：

1. 提供可访问的 SFTP 服务器。
2. 编写定时任务扫描目录、解压并解析 CSV 文件。
3. 按主键和更新时间将数据写入本地数据库、缓存或查询服务。

数据接入本身并不复杂：SFTP 准备通常是运维工作，文件扫描、解压和 CSV 解析可用任意主流语言的标准库实现。需要重点设计的是用 `atlas_flight_fare_id` 做 upsert、按 `update_time` 判新旧，以及全量快照和 `deleted=1` 的失效处理。

| 条件       | 说明                                         | 难度 |
| -------- | ------------------------------------------ | -- |
| SFTP 服务器 | 可公网访问的 Linux 服务器，或复用已有 SFTP 服务             | 低  |
| 磁盘空间     | 需按航司范围和保留周期预留原始文件与解压文件空间；可按约 2 GB/天压缩文件量评估 | 中  |
| 定时任务     | cron 或同类机制，按增量间隔扫描目录                       | 低  |
| 数据存储     | 用于保存航班价格记录的数据库或缓存                          | 低  |

#### 开发周期估算

数据接入层（SFTP 拉取、CSV 解析、合并落库）通常需要 1–3 天。实际可查询的价格服务上线时间，还取决于客户是否已有价格数据存储和查询能力。

如果已有航班数据缓存或查询系统，主要工作是字段映射和写入适配；若从零建设，接入层之外还需进行存储选型、索引设计和查询服务开发。两种场景的数据接入流程相同，差异仅在数据落地和查询能力建设。

| 工作项              | 场景 A：已有缓存/查询服务 | 场景 B：从零建设      |
| ---------------- | -------------- | -------------- |
| SFTP 服务器准备       | 0.5 天          | 0.5 天          |
| 文件拉取、解压与 63 列解析  | 0.5 天          | 0.5 天          |
| upsert、快照重建与删除处理 | 1 天            | 1 天            |
| 存储选型与数据建模        | —              | 1–2 天          |
| 数据写入、索引和查询服务     | 0.5–1 天        | 2–3 天          |
| 数据校验、监控和联调       | 1–2 天          | 2–3 天          |
| **合计**           | **约 4–5 人天**   | **约 10–12 人天** |

* **场景 A**：已有航班数据缓存或查询系统时，主要完成字段映射、文件处理和写入适配。
* **场景 B**：从零开始时，除接入外还需完成数据建模、索引和查询服务建设。

#### 与 API 接入方式对比

|      | 数据推送        | API 实时调用          |
| ---- | ----------- | ----------------- |
| 最快上线 | 2–3 天       | 1–2 周             |
| 典型周期 | 4–5 人天      | 2–4 周             |
| 技术难度 | 低：文件处理和数据落库 | 中高：接口编排、状态管理和实时调用 |
