docs(acm2-74): 补齐 FLOP 逐类语义、静态参考数据与数据结构

自洽与精简:

- specification 补第四种状态词 [我们自证]、验证映射补 INV-14 两行、去 CLM-7/CLM-9 的进度语、补编号空缺说明
- architecture/requirements/implementation 去除逐字复述(领域决策三写句、时间常数排序、C-25/C-26、INV-12/21/22)与参数值重复
- README 与新文档对齐:收报谓词改 ID>W、删不存在的 reference/ 包与 lua/、删 msgx.phase 与 MSGX_REDIS_URI、设计权威口径改 docs/、进度段改指 Plane

内容补齐:

- 新增 implementation.md「动态运行事件(FLOP)」:按 SIS 3.19–3.43 的 25 类逐类映射与空标签语义(含 CNCL 空标签为撤销等),补 RMS→AODB 方向拒绝、ROUT 只保留 4 条、VIPP 忽略等约束;厘清 legacy 29 类与 SIS 25 类的差集
- 新增 implementation.md「静态参考数据」章:13 类参考数据与资源状态的 RTYPE/RKEY、list 与增量合并语义、删除只由 DEL 表达、SLST/REMT/RSTA 机位与登机桥映射
- §11.3 数据结构改为标量分组表与集合明细表(元素键、条数上限、SIS 锚点)
- specification 新增 G-FLOP-DIRECTION / G-FLOP-UNMAPPED / G-REF-DATA;Q8 收窄为真实报文分布与 admin-api 清单
This commit is contained in:
windyboy
2026-09-14 07:34:17 +08:00
parent cedd859bdf
commit 5b7938ac4b
6 changed files with 206 additions and 98 deletions
+28 -53
View File
@@ -3,67 +3,55 @@
**系统角色**:消费 CIIMS/AODB 等上游经共享 MySQL 信箱(`CMINMSGS`)投递的 XML 报文,
解析处理后维护自有 PostgreSQL 航班当前态并向 Kafka / 出站信箱投递——**中间件**,非报文源系统。
依据 **ACMA-8 v4 综合架构**(单写者严格 FIFO 管道)与 **ACMA-6 技术选型**
Micronaut 5.1 + Kotlin 2.3)搭建;**存储边界与阶段 B 按 ACM2-11/ACM2-12 定案**
(自有 PostgreSQL 维护内部状态,共享 MySQL 仅作信箱边界)。
现行设计依据是 [设计文档入口](docs/README.md) 的六份文档;早期方案 ACMA-8 / ACMA-6 只作历史记录,
不再作为设计或实现依据(自有 PostgreSQL 维护内部状态,共享 MySQL 仅作信箱边界)。
本仓库独立于 legacy `msgexchange-api`Java 8 / Spring Boot 1.5 / Maven)——
过渡期两套系统并存(影子对拍→切流→旧仓库冻结),legacy 维护不受本仓库影响。
> **JDK 口径实测修正**Micronaut 5.1 系构件要求 **JVM 25+**`./gradlew :dependencies` 实测
> core 系解析 **5.1.13**platform BOM **5.1.3**classpath 混用);计划原定 JDK 21 不可行;
> 工程已按 **JDK 25** 配置(ACMA-9 记录)。
> 工程已按 **JDK 25** 配置(工具链约束见 `AGENTS.md`)。
## 系统边界
| 方向 | 机制 | 说明 |
|---|---|---|
| **入站(主路径)** | JDBC 轮询共享 `CMINMSGS` | 上游经 CIIMS 等**外部系统**写信箱;本系统 1s 轮询 `DATE_PROCESSED IS NULL` 发现新信 → 自有 PG 入队(`InboxPoller`U05 |
| **入站(主路径)** | JDBC 轮询共享 `CMINMSGS` | 上游经 CIIMS 等**外部系统**写信箱;本系统按轮询间隔读取水位之后的记录(`ID > W`**不以处理标记为谓词**→ 自有 PG 入队(`InboxPoller` |
| **入站(compat** | HTTP `POST /cminmsgs/send` | 手工注入/影子对拍;写信箱 + PG 入队,**非**生产主拓扑 |
| **处理** | 主泵 FIFO 管道 | 解码 → identity → Handler 决策 → PostgreSQL 航班当前态 |
| **出站** | Kafka + `COUTMSGS` | 向下游推送 msg/schd;请求类报文写出站信箱 |
与 SIS / legacy 一致:本系统**不**替代 CIIMS 落信,**不**生成原始 AODB 业务报文。
## 包结构 → ACMA-8 架构映射
## 包结构(与 [architecture.md](docs/architecture.md)「模块职责」一致)
| 包 | 职责 | 对应 ACMA-8 |
|---|---|---|
| `ingress/` | Ingress & InboxJDBC 轮询共享信箱发现新信 → 自有 PG 入队(+ HTTP compat 写路径),不解析报文 | 流程 1,I3 |
| `processing/` | Processing 主泵:严格 FIFO 领取、identity 绑定、纯函数决策、事务2 | 流程 2/4,I1/I2/I5 |
| `delivery/` | Delivery & Projection:每 target 严格 FIFO 投递、schd 聚合 | 流程 3 |
| `reference/` | Reference & Query21 类同步 + 15 类请求状态机 | 流程 6 |
| `jobs/` | 泵作业:ARCHIVE / HISTORY_SWEEP / PROJECTION_REBUILD | 流程 4/5/7I4 |
| `codec/` | SIS XML codec(注解 wire DTO → 领域载荷,禁 DTD/外部实体) | 决策 4 前置 |
| `domain/` | 领域模型:Decision、事件、状态机枚举、Phase 开关 | I1–I5 |
| `config/` | `PipelineProps` 参数表(ACMA-8 参数初值,`msgx.*` | — |
| `infra/` | 仓储接口、stub、健康、重试策略 | 数据模型节 |
| 包 | 职责 |
|---|---|
| `ingress/` | 轮询共享信箱并持久化入队、兼容 HTTP 写入;不解析业务报文 |
| `processing/` | FIFO 调度、业务身份绑定与去重、领域决策与落库 |
| `delivery/` | 按目标保序投递、`schd` 聚合与失败重试 |
| `jobs/` | 回填补偿扫描、航班历史清理与留痕保留期清理 |
| `codec/` | SIS XML 解码(禁 DTD/外部实体),区分非法报文与可修复的解码失败 |
| `domain/` | 领域状态、事件与决策模型 |
| `config/` | 运行参数(`msgx.*` |
| `infra/` | 仓储(JDBC/stub)、外部适配器、重试、健康与指标 |
## 资源
- `docs/legacy/SIS_AODB_RMS-V0.1.md` + `docs/legacy/unisysaodbsis.xsd`**消息结构唯一事实源**
wire 契约冻结,自 legacy 仓库复制以自包含;codec 实装依据,ACM2-2/ACM2-3)。
- `db/migration/V1__flight_state_baseline.sql`**自有 PostgreSQL**(唯一自有库)的航班状态与处理管道表结构;
- `src/main/resources/db/migration/V1__flight_state_baseline.sql`**自有 PostgreSQL**(唯一自有库)的航班状态与处理管道表结构;
共享 MySQLCMINMSGS/COUTMSGS)仅信箱 DML,不建表。
- `lua/batch_delete.lua`:3:30 清场批量删除(仅 ES 写成功集,I4)。
- `application.yml`:口令全部环境变量外置(零入库);`datasources.default`=自有 PGenabled=false
待 U05)、`mailbox.shared-mysql`=共享信箱;`msgx.phase` 权威口径(A 现役;B 缓做);
pipeline 参数 = ACMA-8 参数表初值。
- `application.yml`:口令全部环境变量外置(零入库);`datasources.default`=自有 PG(默认关闭),
`mailbox.shared-mysql`=共享信箱;运行参数默认值一律以 [reference.md](docs/reference.md) 参数表为准。
## 未完成(按计划属于后续阶段,不是本脚手架遗漏)
## 实现进度
1. **Handler 业务(3+29**`processing/HandlerRegistry` 仅注册骨架,翻译属阶段 2/3
2. **依赖版本锁定**`gradle/libs.versions.toml` 中版本为计划口径,需阶段 0
「Micronaut×现网 Eureka 互操作冒烟 + logstash + ES REST」通过后固化。
3. **自有 PG 数据层 + InboxPoller**ACM2-12):`infra/persistence/Repositories.kt` 目前是接口
Micronaut Data JDBC on PG + 信箱适配层 CminmsgMailbox/OutboxMailbox + JDBC 轮询入队
属 U05 批次),主泵/调度循环以接口驱动,纯逻辑已抽离可单测。
4. **信箱适配层与共享库边界**ACM2-12):上游外部写 `CMINMSGS`;本系统 JDBC 轮询读 +
处理回填写;出站写 `COUTMSGS`mailbox.shared-mysql 配置段已占位);入队/回填的
外部副作用/补偿模型属 U05 批次。
进度、缺口处置与排期在 Plane(ACM2);设计口径、偏差与可声明性见 [docs/specification.md](docs/specification.md)。本 README 不记录进度
## 数据库初始化(ACM2-12 口径)
**自有 PostgreSQL**(唯一自有库):Flyway 执行 `db/migration/V1__flight_state_baseline.sql`,建立
**自有 PostgreSQL**(唯一自有库):Flyway 执行 `src/main/resources/db/migration/V1__flight_state_baseline.sql`,建立
航班当前态、明细表、处理终态与 outbox 等表(PG 方言)。这是**单基线**:原 V2–V10 的净结构已
合并其中,全新库直接执行即可,无 legacy 前置。已按旧链(V1–V10)迁移过的库版本链与校验和都
对不上,必须重建 schema 或删除数据卷后重跑,禁止手工 `repair` 或改写 `flyway_schema_history`
@@ -83,8 +71,8 @@
仓库根目录提供兼容 Podman Compose 与 Docker Compose 的开发中间件栈 `compose.yaml`,包含:
- **共享信箱 MySQL**`mysql:8.4` LTS,端口 3306,库 `cdairport`):容器启动时自动执行 `deploy/dev/mysql-init/01-mailbox.sql` 创建本地联调所需的 `CMINMSGS``CMINMSGS_HST``COUTMSGS` 模拟表。
- **自有 PostgreSQL**`postgres:17-alpine`,端口 5432,库 `msgx`):容器提供干净数据库,应用启动时由 Flyway(`db/migration/V1__flight_state_baseline.sql`)自动建自有表。
- **Valkey**`valkey/valkey:8-alpine`,端口 6379):本地中间件兼容服务,不承担航班状态权威或日计划处理
- **自有 PostgreSQL**`postgres:17-alpine`,端口 5432,库 `msgx`):容器提供干净数据库,应用启动时由 Flyway(`src/main/resources/db/migration/V1__flight_state_baseline.sql`)自动建自有表。
- **Valkey**`valkey/valkey:8-alpine`,端口 6379):本地兼容服务;应用不依赖它(`application.yml` 无对应配置键,航班状态权威在自有 PG)
- **Kafka**`apache/kafka:3.8.0` KRaft 单节点,端口 9092):listener `PLAINTEXT://localhost:9092``default.replication.factor=1`,已预配幂等生产者与 acks=all 所需的单节点参数。
### 快速启动
@@ -110,7 +98,6 @@ docker compose ps
| `MSGX_PG_URL` | 自有 PostgreSQL JDBC URL | `jdbc:postgresql://localhost:5432/msgx` | `datasources.default.url` |
| `MSGX_PG_USER` | 自有 PostgreSQL 用户名 | `msgx_dev` | `datasources.default.username` |
| `MSGX_PG_PASSWORD` | 自有 PostgreSQL 密码 | `msgx_dev_pass` | `datasources.default.password` |
| `MSGX_REDIS_URI` | Redis/Valkey 连接串 | `redis://localhost:6379` | `redis.uri` |
| `MSGX_KAFKA_SERVERS` | Kafka bootstrap brokers | `localhost:9092` | `kafka.bootstrap.servers` |
| `MSGX_KAFKA_ACKS` | 生产确认级别 | `all` | `kafka.producers.default.acks` |
| `MSGX_KAFKA_IDEMPOTENCE` | 幂等生产者开关 | `true` | `kafka.producers.default.enable-idempotence` |
@@ -118,7 +105,7 @@ docker compose ps
### 切流前 Kafka Broker 版本确认
生产契约以 [requirements.md](docs/requirements.md) `US-07` 为准:对接 Kafka 2.8+ / 3.x+,生产者强制 `acks=all``enable.idempotence=true``max.in.flight.requests.per.connection=1`**严禁非幂等降级**。README 不提供生产降级环境变量组合。
生产契约以 [requirements.md](docs/requirements.md) `US-07` `D3` 为准(三项生产者约束的取值见 [reference.md](docs/reference.md) 参数表)**严禁非幂等降级**。README 不提供生产降级环境变量组合。
旧系统 `msgexchange-api` 底层依赖 `kafka-clients:0.10.1.1`;现网 Broker 确切版本须在切流前实测确认:
@@ -126,7 +113,7 @@ docker compose ps
```bash
docker exec msgx-dev-kafka /opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server <TARGET_IP>:9092
```
2. **门禁判定**:若输出中 `InitProducerId(22)` 为 **可用** → 保持现代高可靠默认(`acks=all`、`idempotence=true`、`max.in.flight=1`
2. **门禁判定**:若输出中 `InitProducerId(22)` 为 **可用** → 保持 `D3` 的高可靠默认
3. **阻塞切流**:若 `InitProducerId(22)` 为 **UNSUPPORTED**Broker &lt; 0.11)→ **阻塞切流**,须升级 Broker 或经架构豁免(ACM2-1 基础设施升级门禁);降级参数仅可作为经批准的 runbook 附录,**不得**作为生产验收口径与 README 默认配置并存。
## 构建
@@ -134,25 +121,13 @@ docker compose ps
```bash
./gradlew build # 需网络拉取依赖;内网环境见 gradle.properties 注释
./gradlew test # 纯逻辑单测(identity / schd 聚合 / 配置绑定 / 管道语义)
MICRONAUT_ENVIRONMENTS=dev ./gradlew run # dev stub 冒烟:内存 stub,无需 DB/Redis/Kafka/Eureka
MICRONAUT_ENVIRONMENTS=dev ./gradlew run # dev stub 冒烟:内存 stub,无需 DB/Kafka/Eureka
```
> **进程级 dev 冒烟(2026-09-07 修复并实测通过)**:此前 `./gradlew run` 因未声明
> `application.mainClass` 报 "No main class specified"(已补,见 build.gradle.kts);随后实测又暴露
> dev profile 三处装配问题并已修复(application-dev.yml):stub 模式未排除 DataSource
> `datasources.default.enabled=false`);micronaut 自带 Redis/Kafka 健康指示器在无 broker 时把
> /health 拖成 500dev 关闭 redis.health/kafka.health,健康由自定义指示器承担);管理端点前缀误用
> `micronaut.endpoints.*`(正确为顶层 `endpoints.*`/env、/beans 这才真正放开);另关闭 eureka
> discovery。当前 `MICRONAUT_ENVIRONMENTS=dev ./gradlew run` **无需任何环境变量/外部依赖**即可启动,
> compat HTTP `POST /cminmsgs/send`application/json)返回记录 ID`/health` UP。
> 已知边界:text/plain compat 写 415 属 U16 缺口(契约对拍后固化);`application-dev.yml` 数据源配置
> 已对齐为自有 PG 与共享 MySQL 信箱(ACM2-12/13);测试侧端到端语义由 PipelineSmokeTest
> (内嵌上下文,compat 写路径)覆盖。**生产主路径**(JDBC 轮询 `InboxPoller`)属 U05dev stub 尚未覆盖。
> 注解处理:Kotlin 侧经 KSP`kotlin-ksp` + `micronaut-inject-kotlin`)生成 Micronaut
> BeanDefinitionU01;若 build 产物缺少 `*$Definition` 类,先检查 KSP 是否生效。
> BeanDefinition;若 build 产物缺少 `*$Definition` 类,先检查 KSP 是否生效。
> dev/shadow 冒烟装配:`msgx.stubs=true`(内存仓储/适配层,见 infra/stub+
> `msgx.pipeline.autostart=true`PipelineLifecycle 拉起专用线程U07);生产默认两者关闭。
> `msgx.pipeline.autostart=true`PipelineLifecycle 拉起专用线程);生产默认两者关闭。
## 文档