feat(acm2-92): RQRD 人工登记与 POST /refdata/sync(C-12)

- 新增 POST /refdata/sync:STYP 14 类校验,RSTA 须带 RTYP
- REQ_TRACK 加 styp/rtyp 列(V8 迁移);RQRD/RQFD 各自单开放槽
- OutboundRequestService 加 registerRqrdSync,派发按行目标编码
- Pump 加 REF-RESP 守卫:无在途 RQRD 时 SKIPPED
- 回归测试覆盖 C-12 登记/派发/400/409/独立槽/守卫
This commit is contained in:
windyboy
2026-09-23 16:10:54 +08:00
parent f45a0ac898
commit 4fbb974307
28 changed files with 568 additions and 68 deletions
+5 -4
View File
@@ -11,7 +11,8 @@
| 接口 | 请求契约 | 成功响应契约 | 失败契约 | 尚需确定 |
|---|---|---|---|---|
| `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` 日计划请求;请求体为空、请求全量(`C-4`;登记与落信规则见「在途与作废」。 | HTTP 200,返回请求编号;只表示已登记,不代表已落信、更不代表 AODB 已收到。 | 已有未结案请求时 HTTP 409,响应体 `open-request-exists`。 | — |
| `POST /schd/sync` | 触发一次 `RQFD` 日计划请求;请求体是网页选定的时间条件,写入规则见 `C-4`;登记与落信规则见「在途与作废」。 | HTTP 200,返回请求编号;只表示已登记,不代表已落信、更不代表 AODB 已收到。 | 已有未结案请求时 HTTP 409,响应体 `open-request-exists`。 | — |
| `POST /refdata/sync` | 触发一次 `RQRD` 参考数据请求;请求体携带网页选定的类别码 `STYP``STYP=RSTA` 时须带资源类型 `RTYP`,其余类别不得带;`STYP` 取值见 `C-4``RTYP` 取值见 implementation.md「静态参考数据」;登记与落信规则见「在途与作废」。 | HTTP 200,返回请求编号;只表示已登记,不代表已落信、更不代表 AODB 已收到。 | 非法 `STYP` 时 HTTP 400,响应体 `unknown-styp``RSTA``RTYP` 时 HTTP 400,响应体 `missing-rtyp`;非 `RSTA``RTYP` 时 HTTP 400,响应体 `unexpected-rtyp`;已有未结案请求时 HTTP 409,响应体 `open-request-exists`。 | — |
| `GET /all/flights` | 无已定义的请求字段;从 Redis 投影读取当前全部动态航班,不含共享航班,与网页客户端同源(`US-12`)。 | HTTP 200;响应体是裸 JSON 数组(不套旧 `ResponseDto`),元素为日计划 `SCHD.FLTR` 转成的 JSON,与 `KAFKA:schd` 数组元素同形(`C-9``C-11`);共享航班(`MAID` 非空)不出现在数组里;不分页。 | HTTP 503;JSON 对象 `{"error":"FLIGHT_PROJECTION_UNAVAILABLE","reason":"<细节>"}`;Redis 或投影读失败时不得返回 200 空数组(`INV-11`)。 | — |
旧系统线索(来源:旧项目用户故事「HTTP 接口清单」「日计划请求」):
@@ -21,7 +22,7 @@
-`GET /all/flights``ResponseDto``body` 是非共享航班的 `SCHD.FLTR` 列表(对象即日计划 XML 解码后的 `FLTR`,再序列化为 JSON);新版成功体直接返回该列表对应的裸 JSON 数组(`C-11`),元素形状不变。
- `POST /schd/sync` 的请求体是 `{startDate, endDate}`,时间格式 `yyyy-MM-dd hh:mm` 用无 AM/PM 的 12 小时制,解析结果写进 `RQFD``STDB`/`STDE``ddMMMyyHHmm`,大写)。
旧系统 `{startDate, endDate}` `RQFD` `STDB`/`STDE` 均不沿用:新版日计划是 AODB 当前时刻的完整航班列表(`US-07`),出站编码已定(`C-4`),不带日期筛选`POST /schd/sync` 请求与响应口径见 `Q16`。旧系统响应体是否能作为 `POST /cminmsgs/send` 的定稿样例,见 `Q15`
旧系统只把 `startDate``endDate` 写成 `STDB``STDE`,时间格式是无 AM/PM 的 12 小时制,新版不沿用这套入参。新版由网页选择时间条件,四个筛选都可传,格式与含义见 `C-4``POST /schd/sync` 请求与响应口径见 `Q16`。旧系统响应体是否能作为 `POST /cminmsgs/send` 的定稿样例,见 `Q15`
## 入站报文与请求应答
@@ -40,7 +41,7 @@ AODB 经 CIIMS adapter 把 XML 报文写入 `CMINMSGS`,格式以架构指定
| 主题 | 已确定的消息语义 | 尚需确定 |
|---|---|---|
| `msg` | 单条航班变更通知;Kafka value 是整条 `MSG` 的 JSON`META` 加对应业务体,空字段不输出),变更与删除由 `META` 的类型与子类型区分(`Q5`);航班动态与删除处理完成后投递;发送失败自动重试,一直失败的记录保留可查并告警(`US-08`);同一 `FLID` 的变更保序,对外按至少一次投递(`CLM-3`,单分区)。删除通知的来源有三处:`FDEL` 删除(`US-06`)、日计划快照覆盖范围内缺席删除(`US-07`)、历史清理在物理删除前必要时登记(架构 `D1`)。 | 编码方式(JSON 之外的压缩或封装是否引入)。 |
| `msg` | 单条航班变更通知;Kafka value 是整条 `MSG` 的 JSON`META` 加对应业务体,空字段不输出),变更与删除由 `META` 的类型与子类型区分(`Q5`);航班动态与删除处理完成后投递;发送失败自动重试,一直失败的记录保留可查并告警(`US-08`);同一 `FLID` 的变更保序,对外按至少一次投递(`CLM-3`,单分区)。删除通知的来源有三处:`FDEL` 删除(`US-06`)、日计划完整名单覆盖范围内缺席删除(`US-07`)、历史清理在物理删除前必要时登记(架构 `D1`)。 | 编码方式(JSON 之外的压缩或封装是否引入)。 |
| `schd` | 定时批量发送最新航班状态;两次 tick 之间积累的航班组成 `SCHD.FLTR` 数组 JSON,整批作为单条 record 发出,条数不设上限(沿用旧系统,`Q1`);空字段不输出,字段与类型见 [XSD](../legacy/unisysaodbsis.xsd) 的 `FLTR`;没有变化不发;删除航班不进入本主题,由 `msg` 发一条删除通知;发送失败自动重试,对外按至少一次投递(`US-08`)。 | — |
旧系统线索(来源:旧项目用户故事「前端通知」「动态类(FLOP-*)处理」):
@@ -80,7 +81,7 @@ AODB 经 CIIMS adapter 把 XML 报文写入 `CMINMSGS`,格式以架构指定
旧系统线索(来源:旧项目用户故事「Redis key 汇总」「术语与数据语义」「动态航班转历史」):
- 投影是 hash `flightInfo`field 为 `FLID`value 为完整 `SCHD.FLTR` 对象的带类型 JSON,不设过期;Redis 里没有名为 `schd` 的 key`schd` 只是 Kafka 主题。新版 value 仍是 `FLTR` 转 JSON,但不使用旧系统的 Jackson 默认类型标注。
- 写入路径:日计划下载(`DNLD``RESP`整体写入当天航班,单条变更(`ADFT``FLOP`)只写对应的一条,转历史时按 `FLID` 逐条移除。整体写入不删除本次映射中缺席的航班,与 `US-07` AC5 相反,新版按 `US-07` AC5 在覆盖范围内刷新
- 写入路径:日计划下载(`DNLD``RESP`写入报文里的航班,单条变更(`ADFT``FLOP`)只写对应的一条,转历史时按 `FLID` 逐条移除。旧系统整体写入不删除本次映射中缺席的航班新版按 `US-07` AC5:完整名单在覆盖范围内移除缺席航班;带了时间条件的 `RESP` 只替换回信里的航班,不因缺席移除其他航班
- 写入前生成主航班的共享航班列表 `MAFL`,共享航班不单列、随主航班下发(`Q6`);旧系统在日计划下载时按 `MAID` 把共享航班挂进主航班 `MAFL`,登机桥字段 `abdg` 本版不提供——旧系统拼它的数据源是机位与登机桥映射缓存,已列入需求「范围与非目标」不交付。
### 自有 PostgreSQL:内部存储与 admin-api 只读
+3 -3
View File
@@ -195,7 +195,7 @@ LIMIT PARAM:msgx.pipeline.backfill-batch
1. 已成功 → 幂等,只追加留痕。
2. 整包校验失败 → `DEAD(PROTOCOL)`,不写半包(`INV-4`)。
3. 分批写:跨运营日整包失败**覆盖范围内**快照缺席航班标删、发删除事件、删 Redis,范围外的不受影响`INV-7`)。每批同事务写变更 + 事件(`INV-3`)。
3. 分批写:跨运营日整包失败。报文里的航班整份替换。完整名单才在覆盖范围内缺席航班标删、发删除事件、删 Redis;带了时间条件的 `RESP` 不因缺席删除`INV-7`)。每批同事务写变更 + 事件(`INV-3`)。
4. 整包成功 → `SUCCEEDED` + 回填意图;留痕在事务外。
字段语义见「航班域」。`RESP` 须匹配开放请求,否则不更新。
@@ -374,7 +374,7 @@ PG 是航班数据源(`INV-5`);Redis 从 PG 同步,处理完成前写入
### 12.1 SCHD
整包校验通过后分批写入;**覆盖范围内**快照缺席标删,范围外的不受影响(`INV-7`)。未带字段清空(`C-6`)。成功航班推进 `STATE_VERSION` 并写 `schd`+`msg` 事件。重复由 `PROC_STATE` 控制;校验失败整包不写(`INV-4`)。
整包校验通过后分批写入。报文里的航班整份替换,未带字段清空(`C-6`)。完整名单才在覆盖范围内把缺席航班标删;带了时间条件的 `RESP` 不因缺席删除(`INV-7`)。成功航班推进 `STATE_VERSION` 并写 `schd`+`msg` 事件。重复由 `PROC_STATE` 控制;校验失败整包不写(`INV-4`)。
### 12.2 动态运行事件(FLOP
@@ -416,7 +416,7 @@ PG 是航班数据源(`INV-5`);Redis 从 PG 同步,处理完成前写入
### 12.3 删除与重建
FDEL`ACTIVE→DELETED`,只登记 `KAFKA:msg` 变更通知(`C-9`;不再写 `schd` tombstone)。物理删除仅历史清理成功后(`US-14``D1`)。覆盖范围内快照缺席也标删(`INV-7`)。
FDEL`ACTIVE→DELETED`,只登记 `KAFKA:msg` 变更通知(`C-9`;不再写 `schd` tombstone)。物理删除仅历史清理成功后(`US-14``D1`)。完整名单在覆盖范围内缺席也标删(`INV-7`)。
ADFTSet-only`US-04` AC2),未带字段不清。有 `SODT` 则算 `OPERATION_DAY`
+5 -5
View File
@@ -90,7 +90,7 @@
### US-07 导入日计划(DNLD / RESP
**目标**:日计划是 AODB 在某个时间范围内的完整航班列表:AODB 主动下发(DNLD)或本系统请求后应答(RESP),收到后分批同步本地数据;整包成功时本地航班当前态与快照一致——请求日计划就是主动与 AODB 全量同步一次。覆盖范围由报文自身给出:`DNLD` 覆盖下发时刻起约 48 小时`RESP` 覆盖请求的日期区间(`SIS:3.16`
**目标**:日计划报文里的每一班,用这份记录整份替换本地同一班,报文没带的字段清掉。机场主动下发(`DNLD`)是下发时刻起约 48 小时的完整名单。本系统请求后的应答(`RESP`)是否为完整名单,取决于请求有没有带时间条件(`C-4`)。四个条件都没传时,应答是当天全部航班,与机场主动下发的日计划同一范围。带了任一时间条件时,应答只包含这一部分航班,不是完整名单
| 报文 | 说明 |
|---|---|
@@ -100,10 +100,10 @@
**验收标准**
1. 报文整体校验(声明的航班数、航班标识、覆盖范围等)通过才处理;校验失败整包拒绝,本地数据不变。
2. 报文里的航班逐条写入或更新;**覆盖范围内**快照里没有的航班,在本地标记已删除,并登记待发删除消息。覆盖范围外的航班不受本报文影响:前一日延误航班不在 `DNLD` 窗口内,不因缺席被判为已删除。
3. 以 AODB 下发的数据为准:日计划里某航班没携带的字段视为 AODB 已删除该值,本地同步清掉。
2. 报文里的航班逐条整份替换本地同一班。只有完整名单才把范围内有、报文里没有的航班标为已删除,并登记待发删除消息。完整名单是 `DNLD`,以及没带时间条件的 `RESP`。带了时间条件的 `RESP` 不删除报文里没有的航班。完整名单范围外的航班不受本报文影响:前一日延误航班不在 `DNLD` 窗口内,不因缺席被判为已删除。
3. 以 AODB 下发的数据为准:出现在报文里的航班没携带的字段视为 AODB 已删除该值,本地同步清掉。
4. 航班量大,分批写入数据库,每批一个事务;处理失败不标记已处理,下轮整包重新处理。
5. 按快照结果刷新 Redis:报文里的航班写入**覆盖范围内**缺席航班移除,覆盖范围外的投影保留。
5. 按快照结果刷新 Redis:报文里的航班写入。完整名单在覆盖范围内移除缺席航班范围外的投影保留。带了时间条件的 `RESP` 不因缺席移除其他航班。
### US-08 通知网页客户端(Kafka
@@ -117,7 +117,7 @@
### US-09 向 AODB 请求数据
**目标**:本系统可以主动向 AODB 要数据:14 类参考数据(`RQRD`+ 1 类日计划(`RQFD`),子类型以消息接口规范为准。参考数据请求由人工发起日计划请求由 `POST /schd/sync` 发。
**目标**:本系统可以主动向 AODB 要数据:14 类参考数据(`RQRD`+ 1 类日计划(`RQFD`),子类型以消息接口规范为准。参考数据请求由人工发起日计划请求由网页选定时间条件后,经 `POST /schd/sync`出(`C-4``C-8`
**验收标准**
+9 -8
View File
@@ -47,7 +47,7 @@
### 2.2 上游(AODB / SIS
- **C-3** 一条报文的身份 = `SNDR` + `TYPE` + `STYP` + `SEQN``SEQN` 自增,极少在消息服务器重启时重置;重置后不与历史冲突。
- **C-4** 出站请求写入 `COUTMSGS`;本系统只保证写入信箱,不保证 AODB 收到。编码:`SNDR=OMMS``SEQN` 本系统生成;`DTTM` 北京时间 `YYYYMMDDHHMMSS``RQRD` 共 14 种子类型(以 SIS 为准);`RQFD``STYP=NONE`,全量不带 `STDB`/`STDE` 筛选。季度计划不属本系统出站范围(`Q25`)。
- **C-4** 出站请求写入 `COUTMSGS`;本系统只保证写入信箱,不保证 AODB 收到。编码:`SNDR=OMMS``SEQN` 本系统生成;`DTTM` 北京时间 `YYYYMMDDHHMMSS``RQRD` 共 14 种子类型(以 SIS 为准);`RQFD``STYP=NONE`。日计划请求的时间条件由网页传入,本系统不补、不改。`STDB``STDE` 按计划到港或计划离港时间筛,`ETDB``ETDE` 按预计到港或预计离港时间筛;带 `B` 的是大于等于该时刻,带 `E` 的是小于等于该时刻;多个条件同时成立。时刻格式为 `DDMONYYHHMM`(见 [SIS](legacy/SIS_AODB_RMS-V0.1.md)「RMS 日航班计划请求事件」)。没传的条件不写入报文。四个都不传时不带筛选,AODB 返回当天全部记录,与它主动下发的日计划同一范围,这份回信是完整名单。带了任一时间条件时,回信只是筛选出来的一部分,不是完整名单。回信里的每一班整份替换本地同一班(`C-6``US-07` AC3)。只有完整名单才删除范围内缺席的航班;带了时间条件时不删除回信里没有的航班(`US-07` AC2。季度计划不属本系统出站范围(`Q25`)。
- **C-5** 删航班只打删除标记;主航班与共享航班各自独立标记。
- **C-6** 日计划快照里没带的字段,视为 AODB 已删掉该值,本地也清掉(`US-07` AC3)。
@@ -55,7 +55,8 @@
- **C-7** `POST /cminmsgs/send`:XML 写进入站信箱,与 adapter 走同一套处理。成功返回消息编号(只表示已写入、尚未处理);失败不返回编号。接受 `text/xml``application/xml``text/plain`(UTF-8);拒收空报文、超长、非法 XML;解析禁止访问外部资源。内网访问由网络层控制;仅供内部联调。
- 待确认:大小上限、HTTP 状态码与响应体 → `Q15`
- **C-8** `POST /schd/sync`:登记一次 `RQFD` 日计划请求。请求体为空、请求全量(`C-4`;成功返回请求编号,已有未结案请求时返回 `409``Q16`)。同类型若还有未处理完的请求,新请求先不写信箱,等旧的处理完再写 `COUTMSGS``US-09` AC1)。
- **C-8** `POST /schd/sync`:登记一次 `RQFD` 日计划请求。请求体是网页选定的时间条件,写入规则见 `C-4`;成功返回请求编号,已有未结案请求时返回 `409``Q16`)。同类型若还有未处理完的请求,新请求先不写信箱,等旧的处理完再写 `COUTMSGS``US-09` AC1)。
- **C-12** `POST /refdata/sync`:登记一次 `RQRD` 参考数据请求。请求体携带网页选定的类别码 `STYP`(取值见 `C-4`,以 SIS 为准);`STYP=RSTA` 时须带资源类型 `RTYP`,其余类别不得带;成功返回请求编号,已有未结案请求时返回 `409``Q17`)。登记后等在途请求结案再写 `COUTMSGS``US-09` AC1)。
### 2.4 下游(admin-api、网页客户端)
@@ -84,7 +85,7 @@
- **INV-5** 航班当前数据以自有 PG 为准。
- **INV-6** `FLID` 全局唯一。
- **INV-7** 日计划**覆盖范围内**快照里没有的航班打删除标记、记删除事件、从 Redis 删掉;覆盖范围外的航班不受本报文影响;快照没带的字段本地清掉`US-07` AC2/AC3)。
- **INV-7** 日计划报文里的航班整份替换,没带的字段清掉(`US-07` AC3、`C-6`)。完整名单(`DNLD`,以及没带时间条件的 `RESP`)在覆盖范围内快照里没有的航班打删除标记、记删除事件、从 Redis 删掉;范围外的航班不受本报文影响。带了时间条件的 `RESP` 不因缺席删除`US-07` AC2)。
- **INV-8** 已打删除标记的航班必须从 Redis 删掉;删掉才算这条消息处理完成(`US-06` AC1)。
- **INV-9** 日计划可以分批写 PG,但整份 PG 写完且 Redis 按快照刷完才算完成;失败则全部重来(`US-07` AC4/AC5)。
@@ -126,8 +127,8 @@
| Q13 | 已定 | `RQRD` / `RQFD` 编码字段 | `C-4` |
| Q14 | 本系统 | 生产用 PostgreSQL 还是 Oracle 11g | Oracle 未验证前不作支持承诺 |
| Q15 | 本系统 | `POST /cminmsgs/send` HTTP 约定 | 大小上限、状态码、最终响应体;见 `C-7`。旧系统成功响应是 `ResponseDto``err_code=1``body` 为信箱编号),且未配置大小上限 |
| Q16 | 已定 | `POST /schd/sync` 请求与响应 | 请求体为空、请求全量`C-4`);成功返回请求编号,开放请求未结案返回 `409``C-8` |
| Q17 | 本系统 | 人工发 `RQRD` 的入口 | `US-09` 要求能人工发,HTTP 清单里没有;旧系统也没有 |
| Q16 | 已定 | `POST /schd/sync` 请求与响应 | 请求体携带网页选定的时间条件`C-4`);成功返回请求编号,开放请求未结案返回 `409``C-8` |
| Q17 | 已定 | 人工发 `RQRD` 的入口 | `POST /refdata/sync` 登记一次请求;请求体携带类别码 `STYP``RSTA` 时加带 `RTYP`);成功返回请求编号,开放请求未结案返回 `409``C-12` |
| Q18 | 已定 | 回退时正在处理的消息怎么办 | 未写回处理标记的消息仍算未处理,由旧系统继续;旧系统停机只等在途任务跑完(`OPS-4` |
| Q19 | — | (未分配) | — |
| Q20 | — | (未分配) | — |
@@ -151,8 +152,7 @@
| `G-FLOP-UNMAPPED` | [XSD](legacy/unisysaodbsis.xsd) FLOP 字段映射不全 | `US-05` |
| `G-MAFL` | 主航班共享列表未做 | `US-06` AC2 |
| `G-REF-DATA` | admin-api 生产侧只读接入与联调验收未闭合 | `US-13`;本网关落库与 `ReferenceDataProcessor` 已做(ACM2-93 |
| `G-REQ-OPEN-UNIQUE` | 新请求等待在途请求结案的登记模型未闭合;当前开放态唯一约束会拒绝新登记 | `US-09` |
| `G-REQ-TRACK` | `RQFD` 跟踪已落地;14 类 `RQRD` 人工登记、子类型作废与等待投递未做 | `US-09` |
| `G-REQ-TRACK` | `RQRD` 人工登记已落地(`C-12`);子类型作废与等待投递未做 | `US-09` |
| `G-REQ-TRACK-RETENTION` | `REQ_TRACK` 已结案保留期未定(`Q24` | `US-09` |
| `G-SRVT-VIPF` | `SRVT``VIPF` 缺席是否清除待 `Q2`;段出现时已落 `FLIGHT_SRVT`/`FLIGHT_VIPF` | `US-05` |
@@ -165,6 +165,7 @@
| 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-12、CLM-4 | `US-09` AC1AC3 | `RQRD` 按类别登记并写入 `COUTMSGS`;非法类别或开放槽占用时拒绝 |
| C-9、CLM-3 | `US-08` AC1AC3 | `msg` 单条、`schd` 批量;失败可重试;`msg``FLID` 保序;同条可能发多次 |
| C-10 | `US-13` AC1~AC4 | 全量替换、增删改逐条;空值表示无值不是删除;单类校验失败只停该类 |
| INV-1(建立处理记录) | `US-01` AC1/AC2/AC4 | 重扫与重启后记录数不变、行不丢;同一编号重复出现时不新增记录 |
@@ -173,7 +174,7 @@
| INV-4 | `US-07` AC1 | 校验失败后 PG 航班数据不变 |
| INV-5 | 架构「数据归属与一致性」 | 航班数据只写入自有 PG |
| INV-6 | implementation.md「数据模型」 | `FLID` 唯一 |
| INV-7 | `US-07` AC2/AC3 | 覆盖范围内快照缺席航班标删并从 Redis 删,范围外不动;未带字段清空 |
| INV-7 | `US-07` AC2/AC3 | 报文里的航班整份替换、未带字段清空;只有完整名单才在覆盖范围内缺席航班标删并从 Redis 删 |
| INV-8 | `US-06` AC1 | 标删后从 Redis 删;失败下轮重做 |
| INV-9 | `US-07` AC4/AC5 | 分批失败全部重来;PG 整份写完后再刷 Redis |
| INV-11 | `US-12` AC1/AC2 | 返回全部非共享航班且与 Redis 一致;Redis 故障返回错误 |