From 50e0e7f389e876c898c9440b30bea40ef724ce29 Mon Sep 17 00:00:00 2001 From: windyboy Date: Thu, 10 Sep 2026 20:54:01 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BF=AE=E5=A4=8D=E8=B7=A8=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E5=86=B2=E7=AA=81=E4=B8=8E=E5=A4=96=E9=83=A8=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 不动代码、配置与迁移,只收敛文档口径。 - 登记两项外部冲突:Q13 日计划缺失可选字段(SIS §3.16 注释 4 要求删除本地值, 与现行"未携带字段保留"相反)、Q14 主/共享删除顺序与 EROR 回报义务 (SIS §1.6.1-1.d / §4.8);航班级"缺席不删"补 SIS §3.16 注释 1 依据。 - flight-state §3.3 补主/共享关联按 FLID 值比较的规则。 - message-lifecycle:§11 水位不变量与 §5.1 空洞老化自洽;删除过期的 updatedAt 判据,统一为 PROCESSING_STARTED_AT + 可注入 Clock;§5.1/§6 补 SIS 480 分钟 Expiry 与 legacy 1 天保留窗,作为 Q2/Q6/Q7 的取值参照。 - architecture/design:明细表口径统一为 8 张资源明细表 + FLIGHT_ROUTE_POINT; Oracle 11g 断言收敛为待适配验证;配置键按 application.yml 实际名称加 msgx. 前缀。 - user-stories:Q2/Q6/Q11 补外部锚点并收窄 Q11 到重置周期 + SNDR 值域; 新增 Q13/Q14 并挂入 US-05/06/08 前置;五个独立事实改为引用 message-lifecycle §1。 - README/legacy:文档基线改为代码基线 eea1120 + git log 指引;补"外部协议冲突以 SIS 为准"的维护规则;修正 /all/flights 过滤措辞;术语统一为「回填意图」。 验证:文档间章节引用 0 缺失、markdown 0 断链、SIS 引用小节全部存在、 Q1–Q14 正文与 Q 表双向可解析、代码块配对正常。 --- docs/README.md | 4 +-- docs/architecture.md | 4 +-- docs/design.md | 33 ++++++++--------- docs/flight-state.md | 16 ++++++--- .../msgexchange-api-legacy-user-stories.md | 5 +-- docs/message-lifecycle.md | 35 +++++++++++-------- docs/user-stories.md | 26 +++++++------- 7 files changed, 70 insertions(+), 53 deletions(-) diff --git a/docs/README.md b/docs/README.md index 9c59fba..f6aafeb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ # 设计文档入口 -当前实现核对日期:2026-09-10(代码基线 99a0f5f;文档基线 328bbf8)。文档中的目标能力不等于已实现;测试通过不等于现场已发布。 +当前实现核对日期:2026-09-10(代码基线 `eea1120`)。文档哈希不在此行回填,以 `git log -- docs/` 为准。文档中的目标能力不等于已实现;测试通过不等于现场已发布。 | 文档 | 唯一职责 | |---|---| @@ -13,4 +13,4 @@ | [legacy/decision-flight-state-history.md](legacy/decision-flight-state-history.md) | 已撤销方案的存档说明;现行规则仍以 `flight-state.md` 为准。 | | [SIS 规范](legacy/SIS_AODB_RMS-V0.1.md) / [XSD](legacy/unisysaodbsis.xsd) | 外部协议事实;即使在 legacy 目录仍是兼容依据。 | -维护规则:架构写约束,设计写机制,规范(`flight-state.md`、`message-lifecycle.md`)写唯一规则,故事写目标,Plane 跟踪工作。不要在多份文档复制阶段清单;同一规则只允许一处定义,其他文档引用章节号。 +维护规则:架构写约束,设计写机制,规范(`flight-state.md`、`message-lifecycle.md`)写唯一规则,故事写目标,Plane 跟踪工作。不要在多份文档复制阶段清单;同一规则只允许一处定义,其他文档引用章节号。外部协议事实([SIS 规范](legacy/SIS_AODB_RMS-V0.1.md)、[XSD](legacy/unisysaodbsis.xsd))与现行文档冲突时以 SIS 为准;legacy 行为基线只描述现役行为。 diff --git a/docs/architecture.md b/docs/architecture.md index 54fccbf..37da428 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -76,7 +76,7 @@ CIIMS / AODB 等上游 ## 5. 必须保持的约束 - **消息严格 FIFO**:队头失败并退避时,后续消息仍不能越过它。只有队头完成或按失败策略进入终态后,队列才继续推进。收报重扫和水位设计必须防止较小 ID 漏入队而被后续消息越过。 -- **动态状态单写者**:`FLIGHT_SCHD` 及明细表只由主泵单线程写入。事务内第一步对 `PIPELINE_LOCK` 单行 `SELECT ... FOR UPDATE` 互斥;该方案在 PG/Oracle 11g 均无需数据库扩展或额外 DBA 特权。不能通过增加实例或处理线程直接扩容。 +- **动态状态单写者**:`FLIGHT_SCHD` 及明细表只由主泵单线程写入。事务内第一步对 `PIPELINE_LOCK` 单行 `SELECT ... FOR UPDATE` 互斥;该方案不依赖数据库扩展或额外 DBA 特权;Oracle 11g 待适配时验证。不能通过增加实例或处理线程直接扩容。 - **身份去重**:同一业务身份只能绑定一条有效处理记录,重复报文不应再次产生业务副作用。具体身份组成和重放规则见设计文档。 - **快照可恢复**:快照以 `PROC_STATE` 成功终态判定重放;每次成功写入推进 `STATE_VERSION`;`OPERATION_DAY` 一经确定不可变(见 flight-state.md §2.1)。 - **物理清除只发生在历史归档**:删除一律先标记(FDEL)或由生命周期清除;历史存储未接通时必须删 0 条(见 flight-state.md §6)。 @@ -87,7 +87,7 @@ CIIMS / AODB 等上游 | 存储 | 承载内容 | 职责说明 | |---|---|---| -| 自有 PostgreSQL | 单行锁 `PIPELINE_LOCK`、处理状态与回填事实 `PROC_STATE`、消费水位 `INBOX_CURSOR`、待发事件 `MSG_EVENT`、请求跟踪 `REQ_TRACK`、航班当前态 `FLIGHT_SCHD` + 9 张明细表、留痕 `SCHD_SNAP_LOG` | 本系统唯一业务数据库。消息处理、状态推进、处理终态、回填意图与待发事件在单事务内原子提交;本地事务只在此库。 | +| 自有 PostgreSQL | 单行锁 `PIPELINE_LOCK`、处理状态与回填事实 `PROC_STATE`、消费水位 `INBOX_CURSOR`、待发事件 `MSG_EVENT`、请求跟踪 `REQ_TRACK`、航班当前态 `FLIGHT_SCHD` + 8 张资源明细表 + `FLIGHT_ROUTE_POINT`、留痕 `SCHD_SNAP_LOG` | 本系统唯一业务数据库。消息处理、状态推进、处理终态、回填意图与待发事件在单事务内原子提交;本地事务只在此库。 | | 共享 MySQL | `CMINMSGS` 入站信箱、`COUTMSGS` 出站信箱 | 外部系统所有。本系统仅执行约定的信箱读写与处理标记回填,不建表、不迁移 schema、不写历史表;由库方按 Q9 执行的清除与历史归档见 message-lifecycle.md §6。兼容 HTTP 入口可按既有契约写入入站信箱。 | **不使用跨库事务。** PG 事务只能保证“处理结果与待发事件一起提交”,不能覆盖 MySQL 回填或 Kafka 发送等外部副作用。跨存储依靠幂等、重试和持久化补偿恢复;各中断位置的判定与恢复动作统一见 [message-lifecycle.md](message-lifecycle.md) §4,本文不重复。 diff --git a/docs/design.md b/docs/design.md index 6c6cf14..9e01680 100644 --- a/docs/design.md +++ b/docs/design.md @@ -21,7 +21,7 @@ | `MSG_EVENT` | 等待投递的事件(outbox) | `EVENT_ID` 决定投递顺序;`TARGET` 区分 `KAFKA:msg` / `KAFKA:schd`;`PARTITION_KEY` 恒为 `FLID`;`EVENT_TYPE` 区分 UPSERT 与 TOMBSTONE。 | | `REQ_TRACK` | 上游请求及应答关联 | 保存请求类型、覆盖运营日、发送方、出站信箱 ID 与发送/完成时间;同类只允许一个开放请求。登记、超时与应答匹配尚未实现(§10)。 | | `REF_MASTER` | 静态参考数据(目标表) | `(RTYPE, RKEY)` 唯一;尚未建表,客户端与刷新流程见 user-stories.md US-13/US-14(US-14 两类映射的存储落点未定)。 | -| `FLIGHT_SCHD` | 航班标量及单值异常字段 | `FLID` 主键;`OPERATION_DAY` 一经确定不可变;版本与最近消息 ID 用于追踪。变长资源集合存于 9 张明细表,规则见 [flight-state.md](flight-state.md) §2,不在此重复。 | +| `FLIGHT_SCHD` | 航班标量及单值异常字段 | `FLID` 主键;`OPERATION_DAY` 一经确定不可变;版本与最近消息 ID 用于追踪。变长资源集合存于 8 张资源明细表与 `FLIGHT_ROUTE_POINT`,规则见 [flight-state.md](flight-state.md) §2,不在此重复。 | | `INBOX_CURSOR` | 共享信箱消费水位 `W` | 单行游标;只随新 ID 成功入队推进,遇空洞即停,空洞超期判定为永久([message-lifecycle.md](message-lifecycle.md) §5.1)。 | | `PROC_STATE_HST` | 终态处理记录的归档目标 | 尚未建表;不得改写为共享库历史表。 | @@ -37,7 +37,7 @@ SNDR | TYPE | STYP | SEQN ``` -接收时只按信箱 ID 去重;解码后才首次绑定业务身份。重试保留原有绑定,不能把自己判为重复消息。身份被另一条记录占用时,当前消息转为 `SKIPPED`,记录 `duplicate-of:`。是否加入日期边界取决于上游序号重置规则,默认关闭;上线后不能随意更换身份算法。 +接收时只按信箱 ID 去重;解码后才首次绑定业务身份。重试保留原有绑定,不能把自己判为重复消息。身份被另一条记录占用时,当前消息转为 `SKIPPED`,记录 `duplicate-of:`。是否加入日期边界取决于上游序号重置周期,默认关闭(`SEQN` 的取值范围与回绕已由 `SIS_AODB_RMS-V0.1.md` §2.8.1 定义,重置周期见 Q11);上线后不能随意更换身份算法。 分派与落库由 `MessageProcessor` 统一协调:按 `MsgKind` 把已绑定身份的队头消息交给对应事务协调器(DNLD/RESP → `ScheduleProcessor`,ADFT → `AdftProcessor`,FLOP → `FlopProcessor`,FDEL → `FdelProcessor`,其余 → `FAILED(UNSUPPORTED)`)。这些 Processor 在 `PIPELINE_LOCK` 事务内读取当前完整态,调用纯领域决策逻辑得到下一完整态与待发事件,再统一落库并登记回填意图;它们不直接触碰 Kafka。处理终态与业务变更在同一事务边界提交(见 flight-state.md §4)。 @@ -45,7 +45,7 @@ SNDR | TYPE | STYP | SEQN ```text 处理:PENDING / FAILED → SUCCEEDED(成功) - → SKIPPED(业务重复;忽略/无匹配分支见 US-04/US-06,尚未实现) + → SKIPPED(业务重复已实现;忽略/无匹配分支见 US-04/US-06,尚未实现) → FAILED(等待退避重试) → DEAD(MALFORMED / PROTOCOL / EXHAUSTED,均需人工处置) @@ -71,7 +71,7 @@ SNDR | TYPE | STYP | SEQN `InboxPoller` 默认每秒按 ID 升序、有限批次(`claim-batch`,默认 50)读取水位之后的信箱记录(`ID > W`,**不以处理标记为谓词**),在自有 PG 建立 `PENDING` 并把水位推进到连续上界;入队与水位推进在同一 PG 事务内提交,重复扫描幂等、中断后重扫补建。收报层不解析业务载荷,也不回填已处理标记。 -水位 W 与扫描谓词的完整口径(连续上界、遇空洞即停、空洞老化)唯一见 [message-lifecycle.md](message-lifecycle.md) §5.1;水位不是已处理标记,其有效性以 Q2 的 ID 单调承诺为前提。空洞老化阈值取 `pipeline.max-commit-delay`。 +水位 W 与扫描谓词的完整口径(连续上界、遇空洞即停、空洞老化)唯一见 [message-lifecycle.md](message-lifecycle.md) §5.1;水位不是已处理标记,其有效性以 Q2 的 ID 单调承诺为前提。空洞老化阈值取 `msgx.pipeline.max-commit-delay`。 兼容 HTTP 入口执行“写入共享信箱 → PG 入队”。两步不在同一事务中:信箱成功而 PG 失败时,原文不能丢失,由轮询补建;客户端失败重试可能再次写信箱,业务身份去重仍然必需。 @@ -94,7 +94,7 @@ SNDR | TYPE | STYP | SEQN DNLD / RESP → ScheduleProcessor(快照事务) ADFT / FLOP / FDEL → Adft / Flop / FdelProcessor(单航班事务) Unsupported → FAILED(UNSUPPORTED) - PG 单事务:锁 + 航班变更 + 待发事件 + 终态 + 回填待办预登记 + PG 单事务:锁 + 航班变更 + 待发事件 + 终态 + 回填意图预登记 → 提交后回填信箱;失败由补偿待办重试 ``` @@ -112,7 +112,7 @@ SNDR | TYPE | STYP | SEQN 1. **重放判定**:`PROC_STATE` 已存在成功终态 → 幂等成功,仅追加留痕,不重复写入。 2. **整包校验**:声明记录数、航班标识与运营日推导等校验失败 → 整包 `DEAD(PROTOCOL)`,不写半包,既有状态保持不变。 3. **事务写入**:锁内按 `FLID` 点查归属日,发现同一航班跨运营日即整包回滚并 `DEAD(PROTOCOL)`;通过后合并写主表与资源明细。报文未携带的航班不因本次日计划报文被删除。 -4. **提交结果**:同一事务保存 `KAFKA:schd` / `KAFKA:msg` 事件、置消息 `SUCCEEDED` 并预登记回填待办;事务提交后执行信箱回填,留痕在事务外追加。 +4. **提交结果**:同一事务保存 `KAFKA:schd` / `KAFKA:msg` 事件、置消息 `SUCCEEDED` 并预登记回填意图;事务提交后执行信箱回填,留痕在事务外追加。 单事务保证未提交变更整体回滚。消息重放由 `PROC_STATE` 的消息 ID 与业务身份控制;版本号不能单独证明消息身份。 @@ -133,7 +133,7 @@ REGISTERED → SENT → WAITING → DONE - 应答优先按已确认的回显字段精确匹配;回显契约未确认时的降级匹配(同类开放请求且 `DTTM ≥ sentAt`)存在跨代误配风险,必须明确接受并审计,不能宣称精确关联。比较前统一时区和时间单位。 - 参考应答写入自有 `REF_MASTER`(尚未建表),日计划应答走快照流程;请求完成必须在相应数据处理成功之后,超时和迟到应答不能修改已关闭请求对应的状态。 -请求与参考数据的交付范围见 user-stories.md US-08/US-13/US-14 与 §10。 +请求与参考数据的交付范围见 user-stories.md US-08/US-13/US-14 与本文 §10。 ## 5. 事件投递 @@ -167,7 +167,7 @@ REGISTERED → SENT → WAITING → DONE ### 6.2 历史清理与归档 -**航班历史清理**(`HistorySweepJob`,每天 03:30 触发):按 `HistoryProps` 的保留期与终态/静默判据选出候选(含 `DELETED`),先写历史存储,成功后物理删除主行与明细;历史存储未接通或 `history-store-enabled=false` 时删除 0 条。未经 FDEL、由生命周期直接清除的航班,清除前补发一次删除事件。语义与红线见 flight-state.md §6,不在此重复。 +**航班历史清理**(`HistorySweepJob`,每天 03:30 触发):按 `HistoryProps` 的保留期与终态/静默判据选出候选(含 `DELETED`),先写历史存储,成功后物理删除主行与明细;历史存储未接通或 `msgx.history.history-store-enabled=false` 时删除 0 条。未经 FDEL、由生命周期直接清除的航班,清除前补发一次删除事件。语义与红线见 flight-state.md §6,不在此重复。 **留痕清理**:`SCHD_SNAP_LOG` 保留 90 天,在历史清理窗口内按 `(SCOPE_END, RECV_AT)` 删除。 @@ -179,15 +179,16 @@ REGISTERED → SENT → WAITING → DONE 兼容入口为 `POST /cminmsgs/send`,请求体为原始 XML,当前成功响应为 HTTP 200、`text/plain` 格式的信箱记录 ID。这只表示接收结果,不表示业务处理成功。请求媒体类型、字符集和失败响应仍需与现役逐项对拍;查询与其他兼容端点不能因列入需求就视为已提供。 -运行配置以 `application.yml`、`application-dev.yml` 和 `.env.example` 为准,设计上重点区分: +运行配置以 `application.yml`、`application-dev.yml` 和 `.env.example` 为准,设计上重点区分(下列 `msgx.*` 参数以 `msgx.` 为前缀,`mailbox.*` 不带前缀): -- `pipeline.autostart` 与 `msgx.stubs`:分别控制管道启动与内存适配器;生产禁止 stub,默认不自动启动。 -- `pipeline.poll-interval / claim-batch / max-attempts / backoff / head-deadline`:控制轮询节奏、批次、重试上限、退避与队头滞留;这些参数不能改变 FIFO。 -- `pipeline.max-commit-delay / overdue-backfill / backfill-batch`:空洞老化阈值(Q2 最大提交时延)、超期补写期限 R(Q6)与回填扫描批量;R 与老化阈值都不能为提速而下调。 +- `msgx.pipeline.autostart` 与 `msgx.stubs`:分别控制管道启动与内存适配器;生产禁止 stub,默认不自动启动。 +- `msgx.pipeline.poll-interval / claim-batch / max-attempts / backoff-ms / backoff-cap-ms / head-deadline`:控制轮询节奏、批次、重试上限、退避与队头滞留;这些参数不能改变 FIFO。 +- `msgx.pipeline.max-commit-delay / overdue-backfill / backfill-batch`:空洞老化阈值(Q2 最大提交时延)、超期补写期限 R(Q6)与回填扫描批量;R 与老化阈值都不能为提速而下调。 +- `msgx.operation-day.zone / cutoff-hour`:运营日时区与切日边界,决定 `OPERATION_DAY` 推导(flight-state.md §2.1)。 - `mailbox.processed-value`:写回共享信箱的处理标记值,仅限库方认可的 legacy 值集(Q7)。 -- `schd.flush-period / flush-limit`:控制状态通知的聚合延迟与批量大小。 -- `identity.include-day-boundary`:影响去重语义,不能作为普通调优项切换。 -- `mailbox.shared-mysql.enabled` 与 `history.history-store-enabled`:分别门控真实信箱与历史存储接线,默认关闭。 +- `msgx.schd.flush-period / flush-limit`:控制状态通知的聚合延迟与批量大小。 +- `msgx.identity.include-day-boundary`:影响去重语义,不能作为普通调优项切换;序号重置周期见 Q11。 +- `mailbox.shared-mysql.enabled` 与 `msgx.history.history-store-enabled`:分别门控真实信箱与历史存储接线,默认关闭。 日志关联消息 ID、事件 ID 和批次;失败记录错误分类、次数、下次执行时间。健康检查反映依赖实际可用性;队头滞留、积压、死信和补偿失败需要指标及告警。日志出口故障不得阻塞业务线程。 @@ -227,7 +228,7 @@ REGISTERED → SENT → WAITING → DONE 以下缺口直接影响上述设计是否成立,不能以类或接口已存在作为完成依据: -- **事务与外部副作用**:状态、事件、处理终态与回填意图同 PG 事务提交;回填意图落在 `PROC_STATE`(`BACKFILL_AT/NEXT_AT/ATTEMPTS/ERROR`),`BACKFILL_TODO` 已随 V2 迁移下线,[message-lifecycle.md](message-lifecycle.md) §4 的两个崩溃窗口(提交后回填前崩溃、待办二次落账失败)不再存在。回填本身仍是跨库单写,失败按退避重试并由超期期限 R 兜底;R 的取值待 Q6 确认。 +- **事务与外部副作用**:状态、事件、处理终态与回填意图同 PG 事务提交;回填意图落在 `PROC_STATE`(`BACKFILL_AT/NEXT_AT/ATTEMPTS/ERROR`),`BACKFILL_TODO` 已随 V2 迁移下线,[message-lifecycle.md](message-lifecycle.md) §4 的两个崩溃窗口(提交后回填前崩溃、回填意图二次落账失败)不再存在。回填本身仍是跨库单写,失败按退避重试并由超期期限 R 兜底;R 的取值待 Q6 确认。 - **收报与调度**:水位 W 落库并与入队同事务推进,遇空洞即停、空洞超过 `max-commit-delay` 判定为永久(Q2 未书面确认前该阈值是假定值);旧空洞补齐后出现的新空洞会重置老化起点。较小 ID 迟提交与空洞场景的端到端顺序保证仍待与库方联合验证。HOL deadline 已改用 `PROCESSING_STARTED_AT` 和可注入 `Clock`;Q6 仍需确认长期积压与人工重放的期限口径。 - **快照与业务能力**:DNLD/RESP/ADFT 与 FLOP/FDEL 处理器、整包校验与跨运营日整包拒绝均已接入;但 RESP 应答守卫与出站请求未实现,忽略规则(US-04)未实现,29 类 FLOP 与参考应答的逐类矩阵未补全,ADFT 缺失字段与 `FLID` 重用语义待上游确认。 - **航班读写**:唯一写入口与权威读已落地;ROUT/ERUT 联合主键、空值/未知属性保真、事件在事务内只算一次、逐航班多次查询仍待修正(见 flight-state.md §6)。`/all/flights` 尚未实现。 diff --git a/docs/flight-state.md b/docs/flight-state.md index 355ac2f..b216784 100644 --- a/docs/flight-state.md +++ b/docs/flight-state.md @@ -9,7 +9,7 @@ - `FLID` 是航班实例的唯一标识;不得由航班号、日期或资源号推断身份。 - `FLIGHT_SCHD` 及其明细表是唯一权威当前态;展示视图只读,不能作为写入或对账来源。 - 单活动主泵按信箱 FIFO 推进。事务内 `PIPELINE_LOCK` 只串行化本地状态提交,不替代选主或消息认领。 -- 状态写入、outbox 事件、处理终态和回填待办在同一 PostgreSQL 事务中提交;回填与 Kafka 投递在提交后独立重试。 +- 状态写入、outbox 事件、处理终态和回填意图在同一 PostgreSQL 事务中提交;回填与 Kafka 投递在提交后独立重试。 现场目标库为 Oracle 11g;当前已验证的实现基准是 PostgreSQL。Oracle 适配未完成前,不能把它视为可切换的运行时选项。 @@ -46,10 +46,12 @@ ### 3.1 SCHD 日计划 -SCHD DNLD/RESP 在整包校验通过后,逐条将报文携带的航班写入当前态。日计划只更新或创建其携带的 `FLID`,**不会因其他航班未出现在本次报文中而删除任何记录**。 +SCHD DNLD/RESP 在整包校验通过后,逐条将报文携带的航班写入当前态。日计划只更新或创建其携带的 `FLID`,**不会因其他航班未出现在本次报文中而删除任何记录**;SIS 同向(§3.16 注释 1 要求子系统自行保留前一日延误航班)。 日计划在重叠字段上可以覆盖当前动态值;未携带的字段按合并规则保留,显式清空才清除。每个成功写入的航班推进 `STATE_VERSION`,并在同一事务登记 `KAFKA:schd` 与 `KAFKA:msg` 事件。 +**字段缺失语义与外部规范冲突**:SIS 规定最新日计划中未发送的可选字段表示 AODB 已无该数据、子系统应删除本地已有值(`SIS_AODB_RMS-V0.1.md` §3.16 注释 4;RESP 与 DNLD 同格式,见 §3.17),并要求以 AODB 最新数据覆盖本地(SIS §1.6.2)。这与上面的"未携带字段保留"相反。确认前两条并存,按 Q13 跟踪,不得据本节推定已与上游对齐。 + 消息重放由 `PROC_STATE` 的消息 ID 与 `identity_key` 控制;已成功提交的消息不得再次写入或重复登记事件。整包校验失败或运营日冲突时,整包不落地。 DNLD、RESP 和 ADFT 均已有路由入口。RESP 的请求匹配闭环、出站请求与超时重发仍是待交付项;未注册处理器不能被视为已实现。 @@ -68,7 +70,9 @@ FDEL 是业务删除入口:仅在 `ACTIVE → DELETED` 时推进版本、保 ADFT 的字段缺失语义尚待上游确认。在确认前采用保守的 Set-only 规则:出现字段可更新,缺失字段不清空;不得把它当成日计划或动态全量替换。新建 ADFT 若带可解析的 `SODT`,按同一运营日规则计算 `OPERATION_DAY`;否则保留为 `NULL`。 -主/共享航班级联:删除共享航班时更新主航班 `MAFL` 并向主航班通知;删除主航班时级联删除其子共享关联并发出删除通知;主/共享关系必须一次原子变更,不出现主已删、子残留的半状态。共享航班增量通常只更新并通知主航班,不直接发共享通知。这些语义同样约束 FDEL 之外的生命周期清理。 +主/共享航班级联:删除共享航班时更新主航班 `MAFL` 并向主航班通知;删除主航班时级联删除其子共享关联并发出删除通知;主/共享关系必须一次原子变更,不出现主已删、子残留的半状态。共享航班增量通常只更新并通知主航班,不直接发共享通知。这些语义同样约束 FDEL 之外的生命周期清理。主/共享关联的增删按 `FLID` 做值比较,不使用引用比较。 + +SIS 规定删除主航班时必须先删子共享航班、再删主航班,顺序不符时 RMS 应向 AODB 回发 EROR(`SIS_AODB_RMS-V0.1.md` §1.6.1-1.d,事件定义见 SIS §4.8)。本文的原子级联不发该回报,两者取舍见 Q14。 ## 4. 处理事务与失败规则 @@ -76,7 +80,7 @@ ADFT 的字段缺失语义尚待上游确认。在确认前采用保守的 Set-o 1. 主泵只处理 FIFO 队头,解码并绑定业务身份。 2. 校验消息结构、声明数量、航班标识和运营日;协议错误标记 `DEAD`,不提交半包。 -3. 在一个事务中取得 `PIPELINE_LOCK`,读取当前完整态,计算下一状态,写主表和明细表,登记 outbox 和回填待办。 +3. 在一个事务中取得 `PIPELINE_LOCK`,读取当前完整态,计算下一状态,写主表和明细表,登记 outbox 和回填意图。 4. 同一事务提交处理终态;任一步失败则整体回滚并按错误类别重试或终止。 5. 提交后回填共享信箱并异步投递 outbox;后续失败不得把已提交的 `SUCCEEDED` 改回失败。 @@ -99,6 +103,8 @@ ADFT 的字段缺失语义尚待上游确认。在确认前采用保守的 Set-o - Oracle 11g 的完整方言与集成验证; - RESP 请求—应答匹配和出站请求重发; - ADFT 缺失字段和 `FLID` 重用的上游语义; +- 日计划缺失可选字段的删除语义(与 SIS 的冲突见 §3.1,Q13); +- 主/共享删除顺序与 EROR 回报义务(Q14); - 未归属 `OPERATION_DAY` 航班的终止与保留策略; - ROUT/ERUT 联合主键迁移; - 批量一致性读取,以及动态事件仅在事务内计算一次的收敛。 @@ -109,6 +115,6 @@ ADFT 的字段缺失语义尚待上游确认。在确认前采用保守的 Set-o - 同一时刻只有一个主泵推进 FIFO 状态;状态、事件、终态和回填意图原子提交。 - 每个航班每次成功状态写入单调推进 `STATE_VERSION`;重复消息不重复推进。 - `FLID` 唯一,已确定的 `OPERATION_DAY` 不可改变。 -- 报文未携带的字段不会被隐式清空;集合按完整合并结果写入。 +- 报文未携带的字段不被隐式清空(与 SIS §3.16 的冲突见 §3.1 与 Q13);集合按完整合并结果写入。 - 缺席于某个日计划不构成删除理由;删除只由 FDEL 或受控历史清理触发。 - 外部副作用失败可重试,不回滚已提交的本地业务结果。 diff --git a/docs/legacy/msgexchange-api-legacy-user-stories.md b/docs/legacy/msgexchange-api-legacy-user-stories.md index 4b40b81..72a45ac 100644 --- a/docs/legacy/msgexchange-api-legacy-user-stories.md +++ b/docs/legacy/msgexchange-api-legacy-user-stories.md @@ -10,6 +10,7 @@ - 本文档记录 msgexchange-api(现役旧项目)的完整业务逻辑,并以用户故事形式组织,作为新系统(Micronaut + Kotlin)迁移设计及一致性对拍的行为基线。 - 内容依据仓库源码与资源配置编写:`src/main/java/com/gzzn/omms/msgexchange-api/**`、`src/main/resources/*.yml`;分支 `feature/add_dev_containers_supports`(HEAD `eb72a668`)。 - 报文结构依据 `SIS_AODB_RMS-V0.1.md` 与 `unisysaodbsis.xsd`(同目录 docs/legacy/)。 +- 优先级:本文只描述现役行为;与 `SIS_AODB_RMS-V0.1.md` 冲突时以 SIS 为准。 - 文档中"关联代码"均为相对仓库根目录的源码路径,可据此逐条复核。 ## 1. 系统定位与集成边界 @@ -160,7 +161,7 @@ msgexchange-api 为成都机场 OMMS 消息交换服务(`com.gzzn.omms:msgexch ### 3.8 HTTP 查询与运维接口 -- `GET /all/flights`:读取 Redis 全部动态航班,过滤非共享航班(`MAID == NULL`)后返回 `ResponseDto>`。 +- `GET /all/flights`:读取 Redis 全部动态航班,过滤掉共享航班(`MAID != NULL`)后返回 `ResponseDto>`。 - `GET /flights/migrate`:scroll 读取 ES `flight_hts` 全量数据,经 `transToNewFltrs` 还原 `SCHD.FLTR`(将 `ABN[].BODY` 按类型 DLY / CAN / RTN / ALT 映射回对象字段),逐条写入 ES `flight_hts2`。该接口为一次性数据迁移工具。 - `POST /kafka/topics/{name}/msgs`:请求体先按 JSON(Map)解析,成功则直接发送至 Kafka 主题 `{name}`;解析失败则按 XML 解析为 `MSG` 再转 JSON 发送。该接口提供 XML / JSON 双模转发能力。 - `POST /schd/sync`:见 3.9。 @@ -368,7 +369,7 @@ msgexchange-api 为成都机场 OMMS 消息交换服务(`com.gzzn.omms:msgexch - 角色 / 期望 / 目标:作为前端或调用方,期望获取当前全量动态航班(不含共享航班),以便页面渲染。 - 触发条件:`GET /all/flights`。 -- 处理流程:`findAll`(Redis hmget)→ 过滤 `MAID == NULL` → `ResponseDto.success`。 +- 处理流程:`findAll`(Redis hmget)→ 过滤掉共享航班(`MAID != NULL`)→ `ResponseDto.success`。 - 验收标准:返回结果不含共享航班记录,内容与 Redis `flightInfo` 一致。 - 关联代码:`controller/FlightsController.java`、`service/flightInfo/FlightInfoServiceImpl.java`。 diff --git a/docs/message-lifecycle.md b/docs/message-lifecycle.md index 4ee3e3c..d467f88 100644 --- a/docs/message-lifecycle.md +++ b/docs/message-lifecycle.md @@ -52,7 +52,7 @@ MSG_EVENT PENDING ──→ SENT | 收报 | ID 区间内尚未入队的信箱行(谓词见 §5.1) | `PROC_STATE(PENDING)` | `MSG_ID` 主键 | 下轮扫描补入队 | | 取队头 | 最小未完成 `MSG_ID` | 本批处理消息 | 表状态即队列 | 无队头则等待 | | 解码与身份 | 信箱原文 | `DecodedMessage`、`IDENTITY_KEY` | 身份唯一约束 | 报文非法→`DEAD`;处理能力不足→`FAILED` 退避 | -| 事务处理 | 当前完整状态 + 报文载荷 | 航班变更、终态、事件、回填待办 | 消息 ID + 业务身份 | 事务整体回滚重试 | +| 事务处理 | 当前完整状态 + 报文载荷 | 航班变更、终态、事件、回填意图 | 消息 ID + 业务身份 | 事务整体回滚重试 | | 提交后回填 | 终态消息 | 信箱处理标记 | 标记单调(§11) | 终态事务内登记回填意图;失败退避重试,超期强制补写 | | 投递 | `MSG_EVENT` | 投递目标接受确认 | `EVENT_ID` | 退避至 `DEAD`;至少一次 | | 补偿作业 | 到期或已达 `NOW − R` 的终态记录 | 追平标记 | 消息 ID(仅空标生效) | 指数退避,独立线程执行;`R` 覆盖退避 | @@ -68,14 +68,14 @@ MSG_EVENT PENDING ──→ SENT | 已落信、未入队 | 信箱行位于应扫描的 ID 范围且 PG 无记录(不以处理标记为判据) | 重扫补建入队记录 | | 事务执行中 | PG 无该消息终态 | 事务整体回滚,按 `PENDING` 重新处理 | | 事务已提交、标记未写 | 终态行仍持有回填意图(`BACKFILL_NEXT_AT` 非空) | 仅补写标记;业务处理结果保持不变 | -| 回填待办登记失败(与业务同事务) | 事务未提交 | 同"事务执行中",不构成独立窗口 | +| 回填意图登记失败(与业务同事务) | 事务未提交 | 同"事务执行中",不构成独立窗口 | | 标记写入中途 | 标记仍为空 | 重新写入;重复写入同一值无副作用 | | 投递目标已接受、`SENT` 未置 | 事件仍 `PENDING` | 允许重发,消费方按事件身份去重 | 两个窗口已随"终态与回填意图同体同行"消除(口径与 design.md §10「事务与外部副作用」一致),不再是缺口: 1. **提交后回填前崩溃**:回填意图与处理终态是同一条记录的同一次写入、同一个事务;重启后扫描按该意图继续补写。 -2. **回填待办二次落账失败**:不存在第二处落账——意图就在终态行上。 +2. **回填意图二次落账失败**:不存在第二处落账——意图就在终态行上。 仍属未闭环的是回填本身在跨库单写期间的持续失败:由退避重试与 §5.2 的超期期限 `R` 兜底(登记于 design.md §10 与 user-stories.md US-09)。 @@ -90,9 +90,11 @@ MSG_EVENT PENDING ──→ SENT 1. **ID 单调**:信箱 ID 按提交顺序分配,晚提交的较小 ID 不参与。承诺缺失时,`W` 只能作为快路径的提示,不能证明该区间收齐。 2. **最大提交时延**:上游提交到 ID 可见的最长时间。补偿扫描窗口(`NOW − 最大提交时延`)的宽度由此确定。 +取值可参照 SIS:报文在 CIIMS 的 `Expiry` 为 480 分钟,断连 120–480 分钟按 Level 2 处理,CIIMS 保证报文在成功接收或过期前按序保存(`SIS_AODB_RMS-V0.1.md` §2.4.2.3.3、§3.16、§3.17、§5.2)。 + 日常执行方式:快路径从 `ID > W` 起按升序有限批次扫描;另按周期对窗口内可能迟到或空洞的行做补偿扫描。需要区分:水位表示"读取进度",与"已处理标记"是两个事实,不能互相替代。 -**空洞老化**:自增回滚等会在 ID 序列中留下永久空位,而 Q2 只承诺 ID 单调、不承诺无空洞。W+1 处的空洞持续超过最大提交时延仍未被补齐时,即判定为永久空洞并放行水位(实现取 `pipeline.max-commit-delay`);放行只跳过空洞本身,不越过任何已存在的行。没有这条规则,水位会永久停摆于第一个空位,其后的行再也不会入队。 +**空洞老化**:自增回滚等会在 ID 序列中留下永久空位,而 Q2 只承诺 ID 单调、不承诺无空洞。W+1 处的空洞持续超过最大提交时延仍未被补齐时,即判定为永久空洞并放行水位(实现取 `msgx.pipeline.max-commit-delay`);放行只跳过空洞本身,不越过任何已存在的行。没有这条规则,水位会永久停摆于第一个空位,其后的行再也不会入队。 两条扫描路径都按 **ID 区间**取行,不以处理标记为扫描谓词;标记只用于回填与库方清除,不参与消息发现。否则已入队但尚未回填的行会永久占据批次,这正是 US-01 条目 3 与 Q2 要求排除的场景。 @@ -100,7 +102,7 @@ MSG_EVENT PENDING ──→ SENT 所有处理终态都只依赖消息 ID 执行回填;报文残缺或缺少 META 不妨碍正常回填。本节规定回填通道长期失败时的超期兜底,避免终态信箱行无限期保持空标记。处理规则为: -**已达 `PROC_STATE` 终态、且接收时间超期(`DATE_RECEIVED < NOW − R`)仍无标记的信箱行,由回填通道补写一个库方认可的"已处理"类标记。** +**已达 `PROC_STATE` 终态、且接收时间超期(信箱 `DATE_RECEIVED`,落库为 `PROC_STATE.RECEIVED_AT`;判据 `RECEIVED_AT < NOW − R`)仍无标记的信箱行,由回填通道补写一个库方认可的"已处理"类标记。** - 期限 `R` 必须不小于(人工重放期限 + 人工处置期限)之和;两项期限的取值口径统一见 Q6,确认前不得下调 `R`。提前补写会使仍可重放的消息先被库方清除、失去原件资格。 - 中间态(`PENDING` / `FAILED`)不适用本规则:处理未完成时不打标,也不允许被清除。 @@ -126,8 +128,8 @@ MSG_EVENT PENDING ──→ SENT **第三步:等待过程中的四个边界。** - 不插队:主泵按 FIFO 消化,积压期间到达的实时消息排在积压之后。本设计不允许并行队头,也不允许实时通道跳过积压。 -- 不失控:队头滞留上限(head-deadline,当前 10 分钟)对积压同样生效;队头长期失败按既有规则转 `DEAD(EXHAUSTED)` 并告警,不会因积压而延长容忍。在 Q6 收敛前,该上限的实际判据仍是 `updatedAt` 与系统取时(见 design.md §10),本条款是目标口径。 -- 报文类型的处理方式不变:旧的全量日计划(`SCHD-DNLD`)按最新一份**合并**即可收敛(缺席不删、缺失字段保留,见 flight-state.md §3.1),但仍逐条执行;动态增量(`FLOP-*` / `FDEL` / `ADFT`)持有时序语义,必须逐条。 +- 不失控:队头滞留上限(`msgx.pipeline.head-deadline`,当前 10 分钟)对积压同样生效;队头长期失败按既有规则转 `DEAD(EXHAUSTED)` 并告警,不会因积压而延长容忍。判据以首次处理时写入的 `PROCESSING_STARTED_AT` 为稳定起点、经可注入 `Clock` 判定(design.md §3.2、§10);长期积压与人工重放的期限口径仍由 Q6 定案。 +- 报文类型的处理方式不变:旧的全量日计划(`SCHD-DNLD`)按最新一份**合并**即可收敛(缺席不删、缺失字段保留;字段级冲突见 flight-state.md §3.1 与 Q13),但仍逐条执行;动态增量(`FLOP-*` / `FDEL` / `ADFT`)持有时序语义,必须逐条。 - 可放弃但必须留痕:摸底批中经库方确认"不再处理"的行,处置方式为——`PROC_STATE` 置 `SKIPPED` 并记录跳过原因,到达终态后走 §5.2 通道补写标记;本方案不支持任何"整段 DELETE"的快速通道。 **验收口径**:积压消化期间持续输出三项指标——剩余积压条数、最老未处理信龄、预计消化时长;期间不允许出现 FIFO 越序、身份去重失效或头行滞留超时未告警。红线依据:architecture.md §5(消息严格 FIFO)、单写者约束。三项指标并入 user-stories.md OPS-2 的可观测性验收。 @@ -163,6 +165,7 @@ MSG_EVENT PENDING ──→ SENT - 执行清除时,边界内不存在未打标记的行;未达终态的行顺延至处理完成后清除(§5.2 仅对终态行补标); - 时间比较与换算统一采用机场时区 Asia/Shanghai 及明确的类型转换(口径同 user-stories.md §6); - 清除保留期 `R_keep` 不小于 max(回填重试上限、重放期限、审计期限);且 §5.2 的 `R` 不大于 `R_keep`,否则尚在重放窗口内的消息会先于重放被清除; +- legacy 现役按接收时间超过 1 天即归档并删除 `CMINMSGS`(legacy 行为基线 §3.6);若沿用该窗口,则与上一行的保留期下限冲突,须在 Q9 中与库方一并确认; - 信箱 ID 全程不断链:方案 A 天然满足;方案 B 依赖 `AUTO_INCREMENT` 种子,种子缺失时新 ID 与旧记录主键冲突,水位随之失效。 ## 7. 出站信箱(COUTMSGS) @@ -171,6 +174,8 @@ MSG_EVENT PENDING ──→ SENT 出站前在 `REQ_TRACK` 登记 `REGISTERED`;写入 `COUTMSGS` 并确认落信后置 `SENT` 并关联出站记录 ID。"落信"指写入信箱成功,不等于下游已读取或已发送,交付承诺仅到落信为止(US-08)。 +主/共享删除顺序违例时 SIS 要求向 AODB 回发 EROR(`SIS_AODB_RMS-V0.1.md` §1.6.1-1.d,事件定义见 SIS §4.8);当前没有该出站路径,取舍见 Q14。 + 本系统写入的行由谁消费、按什么顺序消费,**当前未知**。以下事项在 Q10 关闭前按未知处理,不得作为已具备能力描述: - 消费方与消费顺序; @@ -178,7 +183,7 @@ MSG_EVENT PENDING ──→ SENT - 出站行的清除责任与保留期; - 落信成功但本地未置 `SENT` 时的重复写入风险及下游去重契约。 -参考事实:legacy 的 SIS 接口规范记载各系统持有各自的 `COUTMSGS` 副本,由 JDBC Adapter 读取后发送至 CIIMS。该机制是否在现场沿用,属于 Q10 的确认范围,不作为现状依据。 +参考事实:legacy 的 SIS 接口规范记载各系统持有各自的 `COUTMSGS` 副本,由 JDBC Adapter 读取后发送至 CIIMS;同时记载从 RMS 发出的报文不执行 CIIMS 确认(`SIS_AODB_RMS-V0.1.md` §2.4.2.2.2、§2.4.2.2.4)。这两点是否在现场沿用、与 `COUTMSGS_ACK_*` 列的关系如何,均属 Q10 的确认范围,不作为现状依据。 ## 8. 自有记录归档 @@ -200,19 +205,21 @@ MSG_EVENT PENDING ──→ SENT | Q6 | 重放 deadline 与人工处置期限的取值(决定 §5.2 的 `R`) | §5.2、§9 | | Q7 | 处理标记值集与写权限;原文保留期;处理时间语义 | §5.2、§6 | | Q8 | 逐类覆盖清单(积压摸底的类型分布依据) | §5.3 | -| Q9(新增) | 清除执行方与 DDL 授权;方案 A / B 选型 | §6 | -| Q10(新增) | 出站消费方、ACK 列语义、出站清理与去重契约 | §7 | -| Q11(新增) | 上游 `SEQN` 重置规则与业务身份的日期边界 | design.md §2.2 | -| Q12(新增) | 历史积压批次“不再处理”的确认主体、审批留痕与跳过值集 | §5.3 | +| Q9 | 清除执行方与 DDL 授权;方案 A / B 选型 | §6 | +| Q10 | 出站消费方、ACK 列语义、出站清理与去重契约 | §7 | +| Q11 | 上游 `SEQN` 重置周期与业务身份的日期边界 | design.md §2.2 | +| Q12 | 历史积压批次“不再处理”的确认主体、审批留痕与跳过值集 | §5.3 | +| Q13 | 日计划缺失可选字段的删除语义(与 SIS §3.16 注释 4 冲突) | §5.3、flight-state.md §3.1 | +| Q14 | 主/共享删除顺序与 EROR 回报义务 | §7、flight-state.md §3.3 | -Q6–Q12 的完整登记见 [user-stories.md](user-stories.md) §6 的 Q 表。 +Q2、Q6–Q14 的完整登记见 [user-stories.md](user-stories.md) §6 的 Q 表。 ## 11. 不变量 - 五个事实互不替代:入队不引用信箱标记,回填不引用投递,投递不引用回填。 - 处理终态不可逆:已提交的 `SUCCEEDED` 不因回填或投递失败回改。 - 处理标记单调:任何路径只将空标写为已处理,不回撤、不覆盖。 -- 水位不越过未入队的 ID;遇空洞即停。 +- 水位不越过任何已存在的行;遇空洞即停,只有 §5.1 判定为永久空洞时才放行。 - 执行清除前,边界内全部行已持有处理标记;物理删除仅发生在归档成功之后(追加写入与分区留档均构成归档成功)。 - 信箱 ID 全局单调、不断链:以 Q2 的 ID 单调承诺与 §6 各方案前提成立为条件(方案 B 依赖 `AUTO_INCREMENT` 种子)。 - 对外投递按至少一次设计;端到端恰好一次不在交付范围内。 diff --git a/docs/user-stories.md b/docs/user-stories.md index fbc24e7..4d95b17 100644 --- a/docs/user-stories.md +++ b/docs/user-stories.md @@ -6,7 +6,7 @@ - 系统边界见 [architecture.md](architecture.md),模块流程见 [design.md](design.md)。本文不重复设计全文,也不以工单状态代替代码验收。消息生命周期与信箱清除见 [message-lifecycle.md](message-lifecycle.md),航班规则见 [flight-state.md](flight-state.md)。 - “当前基础”来自本轮代码核对,只表示有接口或部分实现,不表示故事完成。`KEEP` 是保留业务兼容,`FIX` 是明确修正旧缺陷,`DEFERRED` 不进入阶段 A。 -- “信箱落信、PG 入队、业务处理完成、信箱回填、目标端投递确认”是五个独立事实,接口、日志和测试必须分开表达。投递确认不等于业务消费者已消费。 +- 五个独立事实(落信、入队、处理完成、回填、投递确认)的定义与判定见 [message-lifecycle.md](message-lifecycle.md) §1;接口、日志和测试必须分开表达,投递确认不等于业务消费者已消费。 - 所有内部迁移只落自有 PG;共享 MySQL 不建表、不增列、不写历史表。本文用 `DATE_PROCESSED / STATUS` 表示逻辑字段,实际列名以库方契约为准。 - 验收条目可按 `US-xx/条目号` 引用。故事较大时按下文子范围拆成小 PR,不把一个故事等同于一个提交。 @@ -42,7 +42,7 @@ 4. PG 不可用或批次中途失败时不改信箱标记;恢复后补建遗漏,记录失败次数与扫描进度。 5. 较小 ID 迟提交、ID 有空洞、兼容入口先入队较大 ID 时,必须遵守经 Q2 确认的发现与顺序协议;不能用“最终会重扫”冒充严格 FIFO。 -**当前基础与落点**:`ingress/InboxPoller.kt` 按 ID 区间扫描(`ID > W`,不以处理标记为谓词),水位落 `INBOX_CURSOR` 并与入队同事务推进;`JdbcCminmsgInboxRepository.readRange/maxId` 与 `ProcStateRepository.insertIfAbsent` 承担发现与幂等入队。空洞老化阈值取 `pipeline.max-commit-delay`。剩余:Q2 未书面确认前,老化阈值与严格顺序仍是假定口径;真实 MySQL 的中断恢复与迟提交联合测试待现场环境。 +**当前基础与落点**:`ingress/InboxPoller.kt` 按 ID 区间扫描(`ID > W`,不以处理标记为谓词),水位落 `INBOX_CURSOR` 并与入队同事务推进;`JdbcCminmsgInboxRepository.readRange/maxId` 与 `ProcStateRepository.insertIfAbsent` 承担发现与幂等入队。空洞老化阈值取 `msgx.pipeline.max-commit-delay`。剩余:Q2 未书面确认前,老化阈值与严格顺序仍是假定口径;真实 MySQL 的中断恢复与迟提交联合测试待现场环境。 **前置**:共享库读契约;Q2 决定严格顺序的端到端验收。水位与扫描谓词口径以 [message-lifecycle.md](message-lifecycle.md) §5.1 为准。 @@ -80,12 +80,12 @@ 2. 安全解码 XML,至少覆盖 META、SCHD、FLOP、参考应答与忽略类路由;合法但能力未支持是 `UNSUPPORTED`,不能一律归为非法报文。保留原文以支持诊断和回放。 3. 解码后首次绑定 `SNDR|TYPE|STYP|SEQN`;冲突转 `SKIPPED` 并记录原 ID;自身重试保留绑定。生产 `include-day-boundary=false`,更改算法须另行评审上游序号规则。 4. `MALFORMED` 直接 `DEAD`;`CODEC_ERROR / UNSUPPORTED / INFRA` 按次数和退避处理,耗尽转 `DEAD(EXHAUSTED)`。不能无限重试未实现类型,也不能立即当非法报文丢弃。 -5. HOL deadline 必须基于稳定起始时刻(必要时在 `PROC_STATE` 增加入队时间列),不能用每次重试刷新的 `updatedAt` 代替;调度判断注入 `Clock`。默认 5 次重试、10 分钟滞留限制;积压与人工重放的 deadline 边界按 Q6 验证。 +5. HOL deadline 必须基于稳定起始时刻(`PROC_STATE.PROCESSING_STARTED_AT`),不能用每次重试刷新的 `updatedAt` 代替;调度判断注入 `Clock`。默认 5 次重试、10 分钟滞留限制;积压与人工重放的 deadline 边界按 Q6 验证。 6. 主泵在同一 PG 事务提交航班主表/明细、事件与处理结果;终态回填意图通过 US-09 同事务保存。任一步失败整体回滚;提交后只重试外部回填,不重复生成业务事件。 7. 领域决策逻辑只读取当前完整态与已解码报文,返回下一完整态和待发事件,不执行 I/O;Processor 作为事务协调器,将状态写入、事件、处理终态与回填意图收敛在同一事务边界内,不直接触碰 Kafka。失败只在持有消息上下文的边界落账,中断向上传递,不作为普通失败吞掉。 8. 权威存储不可用或未完成恢复时停止业务处理;不能把“整个状态丢失”误判为“单航班不存在”而批量成功结束增量报文。 -**当前基础与落点**:`processing/Pump.kt`(含 `MessageProcessor`)、`DynamicProcessors.kt`、`Identity.kt`、`codec/JacksonXmlCodec.kt`、`infra/retry/`。严格 FIFO 主泵、SCHD(DNLD/RESP/ADFT)/FLOP/FDEL 处理器、PG 单事务(含回填待办预登记)、身份绑定与重试已实现;调度取时由可注入 `Clock` 提供,HOL deadline 以 `PROCESSING_STARTED_AT` 为稳定起点。尚未完成:忽略规则分支(US-04),以及逐类矩阵与 golden 样例(US-05)。长期积压和人工重放如何计算 deadline 仍由 Q6 定案。 +**当前基础与落点**:`processing/Pump.kt`(含 `MessageProcessor`)、`DynamicProcessors.kt`、`Identity.kt`、`codec/JacksonXmlCodec.kt`、`infra/retry/`。严格 FIFO 主泵、SCHD(DNLD/RESP/ADFT)/FLOP/FDEL 处理器、PG 单事务(含回填意图预登记)、身份绑定与重试已实现;调度取时由可注入 `Clock` 提供,HOL deadline 以 `PROCESSING_STARTED_AT` 为稳定起点。尚未完成:忽略规则分支(US-04),以及逐类矩阵与 golden 样例(US-05)。长期积压和人工重放如何计算 deadline 仍由 Q6 定案。 **前置**:US-01;Q1 已定单库方向,Q6 决定 deadline 边界。数据库迁移只落自有库。 @@ -118,7 +118,7 @@ **当前基础与落点**:落点已变为 `processing/DynamicProcessors.kt`(`FlopProcessor`/`FdelProcessor`/`AdftProcessor`)与 `domain/flight/FlightStateEngine`、`FlightStateRepository`。FDEL/ADFT 与通用 FLOP 处理器已接入(含 DELETED 幂等与重激活、tombstone 同事务登记);29 类逐类语义矩阵与 golden 样例仍未补全,不能因处理器存在就视为覆盖完成。 -**前置**:US-03;PSDT 另依赖 US-14;Q1、Q8。 +**前置**:US-03;PSDT 另依赖 US-14;Q1、Q8、Q14。 ### US-06 导入 RESP/DNLD 日计划快照 @@ -140,7 +140,7 @@ **当前基础与落点**:DNLD 与 RESP 已共同路由到 `processing/ScheduleProcessor.applyScheduleRecords`,整包校验、归属日冲突整包拒绝与单事务写入已实现;`REQ_TRACK` 表与 `ReqTrackRepository` 已建。仍需补 RESP 应答守卫(开放 RQFD 匹配、时间比对)与请求完成关联逻辑。 -**前置**:US-03、US-08 请求登记/匹配基础;Q1、Q5。 +**前置**:US-03、US-08 请求登记/匹配基础;Q1、Q5、Q13。 ### US-07 可靠、有序地投递 Kafka @@ -176,7 +176,7 @@ **当前基础与落点**:`ReqTrackRepository` 与 JDBC 实现、`REQ_TRACK` 表已存在;运行时协调器、`COUTMSGS` 出站适配与请求编码未实现(`XmlCodec.encodeRqrd` 仅占位),超时与应答匹配未闭环。需补协调器、出站适配、并发约束、应答路由与故障测试。 -**前置**:US-01、US-03;Q5、Q8、出站信箱去重契约。请求基础不依赖 US-06。 +**前置**:US-01、US-03;Q5、Q8、Q14、出站信箱去重契约。请求基础不依赖 US-06。 ### US-09 持久化补偿回填信箱 @@ -275,7 +275,7 @@ 历史存储确认成功后,才允许删除对应实时航班;逐条隔离坏数据,不能删除写历史失败的集合。判史规则与保留期(`HistoryProps`)、业务时区 `Asia/Shanghai`、历史写入与删除事件之间的恢复协议需在启用前完成 golden 对拍。 -阶段 A 不依赖 ES,不启用 `PROJECTION_REBUILD`。`HistorySweepJob` 已作为每日清理脚手架接入,但历史存储未接通(或 `history-store-enabled=false`)时删除 0 条;脚手架存在不等于清场已交付,红线见 flight-state.md §6。 +阶段 A 不依赖 ES,不启用 `PROJECTION_REBUILD`。`HistorySweepJob` 已作为每日清理脚手架接入,但历史存储未接通(或 `msgx.history.history-store-enabled=false`)时删除 0 条;脚手架存在不等于清场已交付,红线见 flight-state.md §6。 ## 5. 运行与切流验收 @@ -305,17 +305,19 @@ | 编号 | 问题与当前口径 | 解除阻塞的产物 | |---|---|---| | Q1 权威存储(方向已定) | 当前 PG 单库权威,主表 + 无损明细;现场供库目标为 Oracle 11g。 | 单库决策已采纳,Oracle 完整适配与部署验收仍待交付;不得退回 Redis 双写。 | -| Q2 入队顺序 | 空洞老化阈值取自"最大提交时延",但库方尚未书面承诺 ID 单调与提交时延,因此迟提交不越序仍无端到端保证。 | 库方 ID/提交顺序约束,或明确的发现完整性与暂停/恢复协议;晚提交、空洞、兼容入口与重扫联合测试。不能凭空假定 ID 连续。 | +| Q2 入队顺序 | 空洞老化阈值取自"最大提交时延",但库方尚未书面承诺 ID 单调与提交时延,因此迟提交不越序仍无端到端保证。取值可参照 SIS:报文在 CIIMS 的 `Expiry` 为 480 分钟,断连 120–480 分钟按 Level 2 处理,CIIMS 在成功接收或过期前保序保存(SIS §2.4.2.3.3、§3.16、§5.2)。 | 库方 ID/提交顺序约束,或明确的发现完整性与暂停/恢复协议;晚提交、空洞、兼容入口与重扫联合测试。不能凭空假定 ID 连续。 | | Q3 HTTP 契约 | 目标 ResponseDto 与现有 text/plain ID 不同;请求媒体类型目标已列出,错误码、状态码、查询格式等仍需对拍。 | 每个保留接口的真实请求/响应样例、错误表和契约测试;10MB 的字节口径、字符集及兼容变更说明一起固定。 | | Q4 Kafka wire | 当前 msg/schd 均按 `FLID` 逐航班发送(schd 由 `flushSchd` 合并为每个 FLID 的最新状态),与 legacy“多 FLID 数组”形态不同;msg 是否需按 `SNDR` 分区尚未接线。 | 下游确认发送粒度、key、去重标识放置、分区内顺序及批次确认策略;未定案前不改动现役消费契约。 | | Q5 请求匹配 | 目标优先 SEQN 回显,但回显是否可靠需确认;DTTM 降级存在跨代误匹配,尤其旧应答到达新请求期间。 | 15 类请求/响应样例、回显字段与时间格式;降级风险是否接受及拒绝条件。默认超时仍为 RQFD 60 秒、RQRD 30 秒。 | -| Q6 deadline 与重放 | 入队时间作为稳定锚点会把长期排队消息计入滞留;历史重放保留 CREATED_AT 后可能立即过期。 | 明确首次处理/排队过期策略与“本次恢复尝试”计时方式,保留原始时间审计;测试积压恢复和旧 DEAD 重放,不用刷新 UPDATED_AT 绕过超时。 | +| Q6 deadline 与重放 | 入队时间作为稳定锚点会把长期排队消息计入滞留;历史重放保留 CREATED_AT 后可能立即过期。外部参照:SIS 报文 `Expiry` 480 分钟(SIS §3.16),legacy 现役按接收超 1 天归档并删除(legacy 基线 §3.6)。 | 明确首次处理/排队过期策略与“本次恢复尝试”计时方式,保留原始时间审计;测试积压恢复和旧 DEAD 重放,不用刷新 UPDATED_AT 绕过超时。 | | Q7 信箱外部契约 | 原目标为成功→SUCCESS、忽略→SKIPPED、重复→DUPLICATE、死信→DEAD;当前 JDBC 写 PROCESSED。新 STATUS 值尚不能假定库方支持。 | 库方认可的状态值、META/缺失字段、权限、原文保留期、出站去重与处理时间语义;若只允许 legacy 集合,显式映射内部原因,不新增外部枚举。 | | Q8 业务覆盖清单 | “29 FLOP、14 RQRD、21 参考类、2 机位类”只是数量,不能直接当字段规范。 | 将 SIS/XSD、现役 KEEP/FIX 基线整理为逐类矩阵与脱敏样例,列明路由、字段、缺失/清空、通知、数据来源优先级、多桥规则及测试文件。资料不全的类型不标完成。 | | Q9 信箱清除契约 | 清除由谁执行与 DDL 授权、方案 A/B 选型、现场 MySQL 版本与分区 DDL 能力(message-lifecycle.md §6)。 | 库方书面确认清除授权与方案选型。 | -| Q10 出站信箱契约 | `COUTMSGS` 消费方与顺序、`COUTMSGS_ACK_DATE_RECV / COUTMSGS_ACK_RESEND_TIMES / COUTMSGS_DATE_SENT / COUTMSGS_ERROR` 等列语义与写入方、出站清理责任与去重契约(message-lifecycle.md §7;legacy 参考见 SIS 规范 COUTMSGS 机制章节)。 | 库方书面确认出站消费、清理与去重契约。 | -| Q11 上游序号重置 | 上游 `SEQN` 重置规则决定业务身份是否加入日期边界(design.md §2.2,默认关闭)。 | 上游确认重置规则;身份算法变更须另行评审。 | +| Q10 出站信箱契约 | `COUTMSGS` 消费方与顺序、`COUTMSGS_ACK_DATE_RECV / COUTMSGS_ACK_RESEND_TIMES / COUTMSGS_DATE_SENT / COUTMSGS_ERROR` 等列语义与写入方、出站清理责任与去重契约(message-lifecycle.md §7;SIS §2.4.2.2.2、§2.4.2.2.4、§2.4.2.3)。 | 库方书面确认出站消费、清理与去重契约。 | +| Q11 上游序号重置 | `SEQN` 的取值范围与回绕已由 SIS §2.8.1 定义(`Number(6)`、1–999999、由发送方设置),未知的是重置周期;它决定业务身份是否加入日期边界(design.md §2.2,默认关闭)。`SNDR` 取值域也需对拍:SIS 为 AODB/RMS,legacy 实发 OSH5 等。 | 上游确认重置周期与 `SNDR` 值域;身份算法变更须另行评审。 | | Q12 积压跳过授权 | 历史积压批次中“不再处理”的确认主体、审批留痕与跳过值集(message-lifecycle.md §5.3)。 | 业务与库方书面确认该批跳过范围;`PROC_STATE=SKIPPED` 记录原因并保留审计,不允许整段 DELETE。 | +| Q13 日计划字段缺失语义 | SIS 要求最新日计划中未发送的可选字段视为 AODB 已无该数据、子系统应删除本地已有值(SIS §3.16 注释 4,RESP 同格式见 SIS §3.17),与现行"未携带字段保留"相反(flight-state.md §3.1、message-lifecycle.md §5.3)。 | 与 AODB/库方书面确认字段级删除语义;确认前不得按任一方向验收对拍差异。 | +| Q14 主/共享删除顺序与 EROR | SIS 要求删主航班前先删子共享航班,顺序不符时 RMS 向 AODB 回发 EROR(SIS §1.6.1-1.d,事件定义 SIS §4.8)。现行设计为自动原子级联,未定义回报路径(flight-state.md §3.3)。 | 确认上游删除顺序约定,并选定"回发 EROR"或"幂等自动级联";出站事件类型随之纳入 US-08 范围。 | 业务日期/日计划采用 `Asia/Shanghai`;持久化与比较使用明确的时间类型和转换规则,不靠服务器默认时区,也不直接比较不同单位的数字。