Files
msgexchange-v2/docs/implementation.md
T

464 lines
29 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 实现设计
msgexchange-v2 怎么处理报文:记录模型、状态机、事务边界、航班与静态参考数据合并规则。
对外术语见 [specification.md](specification.md)「术语」;本文保留管道内部说法(队头、终态、回填意图)。参数见 [reference.md](reference.md) `PARAM:<键>`;不变量见 `INV-x`/`C-x`;能否对外承诺见 specification「声明边界」。不写交付进度。
三块:**管道**(收报→主泵→写回→Kafka)、**航班域**PG + Redis 快照)、**静态参考数据**(`REF_MASTER`)。
## 1. 术语与持久化记录
### 1.1 管道术语
| 术语 | 语义 |
|---|---|
| 扫描谓词 | 读信箱时只取「处理时间为空」的行(`INV-1`);不用 ID 区间或水位当消费边界。 |
| 队头 | 编号最小的未完成消息(`PENDING``FAILED` 都占位)。 |
| 终态 | `SUCCEEDED` / `SKIPPED` / `DEAD`;到达后后面的消息才能处理。 |
| 回填 / 回填意图 | 写回处理时间的实现机制([specification.md](specification.md)「写回处理时间」)。`BACKFILL_*` 列记录进度;`BACKFILL_NEXT_AT` 非空 = 还欠一次写回。意图与终态同一条语句落库,且在 Redis 航班快照写成功之后(`INV-10`)。 |
| `R``R_keep`、处理标记 | 定义见 [specification.md](specification.md)。 |
### 1.2 持久化记录
| 记录 | 用途 | 关键约束 |
|---|---|---|
| `PROC_STATE` | 消息处理状态、身份、重试、错误、回填 | `MSG_ID` 主键;`IDENTITY_KEY` 唯一;最小未完成 `MSG_ID` = 队头。`BACKFILL_NEXT_AT` 非空 = 待写回;`BACKFILL_AT` 非空 = 已写回;`BACKFILL_ABANDONED_*` 非空 = 停自动重试(≠ 已写回)。`ENQUEUED_AT` 非空,超期只看它;`RECEIVED_AT` 可 NULL,仅对账。 |
| `MSG_EVENT` | 待发 Kafka 事件(outbox | `TARGET` = `KAFKA:msg` / `KAFKA:schd``msg``EVENT_ID` 稳定、保序。`schd`:按 `FLID` 单行 upsert,每次更新换 `EVENT_ID``SENT_AT` 与投递确认同事务写入,保留期从它起算。 |
| `REQ_TRACK` | 出站请求与应答 | 状态 `PENDING/SENT/DONE/EXPIRED/FAILED`。开放槽:`RQRD``RQFD` 各一条(`US-09` AC1),不是 14 类 `STYP` 各一条。`OMMS` 发送(`C-4`)。结案态关闭槽。 |
| `REF_MASTER` | SIS 消息提供的静态参考数据与资源状态的逻辑视图(物理为独立数据表组) | `(RTYPE, RKEY)` 唯一;`RTYPE` 类别、合并语义与资源状态见「静态参考数据」;取数路径见 [requirements.md](requirements.md) `US-13`。 |
| `FLIGHT_SCHD` | 航班标量及单值异常字段 | `FLID` 主键;运营日与版本、最近消息 ID 用于追踪。变长集合存于资源明细表与 `FLIGHT_ROUTE_POINT`,规则见「航班域」。 |
| `SCHD_SNAP_LOG` | 日计划处理留痕 | 只追加、可重建,不参与状态决策;保留期见 [reference.md](reference.md)。 |
字段与索引以 `src/main/resources/db/migration/` 为准。原文读共享信箱;清理见 `C-1`
## 2. 消息、身份与决策
`XmlCodec` 解码 XML → `DecodedMessage`(元数据 + `MsgKind` + 载荷)。`MALFORMED` 不重试;`CODEC_ERROR` 可退避重试。分派键:`Schd``Flop``Fdel``RefData``SIS:3.1``3.15`)、`Unsupported`
身份 `Identity.of` = `SNDR|TYPE|STYP|SEQN``C-3`)。入队只按信箱 ID 去重;解码后首次绑定,重试不改绑定。占用 → `SKIPPED(duplicate-of:<id>)`。日期边界见 `PARAM:msgx.identity.include-day-boundary`;上线后不可换算法。绑定是独立单语句(`IDENTITY_KEY IS NULL`),只认身份不比正文。
`MessageProcessor``MsgKind` 分派队头消息:
| `MsgKind` | 处理器 |
|---|---|
| SCHD-DNLD/RESP | `ScheduleProcessor` |
| SCHD-ADFT | `AdftProcessor` |
| FLOP | `FlopProcessor` |
| FDEL | `FdelProcessor` |
| RefData 3.13.14 / 3.15 配对 | `ReferenceDataProcessor` |
| 其余 | `SKIPPED(unsupported)` |
处理器在 `PIPELINE_LOCK` 内读态、算下一态与待发事件 → 提交 → 写 Redis → 另事务写终态与回填意图(`INV-3``INV-10`)。不直接发 Kafka。锁跨 Redis 写,覆盖历史清理协作(`US-14` AC4)。
不支持类型:跳过留档(`US-03` AC2)。`REGN`/`RSTA` 必须走 `US-13`,不得跳过。
## 3. 状态与错误分类
```text
处理:PENDING / FAILED → SUCCEEDED(成功)
→ SKIPPED(业务重复、忽略、不支持类型、无匹配)
→ FAILED(等待退避重试)
→ DEADMALFORMED / PROTOCOL / EXHAUSTED,均需人工处置)
投递:PENDING → SENT
→ PENDING(退避后重试)
→ DEAD(重试耗尽,记录保留作死信)
```
`SUCCEEDED`/`SKIPPED`/`DEAD` 是终态,不挡后续;`FAILED` 仍占队头。用尽重试 → `DEAD(EXHAUSTED)`。错误分类与重放白名单见 [reference.md](reference.md)。
## 4. 收报
### 4.1 收报
`InboxPoller` 周期扫描「处理时间为空」的行,升序、每批 `PARAM:msgx.pipeline.claim-batch` 条,在 PG 建 `PENDING` 记录(`INV-1`):
1. 信箱不可读 → 记日志,下轮再试(不是「无新消息」)。
2. 同事务 `insertIfAbsent(MSG_ID, RECEIVED_AT, ENQUEUED_AT)`;冲突 = 已有记录,跳过。
3. 已写回处理时间的行不再出现;终态未写回的行会重复扫到,幂等跳过。
写回延迟期间扫描量会涨,须观测积压([reference.md](reference.md) 指标)。
信箱 ID 单调不复用(`INV-1`);较小编号晚提交时最坏只是被发现得晚(下轮扫描仍会读到),不会丢,但它不会排在已经处理完的较大编号之前。`POST /cminmsgs/send` 与 adapter 同路径(`C-7`)。单活动实例(`OPS-1`);多实例靠主键幂等,仍须单实例排他。
## 5. 主泵调度与单条处理
### 5.1 调度
`Pump.tick` 只处理队头(最小未完成 `MSG_ID`):
1. 无队头 → 休眠。
2. `FAILED` 且次数用尽 → `DEAD(EXHAUSTED)`,同事 UPDATE 写终态与回填意图;只写 `PROC_STATE`,不取锁。
3. `FAILED` 且未到 `next_attempt_at` → 等到点,不跳队。
4. 其余 → `MessageProcessor.processOne`
不按时间推 `DEAD`;卡死靠外部有界超时。队头年龄见指标 `msgx.pipeline.backlog.oldest_unprocessed_seconds`。时间一律用注入 `Clock`
### 5.2 processOne
```text
processOne(head)
1. 读原文:缺失 → DEAD(MALFORMED, raw-missing);读取异常 → FAILED(INFRA)
2. 解码:
报文非法(MALFORMED) → DEAD(MALFORMED),不重试
可修复解码错(CODEC_ERROR) → FAILED(CODEC_ERROR) 退避
3. 身份绑定(仅当 IDENTITY_KEY 为空):
已被本消息占用 → 继续
已被别的消息占用 → SKIPPED(duplicate-of:<id>),结束
空闲 → 写入 IDENTITY_KEY(独立单语句,不参与业务事务)
4. 按 MsgKind 分派:
SCHD-DNLD / SCHD-RESP → ScheduleProcessor(快照事务)
SCHD-ADFT → AdftProcessor(单航班事务)
FLOP / FDEL → Flop / FdelProcessor(单航班事务)
RefDataSIS:3.13.14 落库;SIS:3.15 闭合 REQ_TRACK → ReferenceDataProcessor
Unsupported → SKIPPED(unsupported)(跳过留档,按已处理写回标记)
载荷缺失 → DEAD(MALFORMED)
整包协议拒绝 → DEAD(PROTOCOL),不落半包
5. 业务型成功,分三步(INV-3、INV-10):
① 事务提交:航班变更 + 待发事件
② 写 Redis 航班快照;失败 → 保持未完成,下轮重处理
③ 事务提交:SUCCEEDED + 回填意图
6. 结束:主泵不做回填;回填意图已随终态落库,由扫描补写信箱标记
```
不支持类型 ≠ 报文非法,走 `SKIPPED`。主泵不写回信箱;写回由扫描做。测试/影子实例禁写信箱。
### 5.3 事务边界
| 动作 | 显式事务 | 持 `PIPELINE_LOCK` | 触及航班表 / 事件 | 原子性来源 |
|---|---|---|---|---|
| 收报入队(`insertIfAbsent`) | 是 | 否 | 否 | 同库事务 |
| 身份首次绑定 | 否 | 否 | 否 | 单语句 + 唯一约束 |
| 业务型领域变更(航班变更 + 待发事件) | 是 | 是 | 是 | 同库事务(`INV-3` |
| Redis 航班快照写 | 否 | 是(处理步骤锁跨越本步) | 否 | 外部副作用,不在 PG 事务内;写成功是终态事务的前置(`INV-10` |
| 业务型终态(`SUCCEEDED` + 回填意图) | 是 | 是 | 否 | 同库事务(`INV-3` |
| 非业务型终态(`MALFORMED` / `PROTOCOL` / `SKIPPED` / `EXHAUSTED`) | 否 | 否 | 否 | 单语句(终态与回填意图同一条 UPDATE) |
| 航班历史清理的物理删除 | 是 | 是(落实 `US-14` AC4) | 是 | 同库事务:复查判据 + 历史写入成功后删除 |
| 回填(信箱标记 + `BACKFILL_AT`) | 否 | 否 | 否 | 跨库两次单写;幂等可重跑 |
| 人工重放(批量改回 `PENDING`) | 否 | 否 | 否 | 单语句批量;`MessageLifecycleGate` 与回填互斥 |
航班变更与终态不在同一事务,中间夹 Redis 写(`INV-3``INV-10`)。`PIPELINE_LOCK` 跨 Redis 写,与历史清理互斥(`US-14` AC4);无第二写者时不额外串行。
### 5.4 历史积压
积压走同一 FIFO 路径,无加速/旁路/跳队。退避与次数上限不变。跳过的行 `SKIPPED` 后走写回。完成时限不作承诺(`CLM-5`);指标见 [reference.md](reference.md)。
## 6. 回填
### 6.1 事实与扫描谓词
终态落库时记回填意图;写回由扫描驱动,跨库单写、幂等。扫描谓词:
```text
STATE ∈ {SUCCEEDED, SKIPPED, DEAD} -- 终态
AND BACKFILL_AT IS NULL -- 标记尚未确认
AND BACKFILL_ABANDONED_AT IS NULL -- 未放弃(放弃行可人工恢复)
AND ( BACKFILL_NEXT_AT IS NULL -- 异常兜底:终态行没有退避时间
OR BACKFILL_NEXT_AT ≤ NOW -- 退避到期
OR ENQUEUED_AT < NOW R ) -- 进入强补写窗口,覆盖退避
ORDER BY BACKFILL_ATTEMPTS ASC, MSG_ID ASC -- 公平轮转,永久失败行不占满批次
LIMIT PARAM:msgx.pipeline.backfill-batch
```
超期只看 `ENQUEUED_AT`,不用 `RECEIVED_AT`。信箱清理见 `C-1`
### 6.2 四种结果与放弃
| 结果 | 判定 | 处置 |
|---|---|---|
| 写入成功 | 标记为空、写入 1 行 | 记 `BACKFILL_AT`,不再重试 |
| 早已有标记 | 写入 0 行且信箱行存在 | **视为成功**,不覆盖已有值,记 `BACKFILL_AT` |
| 信箱行不存在 | 写入 0 行且行已删 | 立即放弃(`MISSING_ROW`)并告警 |
| 暂时性故障超 `R` | 超时/连接失败到 `R` 仍未写回 | 停自动重试(`TRANSIENT_DEADLINE`)并告警;`R` 前只退避 |
放弃 ≠ 已写回:不写 `BACKFILL_AT`,信箱行不删(`C-1`)。
### 6.3 `R`
`R` 见 [reference.md](reference.md) `PARAM:msgx.pipeline.overdue-backfill`
-`R` 未写回 → 每轮都试,不等退避。
- 暂时性故障到 `R` 仍失败 → 停自动重试;`backfill-max-attempts` 只告警。
- `MISSING_ROW` 立即放弃。`R ≤ R_keep`。写回已了结且超保留期(约 1 月,`C-1`)后删原文;放弃行不删。
## 7. 日计划快照与请求匹配
### 7.1 快照
`SCHD-DNLD`/`RESP` 共用 `ScheduleProcessor.applyScheduleRecords`
1. 已成功 → 幂等,只追加留痕。
2. 整包校验失败 → `DEAD(PROTOCOL)`,不写半包(`INV-4`)。
3. 分批写:跨运营日整包失败;**覆盖范围内**快照缺席航班标删、发删除事件、删 Redis,范围外的不受影响(`INV-7`)。每批同事务写变更 + 事件(`INV-3`)。
4. 整包成功 → `SUCCEEDED` + 回填意图;留痕在事务外。
字段语义见「航班域」。`RESP` 须匹配开放请求,否则不更新(`G-RESP-GUARD`)。
### 7.2 上游请求与静态数据
请求状态机(`G-REQ-TRACK`):
```text
PENDING → SENT → DONE
├──→ EXPIRED
└──→ FAILED(收到 ERORUS-09 AC3
```
- 开放槽:`RQRD`/`RQFD` 各一条(`US-09` AC1);同类新请求前作旧请求过期。
- `COUTMSGS` 确认后才 `SENT`;本地未确认须补偿,不能盲重发。
- EROR → 标 `FAILED` 并告警(`US-09` AC3)。
- 应答按类型匹配开放请求(`US-09` AC2);时间用本地时钟。
- 参考应答写 `REF_MASTER`;日计划走快照。结案后迟到应答不改状态。
- 出站只保证写入信箱(`C-4``CLM-4`)。
## 8. 事件投递
### 8.1 `KAFKA:msg`
`FLID` 内按 `EVENT_ID` 保序;跨 `FLID` 不承诺顺序。消费端用 `(FLID, STATE_VERSION, UPDATED_AT)` 防旧盖新。
每批在读取时冻结 `EVENT_ID` 集合。`EVENT_ID` 全局串行分配,顺序 = 提交顺序(`US-14` AC4)。
确认发送后标 `SENT`;失败退避,用尽 → 死信。外部调用须有界超时。
至少一次投递(`D2`);配置见 [reference.md](reference.md)「信箱与外部依赖」。
### 8.2 `KAFKA:schd`
`FLID` 单行 upsert,只保留最新 `STATE_VERSION`。每次更新换 `EVENT_ID`(写代次)。
- **只进不退**`STATE_VERSION` 只升不降。
- **条件标记**`SENT``WHERE STATE_VERSION = 本批版本`;已被新版本覆盖则下轮重发。
发送:取未发送行(`PARAM:msgx.schd.flush-limit`)→ 整批 `SCHD.FLTR` JSON`C-9`)→ 条件标 `SENT`。删航班只走 `msg`
周期与批上限见 [reference.md](reference.md)。`msg``schd` 不承诺顺序。
### 8.3 事件清理
`SENT` 行按 `PARAM:msgx.pipeline.event-retention` 清理。`msg` 死信须人工后再删;`schd` 死信在新代次出现时重置为 `PENDING`
## 9. 失败恢复与维护作业
### 9.1 失败、重试与重放
`ProcFailure`/`FailureScheduler` 统一失败落账与退避。启动自检:退避档位数须比尝试上限少一。失败在具体消息/事件/批次处记数;外层不重复加次数。
`ReplayService` 白名单内可从 `FAILED`/`DEAD``PENDING`,重置次数与时间,**不改 `IDENTITY_KEY`**。与写回互斥(`MessageLifecycleGate`)。重放不恢复历史顺序(`CLM-3`)。
### 9.2 中断恢复
恢复只看持久化记录。PG 备份回退可能重复发 Kafka/写回,接受至少一次,不重放业务。`SCHD_SNAP_LOG` 在事务外,崩溃可丢。
| 中断位置 | 重启后的判定 | 恢复动作 |
|---|---|---|
| 已写入信箱、未入队 | 信箱行处理时间为空且 PG 无记录 | 重扫补建处理记录 |
| 事务执行中 | PG 无该消息终态 | 事务整体回滚,按 `PENDING` 重新处理 |
| 领域事务已提交、Redis 写失败或终态未提交 | 该消息无终态(`PENDING`),仍占队头 | 整条消息重处理:Redis 航班快照按当前完整态重写;领域再跑是否只留一次效果见 `US-03``G-FLOP-IDEMPOTENT`;已提交结果不回滚(`US-03` AC3 |
| 事务已提交、标记未写 | 终态行仍持有回填意图 | 仅补写标记;业务处理结果保持不变 |
| 标记写入中途 | 标记仍为空 | 重新写入;重复写入同一值无副作用 |
| 回填时信箱行已不存在 | 写入 0 行且信箱行不存在 | 立即放弃自动重试(`MISSING_ROW`)并告警;放弃不等于标记已确认,仍需人工对账 |
| `RECEIVED_AT` 为 NULL | 超期分支以本地 `ENQUEUED_AT` 判定,不受库方时钟与 NULL 影响 | 按 `R` 超期强补写;未超期则按退避重试 |
| 投递目标已接受、`SENT` 未置 | 事件仍 `PENDING` | 允许重发,消费方按事件身份去重 |
| PG 从备份恢复 | 终态与事件回退到备份点 | 按至少一次接受重复;不重放业务、不据此改写航班 |
### 9.3 生命周期与清除
`JobRunner` 周期跑写回扫描、历史清理、留痕清理;不挡主泵 FIFO。历史清理跳过处理中的航班(`US-14` AC4)。
清除须终局证据(航班 `US-14`/`D1`;共享信箱的清除属库方职责,见 `C-1`)。证据不明 → 删 0 条。写回失败记录在其覆盖的信箱行被清除前可查(`US-10` AC2)。
**逐对象**(保留期见 [reference.md](reference.md)
| 对象 | 终局判据 | 归档目标 | 清除证据 | 执行方 | 偏差 |
|---|---|---|---|---|---|
| 共享信箱 `CMINMSGS` 原文 | 写回完成(`BACKFILL_AT` 非空) | — | 待确认(`Q9` | 库方 | 清除协议未确认(`C-1` |
| `FLIGHT_SCHD` + 资源明细 | `US-14` AC2 的已结束条件 | 历史存储 | 历史写入确认 + 版本复查 | 我们 | — |
| 航班历史存储 | 保留期 | — | — | 我们 | `G-FLIGHT-HIST-RETENTION` |
| `SCHD_SNAP_LOG` | 保留期 | 无(本地可重建) | 无 | 我们 | — |
| `MSG_EVENT` 已发送行 | `SENT` | 无 | 无 | 我们 | — |
| `PROC_STATE` 终态行 | 见下 | 无(到期直接删除) | 回填了结 | 我们 | `G-PROC-CLEANUP` |
| `REQ_TRACK` 关闭态行 | 保留期 | 无 | 无 | 我们 | `G-REQ-TRACK-RETENTION` |
**`PROC_STATE` 清理**`US-11`):终态 + `BACKFILL_AT` 非空 + 超保留期(从 `UPDATED_AT` 起)。删除按 `STATE` 条件执行,0 行则跳过(与人工重放互斥)。批量删除不持 `PIPELINE_LOCK`
保留期从终局后起算,未写回不进候选。其余:历史清理先写 ES 再删(「航班域」);留痕按 `(SCOPE_END, RECV_AT)`;事件见「事件清理」。信箱见 `C-1`/`C-2`
## 10. 容量假设
单机场、单实例;日消息千~万级;单条报文按 CIIMS 支持的 10 MB 上限、不分片设计(`SIS:3.16`),读取、解码与回归样例都以该上限为准;延迟秒级可接受。不引入多实例/分布式锁/分区表。参数依据见 [reference.md](reference.md)`CLM-6`)。
## 11. 航班域:数据模型与合并
PG 是航班数据源(`INV-5`);Redis 从 PG 同步,处理完成前写入(`INV-10`)。`FLID` 全局唯一。写入与对账只看 `FLIGHT_SCHD` 及明细;Redis 只读。
### 11.1 数据模型
| 对象 | 职责 |
|---|---|
| `FLIGHT_SCHD` | 一行一个 `FLID`,保存标量字段、`STATE``STATE_VERSION``OPERATION_DAY`、最近消息 ID 和审计时间。 |
| 资源明细表 | 保存登机门、值机柜台、转盘、计划机位、滑槽、延误、靠撤桥、轮挡等变长集合;`SRVT`/`VIPF` 专用明细见 `G-SRVT-VIPF`。主键为 `(FLID, ORDINAL)`。 |
| `FLIGHT_ROUTE_POINT` | ROUT 与 ERUT 两类路线点,使用 `ROUTE_KIND` 区分;主键应包含该列,避免两类路线的序号冲突。 |
| `PROC_STATE` | 信箱消息的处理终态、业务身份幂等记录,以及回填事实(`RECEIVED_AT` / `BACKFILL_*`)。 |
| `MSG_EVENT` | 事务 outbox,承载整态快照、变更通知和删除事件(tombstone)。 |
| `SCHD_SNAP_LOG` | 日计划处理留痕,只追加、可重建,不参与状态决策。 |
### 11.2 航班身份与运营日
`OPERATION_DAY``SODT` + 机场时区/切日规则推导,不是接收日。未收录可为 `NULL`。跨运营日冲突 → `DEAD(PROTOCOL)`,整包不写(`INV-4`)。
### 11.3 字段与集合
三层:主表标量、集合明细、路线点。键名取 SIS 标签;语义见 [SIS 规范](legacy/SIS_AODB_RMS-V0.1.md) 与 [XSD](legacy/unisysaodbsis.xsd)。
**标量**(存于 `FLIGHT_SCHD` 主行,出现即覆盖;空串为显式清空)
| 组 | 标签 |
|---|---|
| 身份与计划 | `ALCD``ALSC``FLNO``MVIN``SODT``FLTY``FLIN``ACFT``RENO` |
| 班次与运营 | `TAOP``TAFL``TAID``TRML``CSOP``CSFT``MAID` |
| 时间 | `ESTT``ACTT``BOTM``LACL``FINT``APPT``EGSR``EGST``PADT``NAAT``NEAT``PEDT``FTSS` |
| 资源与状态 | `STND``PHAG``FHAG``MHAG``CNCL``REMC``MAXP``VIPP``VIPR``LBNO``LBWT``PAXC``EXSC``EXSR` |
**集合**(存于明细表;报文带了该集合就按完整合并结果整体替换,元素顺序取报文顺序;未出现则保留本地值)
| 集合 | 元素键(属性在前) | 条数上限 | 定义处 |
|---|---|---|---|
| `GTDT` | `GTNO``GATE``PGOT``PGCT``GOTM``GCTM``GTYP` | 99 | `SIS:3.34` |
| `CKDT` | `CKNO``CHKC``CCLS``PCOT``PCCT``COTM``CCTM``CTYP` | 99 | `SIS:3.24` |
| `CLDT` | `CLNO``BELT``BCLS``PCOT``PCCT``FBAG``LBAG``BTYP` | 99 | `SIS:3.25` |
| `CHDT` | `CHNO``CHUT``CCLS``PCBT``PCET``CBTM``CETM``CTYP` | 99 | `SIS:3.26` |
| `PSDT` | `PSNO``PSST``STST``STET` | 9 | `SIS:3.38` |
| `CHOT` | `CSNO``CHTM``CHID``CHST` | 99 | `SIS:3.22` |
| `DELY` | `CODE``STRT``DURA`、文本 | — | [XSD](legacy/unisysaodbsis.xsd)「FLOP 元素」 |
| `ABTM` | `ASNO``ABDG``ABOP``AOTM` | 99 | [XSD](legacy/unisysaodbsis.xsd)「FLOP 元素」 |
| `ROUT` / `ERUT` | `RTNO``APCD``SCAT``SCDT` | SIS 报文容量 6 / 7;本地各自最多保留 4 条(`US-05` AC3 | `SIS:3.40` |
| `SRVT` | `OPER``SRTC``SRQT``SRST``SRET``SRPR``SANR``SARR` | 无界 | `G-SRVT-VIPF` |
| `VIPF` | `OPER``VPCD``VFES``VIPT/OPER``VIPT/VSCD``VIPT/VTQY``VIPT/VTST``VIPT/VTET` | 无界 | `G-SRVT-VIPF` |
`SRVT`/`VIPF` 只存原文,不参与合并。`MAFL` 由子航班派生(`G-MAFL`)。
- `ORDINAL` 是持久化顺序,从 1 开始;`SOURCE_SEQ` 是上游序号,允许为空或重复。
- 相同资源号不代表同一条分配,禁止按资源号去重。
- 每次持久化完整航班状态时,明细表按该 `FLID` 先删后插,以完整合并结果为准。
- ROUT 与 ERUT 是两类独立集合,不能因相同序号覆盖彼此。
- `CHDT` 的类字段固定为 `CCLS`/`CTYP`;当前 wire DTO 与持久化列误写成 `CHCLS`/`CHTYP`,见 `G-FLOP-UNMAPPED`
- 主/共享:`MAID` 指向主航班 `FLID``MAFL` 从子航班派生,不入站。
### 11.4 `MAFL`
主航班派生集合:`{FLID, FLNO}``US-06` AC2)。仅主航班(`MAID` 空)带 `MAFL`;共享航班不带。已删子航班自动退出。子航班变更须通知主航班(`msg`+`schd`)。同事务写入;`MAID` 须索引。
## 12. 航班域:合并、删除与生命周期
### 12.1 SCHD
整包校验通过后分批写入;**覆盖范围内**快照缺席标删,范围外的不受影响(`INV-7`)。未带字段清空(`C-6`)。成功航班推进 `STATE_VERSION` 并写 `schd`+`msg` 事件。重复由 `PROC_STATE` 控制;校验失败整包不写(`INV-4`)。
### 12.2 动态运行事件(FLOP
只改报文带的字段/集合(见「字段与集合」)。`STYP` 须命中白名单;未知 → `SKIPPED``US-03` AC2)。逐类规则见 `G-FLOP-IDEMPOTENT`
**现行白名单 7 类**`US-05` AC1):`ABTM``DELY``PSDT``CKDT``CLDT``CHDT``GTDT`。下表其余行是闭合目标(`Q3`),不是现行白名单。
| SIS | STYP | 目标 | 空标签 / 缺失语义 |
|---|---|---|---|
| XSD FLOP | `ABTM` | `ABTM` 集合 | 现行基线须处理(`US-05` AC1);形态与逐类终态待 `Q3` |
| XSD FLOP | `DELY` | `DELY` 集合 | 现行基线须处理(`US-05` AC1);形态与逐类终态待 `Q3` |
| `SIS:3.19` | `ACFT` | `ACFT` 标量 | 字段为 M,未写明空语义 |
| `SIS:3.20` | `ACTT` | `ACTT` 标量 | 空 = 清除已有实际时间 |
| `SIS:3.21` | `BOTM` | `BOTM` 标量;非空时设置登机状态 | 空 = 清除登机时间并重置登机状态 |
| `SIS:3.22` | `CHOT` | `CHOT` 集合 | 集合按完整结果替换;元素内 `CHTM` 空 = 删除该轮挡时间 |
| `SIS:3.23` | `FLTY` | `FLTY` 标量 | 字段为 M,未写明空语义 |
| `SIS:3.24` | `CKDT` | `CKDT` 集合 | 空 `<CKDT CKNO="0">` = 现无柜台分配;AODB 实际会发,照常接收处理(`US-05` AC3 |
| `SIS:3.25` | `CLDT` | `CLDT` 集合 | 空 `<CLDT CLNO="0">` = 现无转盘分配;AODB 实际会发,照常接收处理(`US-05` AC3 |
| `SIS:3.26` | `CHDT` | `CHDT` 集合 | 空 `<CHDT CHNO="0">` = 现无滑槽分配;AODB 实际会发,照常接收处理(`US-05` AC3 |
| `SIS:3.27` | `CNCL` | `CNCL` 标量 | 空 = **撤销**上一次取消(不是清空时间) |
| `SIS:3.28` | `ESTT` | `ESTT` 标量 | 空 = 删除预计时间 |
| `SIS:3.29` | `FDEL` | 走删除路径(「删除与重建」) | 恒为空标签 |
| `SIS:3.30` | `FDIV` | `FDIV``DDES`/`DDIR`),当前未映射(`G-FLOP-UNMAPPED` | 空 `DDES=""` = 清除转场标志 |
| `SIS:3.31` | `FINT` | `FINT` 标量 | 空 = 删除最终时间 |
| `SIS:3.32` | `FLIN` | `FLIN` 标量 | 字段为 M,未写明空语义 |
| `SIS:3.33` | `FRET` | `FRET``REID`),当前未映射(`G-FLOP-UNMAPPED` | 空 = 清除返航标志 |
| `SIS:3.34` | `GTDT` | `GTDT` 集合 | 空 `<GTDT GTNO="0">` = 现无登机门分配;`GOTM`/`GCTM` 缺失 = 未分配开放/关闭时间;AODB 实际会发,照常接收处理(`US-05` AC3 |
| `SIS:3.35` | `LACL` | `LACL` 标量;非空时设置最后通知状态 | 空 = 清除最后通知时间并重置最后通知状态 |
| `SIS:3.36` | `MAXP` | `MAXP` 标量 | 空 = 删除最大载客数 |
| `SIS:3.37` | `HNAG` | `FHAG`/`PHAG`/`MHAG` 标量 | `FHAG` 空 = 删除该代理;`MHAG` 可缺席 |
| `SIS:3.38` | `PSDT` | `PSDT` 集合 | 空 `<PSDT PSNO="0">` = 现无机位分配;AODB 实际会发,照常接收处理(`US-05` AC3 |
| `SIS:3.39` | `RENO` | `RENO` 标量 | 空 = 清除注册号 |
| `SIS:3.40` | `ROUT` | `ROUT` 集合 | `SCAT`/`SCDT` 分别对起点/终点缺席。SIS 报文容量 6/7;本地 `ROUT`/`ERUT` **各自最多保留 4 条**`US-05` AC3),不保存 `SCAT`/`SCDT` |
| `SIS:3.41` | `TAOP` | `TAOP`/`TAFL`/`TAID` 标量 | 任一为空 = 该到达航班的经停连接断开 |
| `SIS:3.42` | `TRML` | `TRML` 标量 | 空 = 删除航站楼 |
| `SIS:3.43` | `VIPP` | `VIPP`/`VIPR` 标量 | 空 = 删除;SIS 另要求 RMS 忽略 `VIPP`(忽略事件还是忽略字段,SIS 未写明,须以真实报文确认,见 `G-FLOP-SEMANTICS` |
**legacy 有、SIS 无**`PADT``FTSS``STND``BDPB``REMC`(形态待 `Q3`)。**SIS 有、legacy 无**`CHDT``FINT``MAXP`。**未映射**`G-FLOP-UNMAPPED`。不改 `OPERATION_DAY`;目标航班缺失时的终态待 `Q3`。实现偏差见 `G-FLOP-SEMANTICS`
### 12.3 删除与重建
FDEL`ACTIVE→DELETED`,写 tombstone。物理删除仅历史清理成功后(`US-14``D1`)。覆盖范围内快照缺席也标删(`INV-7`)。
ADFTSet-only`US-04` AC2),未带字段不清。有 `SODT` 则算 `OPERATION_DAY`
主/共享:删共享 → 重算主航班 `MAFL` 并通知;删主 → 级联删子并通知。须原子,按 `FLID` 值比较。
### 12.4 Kafka 与读取
`schd` = 整态快照;`msg` = 变更通知。不承诺 topic 间顺序。删航班只走 `msg``C-9`)。读航班须同事务/一致读边界批量加载。
### 12.5 生命周期
运营日过去 ≠ 航班结束。历史清理候选按 `US-14` AC2 的五类已结束条件选取;先写 ES 再删,失败删 0 行(`D1`)。当前配置的判据与窗口尚未对齐该验收,见 [reference.md](reference.md) 依据列标「与需求冲突」的四项。
## 13. 静态参考数据
静态参考数据:类别、结构、合并语义。字段语义见 [SIS 规范](legacy/SIS_AODB_RMS-V0.1.md);验收见 `US-13`
### 13.1 来源
| 来源 | SIS 节 | 形态 |
|---|---|---|
| AODB 参考数据事件 | `SIS:3.1``SIS:3.13` | `META.TYPE` 即类别码;`STYP``DNLD` / `RESP` / `ADD` / `UPD` / `DEL` |
| AODB 资源状态事件 | `SIS:3.14` | `TYPE=RSTA``DNLD` 是单条状态更新,`RESP` 才可包含多条 |
| AODB 应答参考数据请求 | `SIS:3.15` | 复用对应类别格式且 `STYP=RESP`;**没有请求 ID 或关联字段**,以响应 `TYPE` 匹配原请求 `STYP` |
### 13.2 类别
| `RTYPE` | 含义 | 唯一键 `RKEY` | SIS 节 |
|---|---|---|---|
| `COUL` | 国家代码 | `COUC` | `SIS:3.1` |
| `ARPT` | 机场代码 | `ITCD` | `SIS:3.2` |
| `AIRL` | 航空公司代码 | `ITOP` | `SIS:3.3` |
| `AIRC` | 机型代码 | `ITAT` | `SIS:3.4` |
| `REGN` | 注册号 | `RNUM` | `SIS:3.5` |
| `ORGN` | 机构代码 | `OGID` | `SIS:3.6` |
| `FLTL` | 航班类型代码 | `FTYP` | `SIS:3.7` |
| `TLST` | 航站楼代码 | `TCOD` | `SIS:3.8` |
| `GLST` | 登机门代码 | `GCOD` | `SIS:3.9` |
| `SLST` | 机位代码 | `SCOD` | `SIS:3.10` |
| `CLST` | 值机柜台代码 | `CCOD` | `SIS:3.11` |
| `BLST` | 行李转盘代码 | `BCOD` | `SIS:3.12` |
| `CHLT` | 行李滑槽代码 | `CCOD` | `SIS:3.13` |
| `RSTA` | 资源状态 | (`RTYP`, `RSID`) | `SIS:3.14` |
`CLST``CHLT` 复用同名标签 `CCOD``CNMC``CTML``CCAT`,语义不同,只能靠 `RTYPE` 区分。
### 13.3 结构与合并语义
逻辑视图 `REF_MASTER`,键 `(RTYPE, RKEY)``RSTA``RTYP`/`RSID`。物理为独立表组。
- 13 类:`DNLD`/`RESP` 全量替换(同 `RTYPE`);`ADD`/`UPD`/`DEL` 单条增量。
- `RSTA`:单条覆盖或 RESP 多条;不以「包内未出现」删其他状态。
- 删除只认 `STYP=DEL`;空标签 = 不可用,不是删(与航班动态相反)。
- 应答 `STYP=RESP`,按 `TYPE` 配对 `RQRD.STYP`
- 不进 `MSG_EVENT`,不参与航班推进。
SIS 声明的上游忽略与截断口径见 `SIS:3.1``SIS:3.2``SIS:3.4``SIS:3.5``SIS:3.6``SIS:3.7``SIS:3.8``SIS:3.10``SIS:3.11``SIS:3.12`。未确认本地用途前不入模型;`US-13` 增补须说明用途。
### 13.4 资源状态
`RSTA``RTYP``BELT`/`CNTR`/`GATE`/`STND``STAT` = `E`/`D`。默认可用;禁用可带时间窗,到期自动恢复;无结束时间 = 一直禁用至新事件。
### 13.5 admin-api 下游读取边界
SIS → 本网关 → PG → admin-api`C-10`)。本网关不调用 admin-api。Oracle 待 `Q14` 验证。MySQL 只是信箱。