Files
msgexchange-v2/README.md
T
windyboyandCursor fd65bb24fb docs(acm2-75): 按需求与架构收口契约和规范
补齐接口契约的入站、Redis 与出站边界,规范对齐已定语义并作废过期条款;Kafka 生产端约束编号改为 D2。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 08:49:19 +08:00

10 KiB
Raw Blame History

msgexchange-v2(机场上游报文处理中间件)

系统角色:消费 CIIMS/AODB 等上游经共享 MySQL 信箱(CMINMSGS)投递的 XML 报文, 解析处理后维护业务数据库中的航班当前态与静态参考数据,并向 Kafka / 出站信箱投递——独立消息网关,非报文源系统。admin-api 是数据库的下游读取方,本网关不调用 admin-api。

现行设计依据是 设计文档入口 的六份文档;早期方案 ACMA-8 / ACMA-6 只作历史记录, 不再作为设计或实现依据(自有 PostgreSQL 维护内部状态,共享 MySQL 仅作信箱边界)。 本仓库独立于 legacy msgexchange-apiJava 8 / Spring Boot 1.5 / Maven)—— 过渡期两套系统并存(影子对拍→切流→旧仓库冻结),legacy 维护不受本仓库影响。

JDK 口径实测修正Micronaut 5.1 系构件要求 JVM 25+./gradlew :dependencies 实测 core 系解析 5.1.13platform BOM 5.1.3,classpath 混用);计划原定 JDK 21 不可行; 工程已按 JDK 25 配置(工具链约束见 AGENTS.md)。

系统边界

方向 机制 说明
入站(主路径) JDBC 轮询共享 CMINMSGS 上游经 CIIMS 等外部系统写信箱;本系统按轮询间隔读取「处理时间为空」的行,按编号升序、每批有上限(C-30)→ 自有 PG 入队(InboxPoller
入站(compat HTTP POST /cminmsgs/send 手工注入/影子对拍;写信箱 + PG 入队,生产主拓扑
处理 主泵 FIFO 管道 解码 → identity → Handler 决策 → PostgreSQL 航班当前态 / 静态参考数据
出站 Kafka + COUTMSGS 向下游推送 msg/schd;请求类报文写出站信箱
数据消费 admin-api 只读数据库 从本网关处理后的 PostgreSQL(或通过适配验证的 Oracle)读取;不形成反向依赖

与 SIS / legacy 一致:本系统替代 CIIMS 落信,生成原始 AODB 业务报文。

包结构(与 architecture.md「模块职责」一致)

职责
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)。
  • src/main/resources/db/migration/V1__flight_state_baseline.sql自有 PostgreSQL(唯一自有库)的航班状态与处理管道表结构; 共享 MySQLCMINMSGS/COUTMSGS)仅信箱 DML,不建表。
  • application.yml:口令全部环境变量外置(零入库);datasources.default=自有 PG(默认关闭), mailbox.shared-mysql=共享信箱;运行参数默认值一律以 reference.md 参数表为准。

实现进度

进度、缺口处置与排期在 Plane(ACM2);设计口径、偏差与可声明性见 docs/specification.md。本 README 不记录进度。

数据库初始化(ACM2-12 口径)

自有 PostgreSQL(唯一自有库):Flyway 执行 src/main/resources/db/migration/V1__flight_state_baseline.sql,建立 航班当前态、明细表、处理终态与 outbox 等表(PG 方言)。这是单基线:原 V2–V10 的净结构已 合并其中,全新库直接执行即可,无 legacy 前置。已按旧链(V1–V10)迁移过的库版本链与校验和都 对不上,必须重建 schema 或删除数据卷后重跑,禁止手工 repair 或改写 flyway_schema_history

共享 MySQLcdairport,他人系统库):本系统不建表/schema,仅信箱 DML——上游外部写 CMINMSGS;本系统 JDBC 轮询读 + 处理回填;出站写 COUTMSGS(他人读取发送);表结构与 保留策略归库方管理。部署前需确认共享库 CMINMSGS 已存在(他人系统提供);本仓库不声明其 schema。

  • 事务模型:与共享库交互均为外部副作用(ACM2-12)——主路径=上游外部写信箱 → JDBC 轮询发现新信 → 自有 PG 建 PENDING 入队(失败重扫补建);HTTP /cminmsgs/send 为 compat 写路径;处理成功回填 DATE_PROCESSED/STATUS 为最终一致。
  • 航班状态:写入自有 PostgreSQL;完整规则见 implementation.md「航班域」。
  • 影子对拍:自有 PG 开独立 schema;共享信箱为单信箱无法双写,影子输入=只读水位/回放口径。

本地开发依赖中间件栈(Podman / Docker ComposeACM2-13

仓库根目录提供兼容 Podman Compose 与 Docker Compose 的开发中间件栈 compose.yaml,包含:

  • 共享信箱 MySQLmysql:8.4 LTS,端口 3306,库 cdairport):容器启动时自动执行 deploy/dev/mysql-init/01-mailbox.sql 创建本地联调所需的 CMINMSGSCMINMSGS_HSTCOUTMSGS 模拟表。
  • 自有 PostgreSQLpostgres:17-alpine,端口 5432,库 msgx):容器提供干净数据库,应用启动时由 Flyway(src/main/resources/db/migration/V1__flight_state_baseline.sql)自动建自有表。
  • Valkeyvalkey/valkey:8-alpine,端口 6379):本地兼容服务;应用不依赖它(application.yml 无对应配置键,航班状态权威在自有 PG)。
  • Kafkaapache/kafka:3.8.0 KRaft 单节点,端口 9092):listener PLAINTEXT://localhost:9092default.replication.factor=1,已预配幂等生产者与 acks=all 所需的单节点参数。

快速启动

# 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 版本确认

生产契约以 requirements.md US-08D2 为准(三项生产者约束的取值见 reference.md 参数表),严禁非幂等降级。README 不提供生产降级环境变量组合。

旧系统 msgexchange-api 底层依赖 kafka-clients:0.10.1.1;现网 Broker 确切版本须在切流前实测确认:

  1. 版本探测:切流前通过本栈工具探测目标集群 API 能力:
    docker exec msgx-dev-kafka /opt/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server <TARGET_IP>:9092
    
  2. 门禁判定:若输出中 InitProducerId(22)可用 → 保持 D2 的高可靠默认。
  3. 阻塞切流:若 InitProducerId(22)UNSUPPORTEDBroker < 0.11)→ 阻塞切流,须升级 Broker 或经架构豁免(ACM2-1 基础设施升级门禁);降级参数仅可作为经批准的 runbook 附录,不得作为生产验收口径与 README 默认配置并存。

构建

./gradlew build          # 需网络拉取依赖;内网环境见 gradle.properties 注释
./gradlew test           # 纯逻辑单测(identity / schd 聚合 / 配置绑定 / 管道语义)
MICRONAUT_ENVIRONMENTS=dev ./gradlew run   # dev stub 冒烟:内存 stub,无需 DB/Kafka/Eureka

注解处理:Kotlin 侧经 KSPkotlin-ksp + micronaut-inject-kotlin)生成 Micronaut BeanDefinition;若 build 产物缺少 *$Definition 类,先检查 KSP 是否生效。 dev/shadow 冒烟装配:msgx.stubs=true(内存仓储/适配层,见 infra/stub+ msgx.pipeline.autostart=truePipelineLifecycle 拉起专用线程);生产默认两者关闭。

文档

docs/ 是唯一设计依据;从 设计文档入口 开始阅读(职责、事实归属、ID 语法与引用纪律都在那里)。顶层 6 个文件:

  • architecture.md:系统边界、模块职责、存储归属、总体流程与 D1D2 决策。
  • requirements.md:阶段范围与非目标、US-xx / OPS-x 验收目标、需求覆盖与依赖。
  • specification.md:术语、契约 C-x、前提 PRE-x、不变量 INV-x、声明边界 CLM-x、待确认 Q、已知偏差 G、验证映射。
  • implementation.md:管道机制、航班域与静态参考数据——数据模型、状态机、事务、投递、作业、恢复及合并语义。
  • reference.md:参数 PARAM:<key>、指标与健康、模块与代码入口、错误分类。
  • legacy/:外部协议与旧系统基线(SIS_AODB_RMS-V0.1.md 为消息结构唯一事实源、unisysaodbsis.xsd、legacy 行为对拍基线、历史决策记录)。

实现进度与缺口处置在 Plane(ACM2)。运行规程在上线/切流前另立 docs/runbooks/,不进入顶层。