Files
msgexchange-v2/docs/flight-state.md
T

300 lines
17 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.
# 航班运行数据接入与当前状态管理设计
本模块从共享 MySQL 信箱接收 AODB 报文,在本地库维护航班当前态,再通过 Kafka 发布变化。
未标注内容即规范要求;开放与待定项汇总在 §10。
## 1. 目标与边界
1. **当前态唯一**:AODB 是业务事实来源,本地库是本模块唯一查询源。
2. **严格有序**:单活动主泵按信箱 FIFO 处理消息。
3. **原子提交**:航班状态、处理结果、待发事件在同一事务提交。
4. **可恢复**:信箱回填与 Kafka 投递可独立重试,不重算业务。
5. **完整保存**:变长资源用明细表,不用固定槽位截断。
边界:只同步报文、维护当前态,不推导取消、延误、备降等业务状态。`PIPELINE_LOCK` 只串行化数据库事务,不能代替消息认领、选主与故障切换。
## 2. 数据来源与语义
| 来源 | 作用 |
|---|---|
| SCHD `DNLD`/`RESP` | 按 `FLID` upsert 完整航班基准状态,字段完整替换(§5);不做名单比对或差删 |
| SCHD ADFT | 新增或更新单个临时航班(§2.1) |
| FLOP | 更新单个航班运行变化,增量合并,不改运营日 |
| FDEL | 终止航班实例,标记删除(§6.2) |
| 历史任务 | 归档已结束航班并物理清除(§8) |
字段缺失两种含义:
- 完整快照:未出现 = AODB 已无该数据,**清除本地旧值**。
- 增量(FLOP):未出现 = 本次没改,**保留本地值**。
### 2.1 ADFT 语义待确认
ADFT 的字段缺失语义尚需按 SIS 条款及真实报文确认,**确认前不得直接沿用 FLOP 的增量合并规则**。若 ADFT 含足以确定运营日的 `SODT`,应据此设置 `OPERATION_DAY`,不得默认留 `NULL`
## 3. 数据模型
### 3.1 航班实例身份
- **`FLID`**AODB 分配的实例 ID`Number(1-12)`,SIS §3.16.2),全部表的唯一关联键。
- **`FLNO`**:展示用航班号,可重复,不参与身份判定。
- **`OPERATION_DAY`**:所属运营日,一经确定不可变(§3.5、§5.3)。
- **`STATE`**`ACTIVE` / `DELETED`。所有删除先标记;物理清除只发生在历史归档成功之后,归档结果记录在历史存储,不在当前态保留 `ARCHIVED` 状态。
同一航班号不同运营日 = 不同 `FLID`
身份属性变更由 AODB 通过 FDEL 后续接 ADFT 表达。**两条消息的 `FLID` 可能相同,也可能不同**,本模块不得预设一定产生新 `FLID`
- `FLID` 相同:按同一标识下的生命周期重激活处理(FDEL 置 `DELETED`ADFT 恢复 `ACTIVE`);
- `FLID` 不同:按旧实例终止、新实例建立处理。
本模块不得合并实例,也不得由航班号或日期推断两个 `FLID` 是同一实例。
### 3.2 表职责
| 表 | 粒度 | 职责 |
|---|---|---|
| `FLIGHT_SCHD` | 每 `FLID` 一行 | 标量当前态、`OPERATION_DAY``STATE``STATE_VERSION`、最近消息 ID |
| 8 张资源明细表 | 每 `FLID` 多行 | 登机门、值机柜台、行李转盘、计划机位、滑槽、延误、靠撤桥、轮挡 |
| `FLIGHT_ROUTE_POINT` | 每 `FLID` 多行 | ROUT/ERUT 路线点,`ROUTE_KIND` 区分 |
| `PIPELINE_LOCK` | 单行 | 串行化状态写事务 |
| `PROC_STATE` | 每消息一行 | 处理状态与重试结果;兼作快照重放判定(§5.1) |
| `MSG_EVENT`(outbox) | 每事件一行 | 状态、变更、删除通知 |
| `REQ_TRACK` / `COUTMSGS` | 每请求一行 | 请求状态与出站 |
| `BACKFILL_TODO` | 每待补偿一行 | 共享信箱回填任务 |
| `SCHD_SNAP_LOG` | 每快照一行 | 只追加留痕,不参与决策(§5.5) |
明细表共 9 张、承载 10 类集合(ROUT 与 ERUT 共用路线表);`ORDINAL` 保留输入顺序,`SOURCE_SEQ` 只存上游序号。
分层:**决策层**`FLIGHT_SCHD` 与各运行表)承担正确性;**留痕层**(`SCHD_SNAP_LOG`)只追加、可重建;**证据层**(报文归档)冷路径、仅用于重演审计,**尚未交付**。
### 3.3 完整当前态
主行 + 全部明细 = 完整当前态。写入前在内存生成完整新状态再落库,明细集合按组先删后插。展示视图是投影,不是权威。
### 3.4 代码共享与过站
- 主航班与共享航班是独立实例,各有 `FLID`;共享航班用 `CSOP`/`CSFT` 指向主航班,`MAFL` 存共享列表。
- `TAOP`/`TAFL`/`TAID` 是过站关联,不是代码共享。
- 删除时共享航班 FDEL 先于主航班;按 `FLID` 各自处理,不做级联推断。
### 3.5 运营日
`OPERATION_DAY` 是运行保障日期,不是接收日或落库日。
**计算规则**:按机场运营日规则,由计划运行时间字段(`SODT`)与机场时区(Asia/Shanghai)计算;切日边界由业务配置,不得假设等于接收日期或自然日零点。单日请求范围到运营日的映射、多日窗口中每条记录的归日,均按此规则执行。
快照取覆盖日;FLOP 保留当前值;ADFT 或先于快照的 FLOP 暂为 `NULL`,被快照收录时补齐(ADFT 含 `SODT` 时直接计算,见 §2.1)。
延误跨日仍是同一 `FLID`、运营日不变——SIS §3.16 注释 1 明确日计划不含前日延误航班,保留与清理由本模块负责。
## 4. 更新模型
`FLID` 为唯一边界:
```text
SCHD → 完整基准(字段替换,upsert;不改变 DELETED 状态)
FLOP/ADFT → 增量合并
FDEL → 标记删除(DELETED
ADFT → 生命周期重激活(同 FLID)或新实例建立(不同 FLID)
历史任务 → 归档后物理清除(内部清理,不发业务删除事件)
```
- 每次成功写入推进 `STATE_VERSION`;状态、事件、处理终态同事务提交。
- 主链路无集合删除;删除入口只有 FDEL 与历史归档,均先标记后清除。
- **普通 SCHD 不承担恢复语义**:`DELETED` 航班被快照收录时保持 `DELETED` 并记录冲突告警(§5.1);恢复入口只有 ADFT(§6.3)。
## 5. SCHD 处理
设消息 `M`,记录集 `R`。身份前提:同一 `FLID` 属于唯一运营日;`FLID` 不跨日迁移。
主链路入口为 `applyScheduleRecords`,对单日快照与滚动窗口统一适用;对单日快照不做任何名单层面的处理。
### 5.1 applyScheduleRecords(同一事务)
1. 锁定 `PIPELINE_LOCK`
2. `M``PROC_STATE` 已有成功终态 → 重放,直接记幂等成功。
3. 校验报文完整性(§5.2),任一失败整包不落地。
4. 对每条记录按 §3.5 确定其 `OPERATION_DAY`
5. 校验每个 `FLID` 的既有 `OPERATION_DAY`(§5.3)。
6. 按完整快照语义 upsert 每个航班(§5.4);**`DELETED` 航班保持 `DELETED`,记录冲突告警 `SCHD_REVIVE_CONFLICT`,不恢复 `ACTIVE`**。
7. 逐航班推进 `STATE_VERSION`,登记状态事件与处理终态。
任一步失败整体回滚。
### 5.2 报文完整性
五项校验:`RECS` 09999 且等于实收 `FLTR` 数;每条记录含合法数字型 `FLID`;快照内不重复;每条记录的运营日可计算且在报文覆盖范围内。
**记录完整与成员完整必须分开**
- **记录完整**:某 `FLID` 的字段是该航班的完整状态 → 允许覆盖字段。48 小时滚动窗口满足此维度。
- **成员完整**:报文含某运营日全部 `FLID`。主链路不使用成员完整性;若将来引入名单对账,需另行设计并单独定案。
### 5.3 航班归属
| 主行情况 | 处理 |
|---|---|
| 不存在 | 创建,`OPERATION_DAY` 按记录计算,`STATE = ACTIVE` |
| `NULL` | 首次确定 |
| 与记录计算值相同 | 按快照更新状态 |
| 与记录计算值不同 | 违反身份约束:不改写、整包拒绝 `DEAD(PROTOCOL)`、告警 `SAME_FLID_ACROSS_OPERATION_DAYS` |
第四行意味着串日、错发或数据污染:不静默保留,不迁移归属,整包拒绝后交人工确认。
### 5.4 字段语义
| 输入 | 字段缺失含义 |
|---|---|
| `DNLD`/`RESP` | AODB 已无该数据 → 清除本地旧值 |
| `FLOP` | 本次没改 → 保留本地值 |
| `KAFKA_SCHD` | 当前态没有该字段 → 消费者删除旧值 |
标量出现 Set、缺失 Clear;集合出现 Replace、缺失 Replace 空集。清除只覆盖正式映射的字段。
### 5.5 时序约束与留痕
**快照与 FLOP 的覆盖顺序是核心时序问题。** `SEQN` 重启后不可比、不表达业务新旧,因此**不启用陈旧拦截**,只承诺**按信箱接收顺序形成当前态**。“快照比 FLOP 权威”不成立,不得对外承诺。较旧的 SCHD 晚于 FLOP 到达可能把新动态改回旧值——接收序模型的已知代价,靠上游顺序保证或下一次 FLOP 纠正。`SEQN` 仅用于同会话顺序观测(下降记 `SEQN_REGRESSION` 并告警)。
事务外追加一条 `SCHD_SNAP_LOG`
| 字段 | 说明 |
|---|---|
| `MSG_ID` / `RECV_AT` | 消息 ID、接收时刻(UTC) |
| `SCOPE_START` / `SCOPE_END` | 报文覆盖的运营日范围(单日时两者相等) |
| `RECS` / `UPSERTED` / `DURATION_MS` | 规模、写入数、耗时 |
| `RESULT` | `COMMITTED` / `REPLAY_SKIPPED` / `ROLLED_BACK` |
| `FLAGS` | `EMPTY` / `RECS_DROP` / `SEQN_REGRESSION` / `DAY_MISMATCH` / `SCHD_REVIVE_CONFLICT` |
| `ARCHIVE_KEY` | 原文归档引用 |
规则:`RESULT``FLAGS` 分列(可“成功且告警”);一行 = 一次尝试,重放也记;留痕不参与决策,写失败只记指标;保留 90 天,按 `(SCOPE_END, RECV_AT)` 清理。
## 6. 动态事件与删除
### 6.1 FLOP
读取完整当前态 → 合并变化 → 保留运营日 → `STATE_VERSION` 加一 → 同事务登记 `KAFKA_MSG``KAFKA_SCHD` 与处理结果。
### 6.2 FDEL
同一事务内:
1.`FLID` 定位。
2. `STATE = ACTIVE`:置 `DELETED`,推进 `STATE_VERSION`,明细保留,发布删除事件。
3. `STATE = DELETED`:记幂等成功,**不推进 `STATE_VERSION`,不重复发布删除事件**。
4. 不存在:记幂等成功(迟到、重复不报错)。
**删除一律标记,不物理清除**`FLID` 复用未确认(§10)、删除与更新事件可能乱序(§7.3),物理清除统一由历史归档执行(§8.2)。共享航班 FDEL 先于主航班,各自处理,不做级联推断。
### 6.3 ADFT 恢复
ADFT 到达且 `FLID` 已存在、`STATE = DELETED`:这是同 `FLID` 生命周期重激活(§3.1),恢复 `ACTIVE`,推进 `STATE_VERSION`,登记状态事件。`FLID` 不存在则按新实例建立。
## 7. 对外处理
### 7.1 请求
`REQ_TRACK` 登记、`COUTMSGS` 发出;同类请求只留一条有效,新请求置旧为 `EXPIRED`。**只有 `RESP` 完成 RQFD 请求**,按(运营日、发送方、请求类型)匹配最新一条 `PENDING``DNLD` 是主动下发,独立处理,除现场确认的扩展规则外不得用于完成请求。协议无请求关联号,匹配规则待验收。
主链路不依赖逐日请求;当前只收定时下发的 48 小时窗口即为主链路的正常输入。
### 7.2 回填
提交后回填共享信箱;回填失败不得把已提交的 `SUCCEEDED` 改回 `FAILED`
### 7.3 Kafka
`KAFKA_SCHD` 发整态:Dispatcher 合并同一 `FLID` 未发事件,按最新 `STATE_VERSION` 输出。`KAFKA_MSG` 只通知变化。两主题间不保证顺序;整态键缺失表示删除旧值。
判旧:消费端 `(FLID, STATE_VERSION, UPDATED_AT)`
删除用 tombstone:载荷至少含 `FLID``STATE_VERSION``DELETED`;仅在 `ACTIVE → DELETED` 时发布,与删除同事务登记,投递失败持续重试。物理清除(§8.2)是内部清理,**不再发布业务删除事件**;仅对未经 FDEL、由生命周期直接清除的航班,在清除前补发一次删除事件。即使确认 `FLID` 永不复用,`STATE_VERSION` 也不能省——更新与删除事件仍会乱序;`FLID` 复用风险与 GENERATION 方案见 §10。
### 7.4 身份不变量
- `FLID` 是主键;一个 `FLID` 最多一个非空 `OPERATION_DAY`,一经确定不可改。
-`FLNO` 不同运营日 = 不同 `FLID`
- 身份变更时 FDEL 与 ADFT 的 `FLID` 可能相同也可能不同(§3.1);不得由航班号或日期推断实例;`FLID` 不同时是终止与新建,不是主键变更。
`OPERATION_DAY` 不可变由应用层在快照入口校验;需数据库层强化可加触发器或 `WHERE operation_day IS NULL OR operation_day = :d`
## 8. 生命周期与清理
运营日过去不等于航班结束,不能按日期删除。
### 8.1 历史判定
**单一时间窗不构成删除依据**;须同时满足“超过配置时间窗”且有终态证据或足够长的静默期:
- 已取消(`CNCL` 非空)超过 48 小时;
- 已完成离港/到港终态(`NAAT`/`NEAT`,字段含义与来源系统需在术语表确认,§10)超过 48 小时;
- `STATE = DELETED` 超过 48 小时;
- 无终态字段时,最后一次有效更新超过兜底期限(默认 7 天)。
窗口按机场时区(Asia/Shanghai)计算。
### 8.2 归档与清除(顺序不可颠倒)
1. `HISTORY_SWEEP` 选出满足 §8.1 的航班(含 `DELETED`)。
2. 写入历史存储。
3. 历史存储返回成功的 `FLID` 集合,对应行**物理删除**主行与明细;归档结果只记录在历史存储,当前态不保留归档状态。
4. 未经 FDEL 的航班在清除前补发一次删除事件(§7.3);其余不发。
5. 失败或不明确的保留重试。
历史存储未接通时必须删 0 条;绝不允许先删当前态再补历史。**物理清除只发生在本步骤**。
### 8.3 保留期
| 对象 | 默认保留 | 前置条件 |
|---|---|---|
| 留痕 `SCHD_SNAP_LOG` | 90 天 | 按 `(SCOPE_END, RECV_AT)` 清理 |
| `OPERATION_DAY = NULL` 航班 | 7 天 | 仍未被快照收录;终止规则未定前只增不删 |
## 9. 失败与读取
| 错误 | 处理 | 队头行为 |
|---|---|---|
| 非法报文、数量不符、容量超限 | `DEAD`,不重试 | 立即释放 |
| 未支持类型 | `FAILED(UNSUPPORTED)` | 达阈值转 `DEAD` |
| 数据库或内部故障 | `FAILED(INFRA)`,退避重试 | 成功或超限前阻塞 |
| 快照含归属日不符的 `FLID` | 整包拒绝 `DEAD(PROTOCOL)`,留痕告警 | 立即释放,交人工确认 |
| `SEQN` 回退 / `SCHD_REVIVE_CONFLICT` | 只记 `FLAGS` 并告警 | 不阻塞 |
| 留痕写失败 | 只记 error 指标 | 不阻塞 |
读取:完整航班状态须在一致性读事务中读主表与全部明细,且过滤 `STATE = ACTIVE`。当前逐航班读取有 N+1 问题,主表与明细可能不在同一快照——批量加载与一致性读边界待补。
## 10. 未定事项
由 ACM2-31 跟踪:快照与动态事件顺序、FLOP 集合语义、删除事件版本、幂等键生命周期、FIFO 参数、一致性读取、快照完整性校验、Oracle 适配。
| 事项 | 状态 |
|---|---|
| SCHD 按 `FLID` upsert 且不恢复 `DELETED`;删除一律标记,物理清除由历史归档执行 | 已定案(§4、§5.1、§6.2、§8.2 |
| FDEL + ADFT 的 `FLID` 可能相同或不同 | 已定案为处理规则(§3.1、§6.3);上游实际行为待 SIS 条款核对 |
| `OPERATION_DAY` 按机场运营日规则由 `SODT` 与机场时区计算,切日边界业务配置 | 已定案(§3.5);边界值待业务确认 |
| ADFT 字段缺失语义 | **待确认**(§2.1);确认前不得沿用 FLOP 合并规则 |
| `FLID` 是否会被复用 | **待上游书面确认**;确认前保持 `DELETED` 中间态。若不能保证永不复用,需引入生命周期表 `FLIGHT_ID_LIFECYCLE(FLID, GENERATION, LAST_VERSION)`,对外版本升级为 `(FLID, GENERATION, STATE_VERSION)` |
| 上游取消是否必然伴随 FDEL | **待上游确认**;确认前不做任何基于名单缺席的删除 |
| 旧快照晚于 FLOP 的覆盖风险 | 接收序模型的已知代价;上游顺序保证待确认(ACM2-31#8 |
| `NAAT` / `NEAT` 的业务含义与来源系统 | 待术语表确认(§8.1) |
| 逐运营日请求完整日计划 | 未完成;主链路不依赖,是否需要待上游 FDEL 可靠性确认后再评估 |
当前偏差:GTDT 按 Replace 处理但 FLOP 集合语义未确认;动态事件在锁外预览、事务内重算,目标只算一次;回填待办提交后写,崩溃窗口不可恢复,目标事务内预登记。
### 规则 M:快照与动态事件的应用顺序
当前实现按信箱接收顺序应用 SCHD 与 FLOP。SCHD 处理时完整替换对应字段,因此接收顺序上后到的 SCHD 会覆盖先到的 FLOP——**这只是当前实现结果,不代表 SCHD 在业务时间上优先**,不得对外承诺快照权威(ACM2-31#8)。
### 规范依据(SIS
| 结论 | 出处 |
|---|---|
| 以 AODB 最新数据覆盖本地 | §1.6.2 |
| `P` 标签为空 = 删除该值;`O` 标签仅在有值时出现 | §1.6.3 |
| 日计划是完整快照;未发送字段表示 AODB 已无该数据,应删除 | §3.16 注释 2、4 |
| 子系统不消费的字段可忽略 | §3.16 注释 3 |
| 日计划不含前日延误航班,保留与清理由子系统负责 | §3.16 注释 1 |
| 主动下发为约 48 小时窗口,请求应答按请求范围 | §3.16 Data Range |
| 代码共享为独立实例,删除时共享先于主航班 | §1.6.5 |