docs(acm2): 重构 spec 并整理 Q 编号

- specification:直白说法、编号一览、合并 Q 台账、补 Q2/Q15
- interface-contract:POST /cminmsgs/send 待确认项指向 Q15
- 代码:SRVT/VIPF 清空语义 Q11 → Q2
This commit is contained in:
windyboy
2026-09-21 07:54:55 +08:00
parent 7aec4ea0ab
commit b4ef787e93
7 changed files with 145 additions and 146 deletions
+1 -1
View File
@@ -10,7 +10,7 @@
| 接口 | 请求契约 | 成功响应契约 | 失败契约 | 尚需确定 |
|---|---|---|---|---|
| `POST /cminmsgs/send` | 请求体是 XML 原文;接受 `text/xml``application/xml``text/plain`,默认 UTF-8;仅限内网,网络层限制来源。 | 报文写入 `CMINMSGS` 后返回信箱编号;写入的报文与上游投递走同一条处理路径、效果一致;该响应只证明已落信,不证明业务处理或下游投递(`US-02`)。 | 空报文、超大小上限、非法 XML 不落信并返回错误;写信失败不返回编号;XML 解析禁用外部实体和外部资源访问。 | 大小上限、请求编码与 `Content-Type` 的精确处理规则、HTTP 状态码、成功/失败响应体字段及样例。 |
| `POST /cminmsgs/send` | 请求体是 XML 原文;接受 `text/xml``application/xml``text/plain`,默认 UTF-8;仅限内网,网络层限制来源。 | 报文写入 `CMINMSGS` 后返回信箱编号;写入的报文与上游投递走同一条处理路径、效果一致;该响应只证明已落信,不证明业务处理或下游投递(`US-02`)。 | 空报文、超大小上限、非法 XML 不落信并返回错误;写信失败不返回编号;XML 解析禁用外部实体和外部资源访问。 | 大小上限、请求编码与 `Content-Type` 的精确处理规则、HTTP 状态码、成功/失败响应体字段及样例`Q15`。 |
| `POST /schd/sync` | 触发一次 `RQFD` 日计划请求;请求字段尚未定义,登记与落信规则见「在途与作废」。 | 响应内容尚未定义;无论表示登记还是落信,都不代表 AODB 已收到。 | 错误响应尚未定义。 | 请求字段与时间格式、成功响应表示已登记还是已落信(`Q16`)、状态码、响应字段及样例。 |
| `GET /all/flights` | 无已定义的请求字段;从 Redis 投影读取当前全部动态航班,不含共享航班,与网页客户端同源(`US-12`)。 | 返回查询到的全部航班,不分页;JSON 由消息文档中的 XML 结构转换而来(`Q21`)。 | Redis 异常时返回错误,不能返回空列表伪装成功。 | 状态码和错误响应样例;外层包装是否沿用旧 `ResponseDto`。 |
+138 -139
View File
@@ -1,195 +1,194 @@
# 规范:术语、约定、前提、不变量与能力边界
# 规范:术语、约定、前提、不变量与承诺范围
本文整理 msgexchange-v2 对外接口约定(`C-x`)、前提条件`PRE-x`)、系统不变量`INV-x`)、能力边界`CLM-x`)、待确认事项(`Qn`已知偏差`G-NAME`,并统一给出术语。条款依据 [requirements.md](requirements.md) 与 [architecture.md](architecture.md) 编写;字段联调草案见 [contracts/interface-contract.md](contracts/interface-contract.md),对外承诺以本文件为准
本文列出 msgexchange-v2 与外部系统的约定(`C-x`)、运行前提(`PRE-x`)、必须始终成立的规则`INV-x`)、能对外承诺的内容`CLM-x`)、待事项(`Qn`已知缺口`G-NAME`依据 [requirements.md](requirements.md) 与 [architecture.md](architecture.md)HTTP/Kafka/Redis 字段草案见 [contracts/interface-contract.md](contracts/interface-contract.md)。
`C-x``PRE-x``INV-x``CLM-x``Qn``G-NAME` 只在本文件定义编号规则见 [README.md](README.md)。
各编号只在本文件定义编号稳定、不重排,规则见 [README.md](README.md)。
约定条目的标记:
标注 `(待确认 Qn` 需对方确认;标注 `(本系统承诺)` 不依赖对方同意;未标注视为已定。字段未定另起「待确认:」并指向 `Qn`
- 需要对方确认的,末尾标 `(待确认 Qn`
- 内容已定、个别字段未定的,另起一行以「待确认:」列出并指向 `Qn`
- 本系统单方遵守、不依赖对方认可的,末尾标 `(本系统单方承诺)`
- 未标注的,表示需求或架构已定,本系统按此执行。
## 编号一览
| 族 | 所在小节 | 含义 |
|---|---|---|
| `C-1``C-11` | 约定 | 对外约定 |
| `PRE-x` | 前提 | 运行前提 |
| `INV-1``INV-11` | 不变量 | 必须始终成立的规则 |
| `CLM-1``CLM-7` | 声明边界 | 能否对外承诺 |
| `Q1``Q25` | 待确认事项台账 | 待定事项(`Q19``Q20` 未分配) |
| `G-*` | 当前已知偏差 | 已知实现缺口 |
## 1. 术语
| 术语 | 含义 |
|---|---|
| 上游 | 产生报文的 AODB报文经 CIIMS adapter 写入信箱。 |
| 信箱 | 共享 MySQL 的 `CMINMSGS`(入站)与 `COUTMSGS`(出站)归库方所有;边界见 `C-2`。 |
| 库方 | 信箱所在的共享 MySQL 管理方,即 CIIMS adapter。 |
| 处理标记 | 即信箱行上的处理时间:为空表示未处理,处理完成时写入完成时刻。 |
| 落信 | 报文写入信箱成为一行入站由 CIIMS adapter 或兼容入口写入出站由本系统写 `COUTMSGS`;入站报文写进信箱后可能尚未登记。 |
| 登记 | 本系统在自有 PG 为这条报文建立处理记录,排队等待处理。 |
| 处理完成 | 这条消息的结果已经确定——业务改动生效,或明确跳过、进入死信;之后才回填信箱标记发 Kafka。 |
| 回填 | 处理完成后,把完成时刻写回信箱行的处理时间字段,告知上游该消息已处理。 |
| 投递 | 待发事件,发往 Kafka。 |
| Redis 投影 | 供网页客户端(`GET /all/flights`)查询的航班投影,内容来自自有 PG 当前态;读写边界见 `INV-11`。 |
| 自有 PG | 本系统唯一的业务数据库;航班当前态、管道记录与静态参考数据都在这里。 |
| 权威 | 航班当前态以自有 PG 为准(`INV-5`。 |
| 出站请求 | 经 `COUTMSGS` 向 AODB `RQRD` 参考数据请求与 `RQFD` 日计划请求,消费方为 CIIMS adapter。 |
| 运营航班显示界面 | 需求所称网页客户端:Kafka 侧称运营航班显示界面,查询侧称网页客户端(`GET /all/flights` / Redis。 |
| 航班历史 | 已结束航班写入 Elasticsearch 后的副本;写成功后才从实时数据删除。 |
| 静态参考数据 | 13 类基础数据与资源状态,写自有 PG 的独立数据表组(逻辑视图 `REF_MASTER`,物理形态由内部迁移确定),admin-api 直接只读。 |
| 上游 | 报文的 AODB报文经 CIIMS adapter 信箱。 |
| 信箱 | 共享 MySQL 的 `CMINMSGS`(入站)与 `COUTMSGS`(出站)归库方管理,边界见 `C-2`。 |
| 库方 | 管理共享 MySQL 信箱的一方(CIIMS adapter。 |
| 处理标记 | `CMINMSGS_DATE_PROCESSED`:空=未处理,有值=已处理时间。 |
| 写入信箱 | 报文存成信箱里的一行入站由 adapter 或 `POST /cminmsgs/send` 写入出站由本系统写 `COUTMSGS`。写入后未必马上开始处理。 |
| 建立处理记录 | 本系统在自有 PG 记一条待处理记录并排进队列。 |
| 处理完成 | 结果已定(已改库、明确跳过、或无法处理并归档);之后才写处理标记发 Kafka。 |
| 写回处理时间 | 处理完成后,把时刻写`CMINMSGS_DATE_PROCESSED`。 |
| 发到 Kafka | 待发送的变更事件写入 Kafka。 |
| 网页客户端 | 读 Kafka`msg`/`schd`)或调 `GET /all/flights`Redis)的前端。 |
| Redis 航班快照 | 网页客户端查询用的航班数据,从自有 PG 同步;规则见 `INV-11`。 |
| 自有 PG | 本系统主库:航班、处理记录、静态参考数据。 |
| 出站请求 | 本系统`COUTMSGS` 向 AODB `RQRD`参考数据)或 `RQFD`日计划。 |
| 航班历史 | 已结束航班写入 Elasticsearch;写入成功后才从实时库删除。 |
| 静态参考数据 | 13 类基础数据与资源状态;存自有 PG,admin-api 只读。 |
## 2. 约定
### 2.1 共享信箱(库方)
- **C-1** 本系统清理已处理的入站信箱行(MySQL `CMINMSGS`):回填完成且超过保留期后才删;保留期可配置,约 1 个月`Q9`)。处理未完成或回填未完成的行不删。
- **C-2** 共享 MySQL 不做 Schema 变更,本系统只读写 `CMINMSGS``COUTMSGS` 两张表
- **C-1** 删除已处理完毕的入站行(`CMINMSGS`):处理标记已写入,且超过保留期(可配置,默认约 1 个月)。处理完或未写回处理时间的行不删。
- **C-2** 共享 MySQL 不改表结构;本系统只读写 `CMINMSGS``COUTMSGS`
### 2.2 上游(AODB / SIS
- **C-3** 业务身份由 `SNDR``TYPE``STYP``SEQN` 四字段组合;`SEQN` 自增,极少重置(消息服务器重启),重置后不会与旧消息冲突。
- **C-4** 出站请求写入 `COUTMSGS`交付承诺止于落信。编码:`SNDR=OMMS``SEQN` 本系统自建序列`DTTM` 北京时间 `YYYYMMDDHHMMSS``RQRD` 子类型以 SIS 为准共 14 类`RQFD` `STYP=NONE`,全量同步不带 `STDB`/`STDE` 筛选`Q13`
- **C-5** 航班删除标记删除,主航班与共享航班各自独立标记。
- **C-6** 日计划快照里没有携带的字段视为 AODB 已删该值,本地同步清除`US-07` AC3)。
- **C-3** 一条报文的身份 = `SNDR` + `TYPE` + `STYP` + `SEQN``SEQN` 自增,极少消息服务器重启时重置;重置后不与历史冲突。
- **C-4** 出站请求写入 `COUTMSGS`本系统只保证写入信箱,不保证 AODB 收到。编码:`SNDR=OMMS``SEQN` 本系统生成`DTTM` 北京时间 `YYYYMMDDHHMMSS``RQRD` 共 14 种子类型以 SIS 为准`RQFD` `STYP=NONE`,全量不带 `STDB`/`STDE` 筛选。
- **C-5** 航班只打删除标记主航班与共享航班各自独立标记。
- **C-6** 日计划快照里没带的字段视为 AODB 已删该值,本地也清掉`US-07` AC3)。
### 2.3 HTTP 入口
- **C-7** `POST /cminmsgs/send`XML 写进入站信箱,与 adapter 投递走同一套处理。成功返回消息编号(表示已入库、未处理);失败不返回编号。支持 `text/xml``application/xml``text/plain`UTF-8);空、超长、格式错的不写;解析不拉外部资源。内网访问由网络配置控制。仅内部联调。大小上限、HTTP 状态码、成功/失败响应体字段及样例属接口契约「HTTP」,尚未确定
- **C-8** `POST /schd/sync`:登记一次 `RQFD` 日计划请求;同子类型已有在途时新请求不落信,待在途结案后再写入 `COUTMSGS`(架构「主流程」、`US-09` AC1)。请求参数与 HTTP 响应待 `Q16`
- **C-7** `POST /cminmsgs/send`:XML 写进入站信箱,与 adapter 走同一套处理。成功返回消息编号(表示已写入、尚未处理);失败不返回编号。接受 `text/xml``application/xml``text/plain`UTF-8);拒收空报文、超长、非法 XML;解析禁止访问外部资源。内网访问由网络层控制;仅供内部联调
- 待确认:大小上限、HTTP 状态码与响应体 → `Q15`
- **C-8** `POST /schd/sync`:登记一次 `RQFD` 日计划请求。同类型若还有未处理完的请求,新请求先不写信箱,等旧的处理完再写 `COUTMSGS``US-09` AC1)。
- 待确认:请求参数与 HTTP 响应 → `Q16`
### 2.4 下游(admin-api、网页客户端与运营航班显示界面
### 2.4 下游(admin-api、网页客户端)
- **C-9** 航班变更发 Kafka 主题 `msg`(单条)和 `schd`(批量);`schd` 沿用旧系统粒度:两次 tick 之间积累的变化序列化成一个 `SCHD.FLTR` 数组 JSON,整批作为单条 record 发出(`Q1`),空字段不输出,没变化不发;删航班不进 `schd`,只由 `msg` 发一条删除通知;不设消息键。同一条可能发多次
- **C-10** 静态参考数据写本系统数据库,供 admin-api 只读;本系统不调用 admin-api。
- **C-11** 航班投影写入 Redis,供读取全体动态航班`GET /all/flights` 与网页客户端读同一份;返回 JSON 由消息文档中的 XML 结构转换而来(`Q6``Q21`
- **C-9** 航班变更发 Kafka`msg` 一条变更一条消息;`schd` 把两轮采集之间积累的变化合成一条消息,内容是 `SCHD.FLTR` JSON 数组。空字段不输出,没变化不发;删航班只走 `msg`;不设 message key;可能重复投递(待确认 `Q1`
- **C-10** 静态参考数据写本系统库,admin-api 只读;本系统不调用 admin-api。
- **C-11** 航班快照写 Redis`GET /all/flights` 与网页客户端读同一份;JSON 由报文 XML 转换
- 待确认:`MAFL` 是否一并提供(`Q6`);状态码与响应包装(`Q21`)。
## 3. 前提
前提失效时不变量必须整体重
前提不成立时,下文规则需整体重
| 编号 | 前提 | 若不成立的影响 | 状态 |
|---|---|---|---|
| PRE-1 | 测试环境与生产隔离:独立的数据库、RedisKafka 主题,不连生产信箱 | 测试结论与隔离验收(`OPS-3`)失效 | 部署与配置约束 |
| 编号 | 前提 | 若不成立 |
|---|---|---|
| PRE-1 | 测试与生产隔离:各自独立的 DB、RedisKafka 主题,测试不连生产信箱 | `OPS-3` 验收无效 |
## 4. 不变量
### 4.1 管道
### 4.1 消息处理
- **INV-1** 处理时间仍为空的信箱行会登记到本地;同一编号只登记一次。重扫或重启不重复登记,也不会漏掉未登记的行
- **INV-2** 只有本地处理结束后才写信箱里的处理时间;本地记「已结束」时必须同记下还要去更新信箱,两笔记在同一次写入里。回填只把空标记写成完成时刻,不覆盖已有值(本系统单方承诺)。
- **INV-3** 航班增量与删除:改状态和记「待发 Kafka」同一次写入;记「已结束」「还要更新信箱」另一次写入,且须在 Redis 写成功之后。处理未结束前不发 Kafka`US-03` AC4)。日计划与静态参考数据见架构「主流程」。
- **INV-4** 日计划整份报文校验不过时,本地航班数据一律不改(`US-07` AC1)。
- **INV-1** 处理标记为空的信箱行会被建立处理记录;同一编号只一次。重扫或重启不重复、不遗漏
- **INV-2** 只有本地处理结束后才写处理时间;标记「处理结束」时必须同一次记下还要写回信箱」。只填空的处理标记,不覆盖已有值(本系统承诺)。
- **INV-3** 航班增量与删除:改库与记「待发 Kafka」同一次提交;「处理结束」「还要写回信箱」在 Redis 写成功后再记一次。处理完成前不发 Kafka`US-03` AC4)。日计划与静态参考数据见架构「主流程」。
- **INV-4** 日计划整报校验失败时,本地航班数据不改(`US-07` AC1)。
### 4.2 航班
### 4.2 航班
- **INV-5** 航班当前态只以自有 PG 为准;信箱、Redis、Kafka、展示视图都不是
- **INV-6** `FLID` 唯一。
- **INV-7** 日计划:快照里没有的航班标记删除、登记删除事件从 Redis 掉;报文没带的字段本地清掉(`US-07` AC2/AC3)。
- **INV-8** 航班标成已删除后,要从 Redis 掉;去掉成功才算这条消息处理完(`US-06` AC1)。
- **INV-9** 日计划可以分批写,但整份写完且 Redis 按这份结果刷完才算处理完;失败就整份重来(`US-07` AC4/AC5)。
- **INV-5** 航班当前数据以自有 PG 为准。
- **INV-6** `FLID` 全局唯一。
- **INV-7** 日计划:快照里没有的航班打删除标记、记删除事件从 Redis 掉;快照没带的字段本地清掉(`US-07` AC2/AC3)。
- **INV-8** 已打删除标记的航班必须从 Redis 掉;删掉才算这条消息处理完`US-06` AC1)。
- **INV-9** 日计划可以分批写 PG,但整份 PG 写完且 Redis 按快照刷完才算完成;失败则全部重来(`US-07` AC4/AC5)。
### 4.3 Redis 投影
### 4.3 Redis 航班快照
- **INV-10** Redis 写成功,这条消息才算处理完;写失败就还没处理完,不回填、不发 Kafka下轮再写`US-05` AC4)。本地已经写进库的,不因 Redis 失败而撤`US-03` AC3)。
- **INV-11** Redis 航班投影只由本系统维护;查询与网页客户端读同一份Redis 异常时报错,不交空列表`US-12`)。
- **INV-10** Redis 写成功才算处理完成;失败则不写回处理时间、不发 Kafka下轮重试`US-05` AC4)。已写入 PG 的不因 Redis 失败而撤`US-03` AC3)。
- **INV-11** Redis 航班快照只由本系统维护;查询与网页客户端同源Redis 出错时返回错误,不能用空列表假装成功`US-12`)。
## 5. 声明边界
| 编号 | 承诺 | 依赖 | 现在能否作出 | 限制或原因 |
|---|---|---|---|---|
| CLM-1 | 同一消息处理不会多出一份业务效果 | `US-03` | 不能 | 各类报文细则未补齐`G-FLOP-IDEMPOTENT` |
| CLM-2 | 按信箱编号升序处理 | `US-03` AC1 | 能 | — |
| CLM-3 | 主题 `msg` 上同一 `FLID` 内按发送顺序保序 | `US-08` AC2、`C-9` | 能 | `msg` 单分区;至少一次投递时重发可能重复(`D2` |
| CLM-4 | 出站请求写入共享出站表 `COUTMSGS` | `C-4` | 能 | 只保证写入信箱,不保证 AODB 收到 |
| CLM-5 | 消息在固定时内处理完 | — | 不能 | 需求未定完成时限 |
| CLM-6 | 容量与吞吐量级 | — | 不能 | 需求未给日量与峰值,有数据后再估 |
| CLM-1 | 同一消息重复处理不会重复生效 | `US-03` | 不能 | 各类报文细则未写完`G-FLOP-IDEMPOTENT` |
| CLM-2 | 按信箱编号从小到大处理 | `US-03` AC1 | 能 | — |
| CLM-3 | 主题 `msg`同一 `FLID` 内按发送顺序排列 | `US-08` AC2、`C-9` | 能 | `msg` 单分区;至少一次投递时可能重复(`D2` |
| CLM-4 | 出站请求写入 `COUTMSGS` | `C-4` | 能 | 只保证写入信箱,不保证 AODB 收到 |
| CLM-5 | 消息在固定时内处理完 | — | 不能 | 需求未定时限 |
| CLM-6 | 容量与吞吐量级 | — | 不能 | 需求未给日量与峰值 |
| CLM-7 | 配置不完整时拒绝启动 | `OPS-1` | 能 | — |
## 6. 待确认事项台账
### 6.1 待对方确认
`Q1``Q25` 按编号排列。答复后更新对应 `C-x` 或条款,步骤见 [README.md](README.md)「维护清单」。
| 编号 | 事项 | 当前假定 | 影响 |
| 编号 | 状态 | 事项 | 说明 |
|---|---|---|---|
| Q1 | `schd` Kafka record 粒度 | 已定原则:按旧系统,两次 tick 之间积累的整批序列化成一个 JSON 数组作为单条 record 发出(`C-9` | 消费方未确认按此格式解析 |
| Q3 | 现场会发但 SIS 未定义的子类型(靠桥、延误等)的报文形态与逐类终态 | 按现有处理逻辑延续,不得因 SIS 未记载就丢掉(`US-05` AC1) | 逐类终态与幂等规则未定(`G-FLOP-SEMANTICS` |
| Q4 | 上游重发时是否可能改发正文 | 报文不可变,绑定身份后不比对内容(implementation.md「消息、身份与决策」) | 改发正文的重发会被判为重复并跳过 |
| Q5 | `msg` 的 value 字段与类型、编码方式、变更与删除的区分方式 | 沿用旧系统:value 是 `MSG` 的 JSON`META` 加对应业务体),日计划到达通知只有 `META`(接口契约「Kafka」) | 消费方读取契约无法定稿 |
| Q6 | 网页客户端读取 Redis 投影的约定 | 读写边界见接口契约「Redis:航班查询投影」与 `C-11``MAFL` 是否随投影提供仍未定 | 消费方读取契约无法定稿 |
| Q1 | 待对方 | `schd` 一条 Kafka 消息里装多少变更 | 见 `C-9` |
| Q2 | 待对方 | FLOP 里 `SRVT`/`VIPF` 等集合段缺席是否等于删除 | 见 `G-SRVT-VIPF` |
| Q3 | 待对方 | SIS 没写明的 FLOP 子类型怎么解析、处理完算啥 | 按现有逻辑处理(`US-05` AC1);见 `G-FLOP-SEMANTICS``CLM-1` |
| Q4 | 待对方 | 上游重发时会不会改正文 | 假定内容不变,只认身份;改正文会被当重复跳过 |
| Q5 | 待对方 | `msg` 消息体格式、变更与删除怎么区分 | 沿用旧系统 JSON(接口契约「Kafka」) |
| Q6 | 待对方 | 网页客户端怎么读 Redis 快照 | 见 `C-11``MAFL` 是否提供未定 |
| Q7 | 已定 | 信箱编号只增不减、不重用 | `INV-1``CLM-2``US-01` AC3 |
| Q8 | 已定 | 入站处理时间列名 `CMINMSGS_DATE_PROCESSED` | 术语「处理标记」、`US-10` |
| Q9 | 已定 | 入站行删除条件与保留期 | `C-1` |
| Q10 | 已定 | `SEQN` 重置与身份规则 | `C-3` |
| Q11 | 已定 | 日计划没带字段是否删除 | `C-6` |
| Q12 | 已定 | 主航班与共享航班删除联动 | `US-06` AC2 |
| Q13 | 已定 | `RQRD` / `RQFD` 编码字段 | `C-4` |
| Q14 | 本系统 | 生产用 PostgreSQL 还是 Oracle 11g | Oracle 未验证前不作支持承诺 |
| Q15 | 本系统 | `POST /cminmsgs/send` HTTP 约定 | 大小上限、状态码、响应体;见 `C-7` |
| Q16 | 本系统 | `POST /schd/sync` 请求与响应 | 见 `C-8` |
| Q17 | 本系统 | 人工发 `RQRD` 的入口 | `US-09` 要求能人工发,HTTP 清单里没有 |
| Q18 | 本系统 | 回退时正在处理的消息怎么办 | 见 `OPS-4` |
| Q19 | — | (未分配) | — |
| Q20 | — | (未分配) | — |
| Q21 | 本系统 | `GET /all/flights` HTTP 约定 | 见 `C-11` |
| Q22 | 本系统 | 静态参考数据对应哪些表 | 见 `C-10`、接口契约 |
| Q23 | 本系统 | Elasticsearch 历史怎么写、保留多久 | 见 `US-14``G-FLIGHT-HIST-RETENTION` |
| Q24 | 本系统 | `REQ_TRACK` 已结案记录保留多久 | 见 `G-REQ-TRACK-RETENTION` |
| Q25 | 本系统 | 季度计划从哪来、什么格式 | SIS 只有日计划;旧系统读 Oracle 季度表 |
答复就地更新结论,并按 [README.md](README.md)「维护清单」落到对应条款
### 6.2 已确认
| 编号 | 事项 | 结论 |
|---|---|---|
| Q7 | 信箱编号的单调、不复用、不回退 | 已定案(`US-01` AC3、`CLM-2` |
| Q8 | 入站处理时间列 | 已定案:列名 `CMINMSGS_DATE_PROCESSED``US-10` |
| Q9 | 入站信箱行(MySQL `CMINMSGS`)清除 | 已定案(`C-1` |
| Q10 | 上游 `SEQN` 的重置周期与身份是否加日期边界 | 已定案(`C-3` |
| Q11 | 日计划未携带字段的删除语义 | 已定案:未携带字段本地清除(`C-6` |
| Q12 | 主/共享删除顺序 | 已定案(`C-5` |
| Q13 | `RQRD` / `RQFD` 编码字段 | 已定案(`C-4` |
### 6.3 本系统与需求方待决
以下事项不出自对接方,由本系统与需求方决定:
| 编号 | 事项 | 当前假定 | 影响 |
|---|---|---|---|
| Q14 | 生产库选型 | 自有 PG 是唯一权威;生产环境用 PostgreSQL 还是 Oracle 11g 不能从三份依据确定,Oracle 适配验证通过前不作支持承诺 | 生产部署验收 |
| Q16 | `POST /schd/sync` 的请求字段与时间格式、成功响应表示已登记还是已落信、状态码与错误响应 | 交付承诺止于落信(`C-4`);旧系统线索为 `{startDate, endDate}` 与 12 小时制时间(接口契约「HTTP」) | 响应契约无法定稿(`C-8` |
| Q17 | 人工发起 `RQRD` 的方式 | `US-09` 要求人工发起,HTTP 接口清单没有对应入口 | 参考数据请求无法人工触发 |
| Q18 | 回退时在途消息(已提交业务变更、未到处理完成)的处置 | 回填了结后切换(`OPS-4`);在途消息无跨系统幂等保障 | 回退演练的验收口径(`OPS-4` |
| Q21 | `GET /all/flights` 的状态码、错误响应样例、外层包装是否沿用旧 `ResponseDto` | 返回体是消息文档中的 XML 结构转换出的 JSON;读 Redis、返回全体动态航班、不分页(`C-11` | 查询契约无法定稿 |
| Q22 | 13 类报文与资源状态如何映射到 admin-api 实体对齐的基础数据表组(联调栈 `basicdata` schema 为承载) | 表与列直接取 admin-api 实体注解,不改名、不合并(接口契约「自有 PostgreSQL:内部存储与 admin-api 只读」);类别码与识别标签见接口契约「静态参考数据类别与编号来源」;表组物理形态属内部迁移 DDL,不作待决 | admin-api 读取契约无法定稿(`C-10` |
| Q23 | Elasticsearch 历史索引、文档 ID、字段映射、成功判据、保留期与容量上限 | 文档 ID 沿用旧系统 `SODT + FLID` 幂等 upsert,写入结果不明时重试同一写入(接口契约「Elasticsearch 航班历史写入」);历史写入确认成功才删实时数据(`D1``US-14` AC3 | 历史链路无法验收(`US-14``G-FLIGHT-HIST-RETENTION` |
| Q24 | `REQ_TRACK` 已结案记录的保留期取值 | 到期清理没有可依据的窗口(`G-REQ-TRACK-RETENTION` | 请求历史清理无法实现(`US-09` |
| Q25 | 季度计划的数据来源与归属:admin-api 从本系统库读季度计划,而 SIS 只有日计划事件(`SIS_AODB_RMS-V0.1.md`「AODB daily flight schedule event」「AODB daily schedule response to RMS event」「RMS daily schedule request event」) | 应由 AODB 下发,报文形态待确认;旧系统读 Oracle 的 `FIMS_FLIGHTSCHD_SEASON` | admin-api 的季度计划查询没有供数方 |
状态说明:**待对方** = 需对接方确认;**已定** = 结论已写入 `C-x`/`INV-x`;**本系统** = 由本系统与需求方决定;**—** = 编号保留未用
## 7. 当前已知偏差
本表是偏差标记的唯一出处,其他文档只写 `G-NAME`闭合时删除本行全仓引用。
偏差标记只在本表定义;其他文档只写 `G-NAME`解决后删本行全仓引用。
| 偏差 | 缺(交付状态见 Plane | 影响 |
| 偏差 | 缺什么(交付进度见 Plane | 影响 |
|---|---|---|
| `G-REQ-TRACK` | 出站请求跟踪未交付 | `US-09` |
| `G-REQ-OPEN-UNIQUE` | 同类型在途唯一限制未交付 | `US-09` |
| `G-REQ-TRACK-RETENTION` | `REQ_TRACK` 已结案保留期取值未定(`Q24` | `US-09` |
| `G-FLIGHT-HIST-RETENTION` | 历史保留期与容量上限未定(`Q23` | `US-14`;实时数据删除后历史是唯一副本 |
| `G-FLOP-IDEMPOTENT` | 逐类幂等规则未补齐 | `US-03``CLM-1` |
| `G-FLOP-SEMANTICS` | 动态报文语义与 `US-05` 的差距未闭合 | `US-05` |
| `G-FLOP-UNMAPPED` | [XSD](legacy/unisysaodbsis.xsd)「FLOP 元素」字段映射缺口未闭合 | `US-05` |
| `G-MAFL` | 主航班共享列表未交付 | `US-06` AC2`C-5` |
| `G-SRVT-VIPF` | `SRVT``VIPF` 持久化明细未交付 | `US-05` |
| `G-SCAN-PREDICATE` | 按「处理时间为空」读取未交付 | `US-01``INV-1` |
| `G-REDIS-PROJECTION` | Redis 投影写/移除未交付 | `US-05``US-06``US-07``US-12` |
| `G-SCHD-SNAPSHOT` | 日计划快照删除/清空/分批未交付 | `INV-7``INV-3``INV-9` |
| `G-RESP-GUARD` | `SCHD-RESP` 过期判断未交付 | `US-07` |
| `G-PROC-CLEANUP` | 处理记录到期清理未交付 | `US-11` |
| `G-REF-DATA` | 静态参考数据处理与 admin-api 直读未交付 | `US-13``US-03` |
| `G-FLIGHT-HIST-RETENTION` | 历史保留期与容量上限未定(`Q23` | `US-14` |
| `G-FLOP-IDEMPOTENT` | 各类 FLOP 重复处理规则未写完 | `US-03``CLM-1` |
| `G-FLOP-SEMANTICS` | 动态报文处理与 `US-05` 要求不一致 | `US-05` |
| `G-FLOP-UNMAPPED` | [XSD](legacy/unisysaodbsis.xsd) FLOP 字段映射不全 | `US-05` |
| `G-MAFL` | 主航班共享列表未做 | `US-06` AC2 |
| `G-PROC-CLEANUP` | 处理记录到期清理未做 | `US-11` |
| `G-REDIS-PROJECTION` | Redis 快照写入与删除未做 | `US-05``US-06``US-07``US-12` |
| `G-REF-DATA` | 静态参考数据处理与 admin-api 直读未做 | `US-13` |
| `G-REQ-OPEN-UNIQUE` | 同类型未处理完时不允许再发,未做 | `US-09` |
| `G-REQ-TRACK` | 出站请求跟踪未做 | `US-09` |
| `G-REQ-TRACK-RETENTION` | `REQ_TRACK` 已结案保留期未定(`Q24` | `US-09` |
| `G-RESP-GUARD` | `SCHD-RESP` 过期判断未做 | `US-07` |
| `G-SCAN-PREDICATE` | 按「处理时间为空」扫描未做 | `US-01``INV-1` |
| `G-SCHD-SNAPSHOT` | 日计划快照删除、清空、分批未做 | `INV-7``INV-9` |
| `G-SRVT-VIPF` | `SRVT``VIPF` 明细未入库(`Q2` | `US-05` |
## 8. 验证映射
| 不变量 | 需求验收 | 要观察的结果 |
按条款编号排列。
| 条款 | 需求验收 | 要观察的结果 |
|---|---|---|
| INV-1 | `US-01` AC1/AC2/AC4 | 扫描重来与重启后登记数不变、行不丢;同一编号重复出现时处理记录数不增加 |
| INV-1、C-7 | `US-02` AC1AC5 | 兼容入口接受三种媒体类型、默认 UTF-8;空报文、超上限、非法 XML 不落信并返回错误,解析禁用外部实体与外部资源;成功返回编号只表示落信;内网来源由网络层配置核对;写入的行与上游投递同路径被发现、登记 |
| CLM-2(升序) | `US-01` AC3、`US-03` AC1 | 按编号升序处理;队头未完成时后面的消息不被处理 |
| INV-2 | `US-10` AC1/AC2 | 终态后才回填;重启后继续,写不上的有记录与告警 |
| C-9 | `US-08` AC1/AC2/AC3 | `msg` 单条变更、`schd` 批量;失败重试后仍能投出;一直失败的记录保留可查并告警;`msg` 同一 `FLID` 按发送顺序保序(`CLM-3`);同一条可能多发(`C-9` |
| INV-5 | 架构「数据归属与一致性」 | 航班当前态的权威写入只在自有 PG |
| C-5、`US-06` AC2 | `US-06` AC2 | 删共享联动主航班;删主级联删共享 |
| C-7 | `US-02` AC1AC5 | 三种 Content-Type、UTF-8;空/超长/非法 XML 不写入;禁外部资源;成功编号只表示已写入;与 adapter 同路径建记录 |
| C-8、CLM-4 | `US-09` AC1AC3 | 出站写入 `COUTMSGS`;HTTP 触发先登记,旧的处理完才写入信箱 |
| C-9、CLM-3 | `US-08` AC1AC3 | `msg` 单条、`schd` 批量;失败可重试;`msg``FLID` 保序;同条可能发多次 |
| C-10 | `US-13` AC1AC4 | 全量替换、增删改逐条;空值表示无值不是删除;单类校验失败只停该类 |
| INV-1(建立处理记录) | `US-01` AC1/AC2/AC4 | 重扫与重启后记录数不变、行不丢;同一编号重复出现时不新增记录 |
| INV-2 | `US-10` AC1/AC2 | 处理结束后才写回;重启后继续;一直失败有记录与告警 |
| INV-3、INV-10 | `US-03` AC4、`US-05` AC4、`US-06` AC1 | Redis 失败时不记「处理结束」与「还要写回」;完成前不发 Kafka |
| INV-4 | `US-07` AC1 | 校验失败后 PG 航班数据不变 |
| INV-5 | 架构「数据归属与一致性」 | 航班数据只写入自有 PG |
| INV-6 | implementation.md「权威模型」 | `FLID` 唯一 |
| INV-7 | `US-07` AC2/AC3 | 缺席航班在 PG 标为已删除并从 Redis 投影移除;未携带的字段清空 |
| US-04ADFT 更新) | `US-04` AC2 | 未携带的字段保持原值 |
| INV-3 | `US-03` AC4`US-05` AC4、`US-06` AC1 | 投影写失败时,没有终态与回填意图落库;处理完成前事件不可投递 |
| INV-4 | `US-07` AC1 | 校验失败后本地数据不变 |
| INV-9 | `US-07` AC4/AC5 | 分批失败后整包重处理收敛到同一目标(`G-SCHD-SNAPSHOT` 闭合前无法验证);Redis 在整包写入完成后按快照刷新,成功即与快照一致 |
| CLM-1(幂等收敛 | `US-03` AC2/AC3/AC5 | 死信与跳过留档且无业务副作用;错误在对应处理记录上可查;失败回滚后消息仍在未完成;已提交结果不被写信箱处理时间或发 Kafka 失败回滚;同一消息不产生两次效果须等 `Q3``G-FLOP-IDEMPOTENT``G-FLOP-SEMANTICS` 闭合 |
| INV-8 | `US-06` AC1 | 航班标记删除后从投影移除;移除失败时下轮重做 |
| US-14、D1 | `US-14` AC3/AC4 | 历史写入成功才删实时数据;删除事件在实时数据删除前登记(`D1`);清理跳过正在被消息处理的航班 |
| C-5 | `US-06` AC2 | 删共享联动主航班;删主航班级联删共享 |
| INV-10 | `US-05` AC4、`US-06` AC1 | 投影写失败的消息下轮仍被处理 |
| INV-11 | `US-12` AC1/AC2 | 返回全部非共享航班,且与 Redis 一致;Redis 故障时返回错误 |
| G-PROC-CLEANUP(未完成不删) | `US-11` AC1/AC2 | 未完成的记录不被删除 |
| C-10 | `US-13` AC1~AC4 | 全量整体替换、增删改逐条;空值是「没有值」不是删除;一类校验失败只停该类;参考数据落独立表组供 admin-api 只读 |
| CLM-4(止于落信) | `US-09` AC1AC3 | 出站请求写入 `COUTMSGS`;接口触发先登记,在途结案后才落信(`C-8` |
| CLM-5(不承诺完成时限) | — | 需求未定完成时限 |
| CLM-6(容量) | — | 需求未给容量数据 |
| OPS-4(切换与回退) | `OPS-4` | 回退演练:回填了结后切回,旧系统不重处理已产生业务效果的消息;在途窗口处置见 `Q18` |
| CLM-7(启动拒绝) | `OPS-1` | 配置错误启动失败测试可验收 |
| PRE-1(测试隔离) | `OPS-3` | 测试环境配置核对:独立的数据库、Redis 与 Kafka 主题,不连接生产信箱 |
| INV-7 | `US-07` AC2/AC3 | 快照缺席航班标删并从 Redis 删;未带字段清空 |
| INV-8 | `US-06` AC1 | 标删后从 Redis 删;失败下轮重做 |
| INV-9 | `US-07` AC4/AC5 | 分批失败全部重来(`G-SCHD-SNAPSHOT` 做完前不可验);PG 整份写完后再刷 Redis |
| INV-11 | `US-12` AC1/AC2 | 返回全部非共享航班且与 Redis 一致;Redis 故障返回错误 |
| CLM-1(重复不重复生效) | `US-03` AC2/AC3/AC5 | 跳过/无法处理留档且无业务改动;失败可查;PG 已提交不因写回或 Kafka 失败撤销;待 `Q3``G-FLOP-IDEMPOTENT``G-FLOP-SEMANTICS` |
| CLM-2(升序 | `US-01` AC3、`US-03` AC1 | 按编号从小到大;最前面未完成时后面的不处理 |
| CLM-7(启动拒绝) | `OPS-1` | 配置错误时启动失败 |
| US-04ADFT 更新) | `US-04` AC2 | ADFT 未带字段保持原值 |
| US-14、D1 | `US-14` AC3/AC4 | 历史写入成功才删实时数据;删事件先于实时删除登记 |
| G-PROC-CLEANUP(未完成不删) | `US-11` AC1/AC2 | 未处理完的记录不清理 |
| OPS-4(切换与回退) | `OPS-4` | 写回处理时间后再切回;在途消息见 `Q18` |
| PRE-1(测试隔离) | `OPS-3` | 测试环境独立 DB、Redis、Kafka,不连生产信箱 |
@@ -23,7 +23,7 @@ data class ScheduleBody(
* 只保留在 wire/domain、尚未映射到持久化明细的集合键(`[G-SRVT-VIPF]`)。
*
* 它们不参与合并、不进快照、不落库;保留的目的是不让入站事实在解码层被静默抹平,并让真实
* 流量里的出现情况可观测——决定"缺席是否等于删除"的 `Q11` 需要真实报文才能定案。
* 流量里的出现情况可观测——决定"缺席是否等于删除"的 `Q2` 需要真实报文才能定案。
*/
private val UNPERSISTED_COLLECTION_KEYS: Set<String> = setOf("SRVT", "VIPF")
@@ -132,7 +132,7 @@ data class FlightRecordXml(
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "ROUT") val rout: List<RoutXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "ERUT") val erut: List<RoutXml> = emptyList(),
// SRVT/VIPF 用可空表达"段是否出现":null = 未出现;出现即为列表(空元素得到一行空行)。
// 两者都不落明细表、不参与合并,清空语义待 `Q11``[G-SRVT-VIPF]`)。
// 两者都不落明细表、不参与合并,清空语义待 `Q2``[G-SRVT-VIPF]`)。
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "SRVT") val srvt: List<SrvtXml>? = null,
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "VIPF") val vipf: List<VipfXml>? = null,
)
@@ -38,7 +38,7 @@ internal object SisWireMapper {
* `filterValues` 去掉的正是"缺席",避免整包凭空清空本地明细(合并语义见 docs/implementation.md「SCHD 日计划」)。
*
* `SRVT`/`VIPF` 尚未有明细表(`[G-SRVT-VIPF]`):只用"键是否存在"表达段是否出现,保留原始
* 内容与顺序,不参与合并、不判断清空语义(`Q11`)——出现(哪怕为空)与缺席不再被抹平。
* 内容与顺序,不参与合并、不判断清空语义(`Q2`)——出现(哪怕为空)与缺席不再被抹平。
*/
private fun FlightRecordXml.collections(): Map<String, List<Map<String, String>>> {
val mapped = linkedMapOf(
@@ -21,7 +21,7 @@ class PipelineCounters {
* 入站记录里出现 `SRVT`/`VIPF` 段的条数(`[G-SRVT-VIPF]`)。
*
* 这两个集合目前只保留在 wire/domain,不落明细表、不参与合并;计数是"真实报文有没
* 有在用"的唯一取证渠道(清空语义 `Q11` 需要真实样例才能定案)。> 0 表示确有流量携带该段。
* 有在用"的唯一取证渠道(清空语义 `Q2` 需要真实样例才能定案)。> 0 表示确有流量携带该段。
*/
fun unpersistedCollectionSeenAdd(hits: Map<String, Int>) {
hits["SRVT"]?.let { srvtSeen.addAndGet(it.toLong()) }
@@ -94,7 +94,7 @@ class PipelineMetrics(
.strongReference(true)
.register(registry)
// SRVT/VIPF 尚未落明细表([G-SRVT-VIPF]):计数替代静默丢弃,为 Q11 提供真实流量证据。
// SRVT/VIPF 尚未落明细表([G-SRVT-VIPF]):计数替代静默丢弃,为 Q2 提供真实流量证据。
Gauge.builder("msgx.pipeline.codec.srvt_seen.total", counters) { it.srvtSeenCount().toDouble() }
.strongReference(true)
.register(registry)
@@ -190,7 +190,7 @@ class MessageProcessor(
}
}
// [G-SRVT-VIPF]SRVT/VIPF 段只保留在解码载荷里,尚未落明细表(清空语义待 Q11)。
// [G-SRVT-VIPF]SRVT/VIPF 段只保留在解码载荷里,尚未落明细表(清空语义待 Q2)。
// 计数 + 告警替代此前的静默丢弃;出现即证明真实报文携带该段,可作为定案依据。
val unpersisted = unpersistedCollectionHits(decoded.body)
if (unpersisted.isNotEmpty()) {