2026-09-07 10:27:44 +08:00
|
|
|
|
# msgexchange-v2(机场上游报文处理中间件)
|
|
|
|
|
|
|
|
|
|
|
|
**系统角色**:消费 CIIMS/AODB 等上游经共享 MySQL 信箱(`CMINMSGS`)投递的 XML 报文,
|
2026-09-14 08:28:47 +08:00
|
|
|
|
解析处理后维护业务数据库中的航班当前态与静态参考数据,并向 Kafka / 出站信箱投递——**独立消息网关**,非报文源系统。admin-api 是数据库的下游读取方,本网关不调用 admin-api。
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
现行设计依据是 [设计文档入口](docs/README.md) 的六份文档;早期方案 ACMA-8 / ACMA-6 只作历史记录,
|
|
|
|
|
|
不再作为设计或实现依据(自有 PostgreSQL 维护内部状态,共享 MySQL 仅作信箱边界)。
|
2026-09-07 10:27:44 +08:00
|
|
|
|
本仓库独立于 legacy `msgexchange-api`(Java 8 / Spring Boot 1.5 / Maven)——
|
2026-09-07 08:21:11 +08:00
|
|
|
|
过渡期两套系统并存(影子对拍→切流→旧仓库冻结),legacy 维护不受本仓库影响。
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
2026-09-07 10:27:44 +08:00
|
|
|
|
> **JDK 口径实测修正**:Micronaut 5.1 系构件要求 **JVM 25+**(`./gradlew :dependencies` 实测
|
|
|
|
|
|
> core 系解析 **5.1.13**,platform BOM **5.1.3**,classpath 混用);计划原定 JDK 21 不可行;
|
2026-09-14 07:34:17 +08:00
|
|
|
|
> 工程已按 **JDK 25** 配置(工具链约束见 `AGENTS.md`)。
|
2026-09-07 10:27:44 +08:00
|
|
|
|
|
|
|
|
|
|
## 系统边界
|
|
|
|
|
|
|
|
|
|
|
|
| 方向 | 机制 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
| **入站(主路径)** | JDBC 轮询共享 `CMINMSGS` | 上游经 CIIMS 等**外部系统**写信箱;本系统按轮询间隔读取水位之后的记录(`ID > W`,**不以处理标记为谓词**)→ 自有 PG 入队(`InboxPoller`) |
|
2026-09-07 10:27:44 +08:00
|
|
|
|
| **入站(compat)** | HTTP `POST /cminmsgs/send` | 手工注入/影子对拍;写信箱 + PG 入队,**非**生产主拓扑 |
|
2026-09-14 08:28:47 +08:00
|
|
|
|
| **处理** | 主泵 FIFO 管道 | 解码 → identity → Handler 决策 → PostgreSQL 航班当前态 / 静态参考数据 |
|
2026-09-07 10:27:44 +08:00
|
|
|
|
| **出站** | Kafka + `COUTMSGS` | 向下游推送 msg/schd;请求类报文写出站信箱 |
|
2026-09-14 08:28:47 +08:00
|
|
|
|
| **数据消费** | admin-api 只读数据库 | 从本网关处理后的 PostgreSQL(或通过适配验证的 Oracle)读取;不形成反向依赖 |
|
2026-09-07 10:27:44 +08:00
|
|
|
|
|
|
|
|
|
|
与 SIS / legacy 一致:本系统**不**替代 CIIMS 落信,**不**生成原始 AODB 业务报文。
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
## 包结构(与 [architecture.md](docs/architecture.md)「模块职责」一致)
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
| 包 | 职责 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `ingress/` | 轮询共享信箱并持久化入队、兼容 HTTP 写入;不解析业务报文 |
|
|
|
|
|
|
| `processing/` | FIFO 调度、业务身份绑定与去重、领域决策与落库 |
|
|
|
|
|
|
| `delivery/` | 按目标保序投递、`schd` 聚合与失败重试 |
|
|
|
|
|
|
| `jobs/` | 回填补偿扫描、航班历史清理与留痕保留期清理 |
|
|
|
|
|
|
| `codec/` | SIS XML 解码(禁 DTD/外部实体),区分非法报文与可修复的解码失败 |
|
|
|
|
|
|
| `domain/` | 领域状态、事件与决策模型 |
|
|
|
|
|
|
| `config/` | 运行参数(`msgx.*`) |
|
|
|
|
|
|
| `infra/` | 仓储(JDBC/stub)、外部适配器、重试、健康与指标 |
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
|
|
|
|
|
## 资源
|
|
|
|
|
|
|
2026-09-07 08:29:49 +08:00
|
|
|
|
- `docs/legacy/SIS_AODB_RMS-V0.1.md` + `docs/legacy/unisysaodbsis.xsd`:**消息结构唯一事实源**
|
|
|
|
|
|
(wire 契约冻结,自 legacy 仓库复制以自包含;codec 实装依据,ACM2-2/ACM2-3)。
|
2026-09-14 07:34:17 +08:00
|
|
|
|
- `src/main/resources/db/migration/V1__flight_state_baseline.sql`:**自有 PostgreSQL**(唯一自有库)的航班状态与处理管道表结构;
|
2026-09-09 20:22:10 +08:00
|
|
|
|
共享 MySQL(CMINMSGS/COUTMSGS)仅信箱 DML,不建表。
|
2026-09-14 07:34:17 +08:00
|
|
|
|
- `application.yml`:口令全部环境变量外置(零入库);`datasources.default`=自有 PG(默认关闭),
|
|
|
|
|
|
`mailbox.shared-mysql`=共享信箱;运行参数默认值一律以 [reference.md](docs/reference.md) 参数表为准。
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
## 实现进度
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
进度、缺口处置与排期在 Plane(ACM2);设计口径、偏差与可声明性见 [docs/specification.md](docs/specification.md)。本 README 不记录进度。
|
2026-09-06 16:13:50 +08:00
|
|
|
|
|
2026-09-07 08:21:11 +08:00
|
|
|
|
## 数据库初始化(ACM2-12 口径)
|
2026-09-06 17:58:39 +08:00
|
|
|
|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
**自有 PostgreSQL**(唯一自有库):Flyway 执行 `src/main/resources/db/migration/V1__flight_state_baseline.sql`,建立
|
2026-09-13 16:31:14 +08:00
|
|
|
|
航班当前态、明细表、处理终态与 outbox 等表(PG 方言)。这是**单基线**:原 V2–V10 的净结构已
|
|
|
|
|
|
合并其中,全新库直接执行即可,无 legacy 前置。已按旧链(V1–V10)迁移过的库版本链与校验和都
|
|
|
|
|
|
对不上,必须重建 schema 或删除数据卷后重跑,禁止手工 `repair` 或改写 `flyway_schema_history`。
|
2026-09-06 17:58:39 +08:00
|
|
|
|
|
2026-09-07 10:27:44 +08:00
|
|
|
|
**共享 MySQL(cdairport,他人系统库)**:本系统**不建表/schema**,仅信箱 DML——上游外部写
|
|
|
|
|
|
`CMINMSGS`;本系统 JDBC 轮询读 + 处理回填;出站写 `COUTMSGS`(他人读取发送);表结构与
|
|
|
|
|
|
保留策略归库方管理。部署前需确认共享库 CMINMSGS 已存在(他人系统提供);本仓库不声明其 schema。
|
2026-09-07 08:21:11 +08:00
|
|
|
|
|
2026-09-07 10:27:44 +08:00
|
|
|
|
- **事务模型**:与共享库交互均为外部副作用(ACM2-12)——主路径=上游外部写信箱 →
|
|
|
|
|
|
JDBC 轮询发现新信 → 自有 PG 建 PENDING 入队(失败重扫补建);HTTP `/cminmsgs/send`
|
|
|
|
|
|
为 compat 写路径;处理成功回填 DATE_PROCESSED/STATUS 为最终一致。
|
2026-09-13 20:31:13 +08:00
|
|
|
|
- **航班状态**:写入自有 PostgreSQL;完整规则见 [implementation.md](docs/implementation.md)「航班域」。
|
2026-09-07 08:21:11 +08:00
|
|
|
|
- 影子对拍:自有 PG 开独立 schema;共享信箱为单信箱无法双写,影子输入=只读水位/回放口径。
|
2026-09-06 17:58:39 +08:00
|
|
|
|
|
2026-09-07 14:15:07 +08:00
|
|
|
|
|
|
|
|
|
|
## 本地开发依赖中间件栈(Podman / Docker Compose,ACM2-13)
|
|
|
|
|
|
|
|
|
|
|
|
仓库根目录提供兼容 Podman Compose 与 Docker Compose 的开发中间件栈 `compose.yaml`,包含:
|
|
|
|
|
|
- **共享信箱 MySQL**(`mysql:8.4` LTS,端口 3306,库 `cdairport`):容器启动时自动执行 `deploy/dev/mysql-init/01-mailbox.sql` 创建本地联调所需的 `CMINMSGS`、`CMINMSGS_HST`、`COUTMSGS` 模拟表。
|
2026-09-14 07:34:17 +08:00
|
|
|
|
- **自有 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)。
|
2026-09-07 14:15:07 +08:00
|
|
|
|
- **Kafka**(`apache/kafka:3.8.0` KRaft 单节点,端口 9092):listener `PLAINTEXT://localhost:9092`,`default.replication.factor=1`,已预配幂等生产者与 acks=all 所需的单节点参数。
|
|
|
|
|
|
|
|
|
|
|
|
### 快速启动
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 1. 复制环境变量示例(可按需修改口令与端口)
|
|
|
|
|
|
cp .env.example .env
|
|
|
|
|
|
|
|
|
|
|
|
# 2. 启动开发依赖栈(支持 docker compose 或 podman compose)
|
|
|
|
|
|
docker compose up -d
|
|
|
|
|
|
|
|
|
|
|
|
# 3. 检查各服务健康状态(均为 healthy)
|
|
|
|
|
|
docker compose ps
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 环境变量与应用映射
|
|
|
|
|
|
|
|
|
|
|
|
| 环境变量 | 说明 | 示例默认值 | 应用配置映射(application.yml) |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| `MSGX_MAILBOX_URL` | 共享信箱 MySQL JDBC URL | `jdbc:mysql://localhost:3306/cdairport?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4` | `mailbox.shared-mysql.url` |
|
|
|
|
|
|
| `MSGX_MAILBOX_USER` | 共享信箱 MySQL 用户名 | `msgx_dev` | `mailbox.shared-mysql.username` |
|
|
|
|
|
|
| `MSGX_MAILBOX_PASSWORD` | 共享信箱 MySQL 密码 | `msgx_dev_pass` | `mailbox.shared-mysql.password` |
|
|
|
|
|
|
| `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_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` |
|
|
|
|
|
|
| `MSGX_KAFKA_MAX_IN_FLIGHT` | 单连接最大在途请求数 | `1` | `kafka.producers.default.max-in-flight-requests-per-connection` |
|
|
|
|
|
|
|
|
|
|
|
|
### 切流前 Kafka Broker 版本确认
|
|
|
|
|
|
|
2026-09-14 07:34:17 +08:00
|
|
|
|
生产契约以 [requirements.md](docs/requirements.md) `US-07` 与 `D3` 为准(三项生产者约束的取值见 [reference.md](docs/reference.md) 参数表),**严禁非幂等降级**。README 不提供生产降级环境变量组合。
|
2026-09-07 14:15:07 +08:00
|
|
|
|
|
|
|
|
|
|
旧系统 `msgexchange-api` 底层依赖 `kafka-clients:0.10.1.1`;现网 Broker 确切版本须在切流前实测确认:
|
|
|
|
|
|
|
|
|
|
|
|
1. **版本探测**:切流前通过本栈工具探测目标集群 API 能力:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
docker exec msgx-dev-kafka /opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server <TARGET_IP>:9092
|
|
|
|
|
|
```
|
2026-09-14 07:34:17 +08:00
|
|
|
|
2. **门禁判定**:若输出中 `InitProducerId(22)` 为 **可用** → 保持 `D3` 的高可靠默认。
|
2026-09-07 14:15:07 +08:00
|
|
|
|
3. **阻塞切流**:若 `InitProducerId(22)` 为 **UNSUPPORTED**(Broker < 0.11)→ **阻塞切流**,须升级 Broker 或经架构豁免(ACM2-1 基础设施升级门禁);降级参数仅可作为经批准的 runbook 附录,**不得**作为生产验收口径与 README 默认配置并存。
|
|
|
|
|
|
|
2026-09-06 16:13:50 +08:00
|
|
|
|
## 构建
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
2026-09-06 17:58:39 +08:00
|
|
|
|
./gradlew build # 需网络拉取依赖;内网环境见 gradle.properties 注释
|
2026-09-06 20:53:14 +08:00
|
|
|
|
./gradlew test # 纯逻辑单测(identity / schd 聚合 / 配置绑定 / 管道语义)
|
2026-09-14 07:34:17 +08:00
|
|
|
|
MICRONAUT_ENVIRONMENTS=dev ./gradlew run # dev stub 冒烟:内存 stub,无需 DB/Kafka/Eureka
|
2026-09-06 16:13:50 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-06 17:58:39 +08:00
|
|
|
|
> 注解处理:Kotlin 侧经 KSP(`kotlin-ksp` + `micronaut-inject-kotlin`)生成 Micronaut
|
2026-09-14 07:34:17 +08:00
|
|
|
|
> BeanDefinition;若 build 产物缺少 `*$Definition` 类,先检查 KSP 是否生效。
|
2026-09-06 20:53:14 +08:00
|
|
|
|
> dev/shadow 冒烟装配:`msgx.stubs=true`(内存仓储/适配层,见 infra/stub)+
|
2026-09-14 07:34:17 +08:00
|
|
|
|
> `msgx.pipeline.autostart=true`(PipelineLifecycle 拉起专用线程);生产默认两者关闭。
|
2026-09-06 17:58:39 +08:00
|
|
|
|
|
2026-09-06 22:30:03 +08:00
|
|
|
|
## 文档
|
|
|
|
|
|
|
2026-09-13 20:31:13 +08:00
|
|
|
|
`docs/` 是唯一设计依据;从 [设计文档入口](docs/README.md) 开始阅读(职责、事实归属、ID 语法与引用纪律都在那里)。顶层 6 个文件:
|
2026-09-08 18:50:34 +08:00
|
|
|
|
|
2026-09-13 20:31:13 +08:00
|
|
|
|
- [architecture.md](docs/architecture.md):系统边界、模块职责、存储归属、总体流程与 `D1`–`D4` 决策。
|
|
|
|
|
|
- [requirements.md](docs/requirements.md):阶段范围与非目标、`US-xx` / `OPS-x` 验收目标、需求覆盖与依赖。
|
|
|
|
|
|
- [specification.md](docs/specification.md):术语、契约 `C-x`、前提 `PRE-x`、不变量 `INV-x`、声明边界 `CLM-x`、待确认 `Q`、已知偏差 `G`、验证映射。
|
2026-09-14 08:28:47 +08:00
|
|
|
|
- [implementation.md](docs/implementation.md):管道机制、航班域与静态参考数据——数据模型、状态机、事务、投递、作业、恢复及合并语义。
|
2026-09-13 20:31:13 +08:00
|
|
|
|
- [reference.md](docs/reference.md):参数 `PARAM:<key>`、指标与健康、模块与代码入口、错误分类。
|
|
|
|
|
|
- [legacy/](docs/legacy/):外部协议与旧系统基线(`SIS_AODB_RMS-V0.1.md` 为消息结构唯一事实源、`unisysaodbsis.xsd`、legacy 行为对拍基线、历史决策记录)。
|
2026-09-06 22:30:03 +08:00
|
|
|
|
|
2026-09-13 20:31:13 +08:00
|
|
|
|
实现进度与缺口处置在 Plane(ACM2)。运行规程在上线/切流前另立 `docs/runbooks/`,不进入顶层。
|