docs(acm2): 精简运行参考并澄清配置和指标

This commit is contained in:
windyboy
2026-09-20 19:15:25 +08:00
parent c8acb74432
commit 7aec4ea0ab
+108 -102
View File
@@ -1,119 +1,125 @@
# 参考注册表:参数、指标、代码入口、错误分类
# 运行参考
本文件是**可派生事实**的唯一出处:参数默认值、指标名、模块与代码入口、错误分类。正文只引 `PARAM:<完整键>`,不写数值
本文件列出运行参数、固定调度时间、监控指标、代码位置和错误类别。业务约定见 [specification.md](specification.md),处理流程见 [implementation.md](implementation.md);其他文档引用参数时使用 `PARAM:<完整键>`
维护规则:新增或改名一个 `msgx.*` 键、增删一个指标名,必须同步本文件(人工核对,本项目不做 CI 校验)。`mailbox.*``datasources.*``kafka.*` 属基础设施键,成组登记。
## 参数注册表
## 1. 参数注册表
表中数值来自当前配置或代码。「暂定」表示取值还没有业务或容量依据;「与需求冲突」表示当前配置不符合验收要求。
「依据」列含义:**契约** = 由 [specification.md](specification.md) 的 `C-x` 决定;**现役** = 沿用 legacy 行为基线;**实现** = 当前代码采用的机制,取值并非需求契约;**假定** = 无依据的占位值,必须在对应 `Q` 关闭后重评;**安全默认** = 关闭态,需显式开启。
### 收报、处理与投递
### 1.1 管道节奏与重试(`msgx.pipeline.*`
| 参数 | 默认 | 单位 | 依据 | 说明 |
|---|---|---|---|---|
| `msgx.pipeline.poll-interval` | `1s` | Duration | 现役 | 收报轮询节奏;决定队头检查频率 |
| `msgx.pipeline.claim-batch` | `50` | 条 | 假定 | 单轮领取上限;过大延长单轮事务 |
| `msgx.pipeline.max-attempts` | `5` | 次 | 假定 | 处理与投递共用;达到即转 `DEAD(EXHAUSTED)` |
| `msgx.pipeline.backoff-ms` | `[1000,2000,4000,8000]` | ms / 档 | 假定 | **档位数必须 = `max-attempts 1`**,启动自检拦截错位 |
| `msgx.pipeline.backoff-cap-ms` | `60000` | ms | 假定 | 单档封顶;默认表内无档触及 |
| `msgx.pipeline.max-commit-delay` | `5m` | Duration | **假定(无依据)** | **待退役**:水位空洞老化阈值,随收报改谓词扫描(`G-SCAN-PREDICATE`)删除 |
| `msgx.pipeline.overdue-backfill` | `30d` | Duration | 契约(`R ≤ R_keep` | 即 `R`:进入强补写窗口、**取消退避**的阈值;**不是兜底保证** |
| `msgx.pipeline.backfill-batch` | `100` | 条 | 假定 | 回填扫描单批条数 |
| `msgx.pipeline.backfill-scan-period` | `30s`(代码常量,无配置键) | Duration | 现役 | 回填扫描作业周期 |
| `msgx.pipeline.backfill-max-attempts` | `100` | 次 | 假定 | 单行重试的**告警阈值**;放弃判据是 `R` 超期,不是次数 |
| `msgx.pipeline.backfill-backoff-ms` | `30000` | ms | 假定 | 回填独立退避起步间隔;`BackfillService` 指数退避的首档 |
| `msgx.pipeline.backfill-backoff-cap-ms` | `900000` | ms | 假定 | 回填退避封顶(15 分钟) |
| `msgx.pipeline.cutover-watermark` | 不设置 | `min\|zero\|max\|<id>` | 一次性运维决策 | **待退役**:显式播种水位(`G-SCAN-PREDICATE`);非法值由启动自检挡下,升级实例拒绝重新播种 |
| `msgx.pipeline.delivery-batch` | `200` | 条 | 假定 | `KAFKA:msg` 每轮每目标领取上限 |
| `msgx.pipeline.delivery-drain-rounds` | `10` | 轮 | 假定 | 连取批数上限,让出循环跑 `schd` flush,防状态通知被积压饿死 |
| `msgx.pipeline.autostart` | `false` | 布尔 | 安全默认 | 启动即拉起收报 / 主泵 / 投递循环;需真实仓储或 `msgx.stubs=true` |
| `msgx.pipeline.event-retention` | `7d` | Duration | 假定 | 已 `SENT` 事件行保留期,由清理作业删除 `sent_at < now - retention` 的行 |
### 1.2 其它 `msgx.*`
| 参数 | 默认 | 依据 | 说明 |
| 参数 | 默认值 | 用途与约束 | 依据 |
|---|---|---|---|
| `msgx.service-name` | `msgexchangeapi` | 契约冻结 | 服务注册名;影子实例用独立名 |
| `msgx.register-eureka` | `true` | 现役 | 服务注册开关;影子对拍期置 `false` |
| `msgx.stubs` | `true`dev profile | 安全默认 | 内存适配器;**生产禁止**,生产 profile 不设置该键 |
| `msgx.schd.flush-period` | `3s` | 现役 | `schd` 聚合周期 |
| `msgx.schd.flush-limit` | `500` | 假定 | 聚合批上限 |
| `msgx.operation-day.zone` | `Asia/Shanghai` | 现役 | 运营日推导时区;配置值非法时启动失败,不使用代码字面量回退 |
| `msgx.operation-day.cutoff-hour` | `0` | **假定(占位)** | 切日边界;待业务确认 |
| `msgx.identity.include-day-boundary` | `false` | 安全默认 | 身份是否加日期边界;影响去重语义,不能当调优项切换(`Q10` |
| `msgx.health.backlog-cache-ttl-ms` | `30000` | 假定 | 积压快照缓存窗口;`0` = 不缓存;`/health``/metrics` 共用同一快照 |
| `msgx.history.history-store-enabled` | `false` | 安全默认 | 历史存储门控;关闭时历史清理删 0 条 |
| `msgx.history.cancelled-hours` | `1` | 契约(`US-14` AC2) | 取消态判据窗口:CNCL 早于当前超过 1 小时 |
| `msgx.history.terminal-hours` | `48` | **与 `US-14` AC2 冲突** | 终态判据窗口;AC2 判史条件(计划超 3 天、离港/到港成套条件)不含 48h 终态窗口,取值须按 AC2 重评 |
| `msgx.history.deleted-hours` | `48` | 实现 | 已删除判据窗口;`US-14` AC2 之外的兜底判据 |
| `msgx.history.idle-hours` | `168` | 实现 | AC2 五条件之外的静默期兜底;与 AC2 的关系须在实现定案时说明 |
| `msgx.history.sweep-time` | `03:30`(代码常量,无配置键) | 契约(`US-14` AC1) | 历史清理作业每日执行时刻 |
| `msgx.history.snap-log-retention-days` | `90` | 假定 | `SCHD_SNAP_LOG` 保留天数 |
| `msgx.pipeline.poll-interval` | `1s` | 每隔多久查询一次信箱 | 沿用旧系统 |
| `msgx.pipeline.claim-batch` | `50` 条 | 一次最多从信箱读取多少条消息 | 暂定 |
| `msgx.pipeline.max-attempts` | `5` 次 | 处理或发送一条消息最多尝试多少次 | 暂定 |
| `msgx.pipeline.backoff-ms` | `[1000, 2000, 4000, 8000]` ms | 每次失败后等多久再试;等待时间档位数须比最多尝试次数少一 | 暂定 |
| `msgx.pipeline.backoff-cap-ms` | `60000` ms | 失败后最长等待时间 | 暂定 |
| `msgx.pipeline.delivery-batch` | `200` 条 | 一次最多领取多少条待发的 `msg` 消息 | 暂定 |
| `msgx.pipeline.delivery-drain-rounds` | `10` | 连续领取多少批后,先尝试发送一批 `schd` 消息 | 暂定 |
| `msgx.schd.flush-period` | `3s` | 每隔多久尝试发送一批 `schd` 消息 | 沿用旧系统 |
| `msgx.schd.flush-limit` | `500` 条 | 一批 `schd` 消息最多包含多少个航班 | 暂定 |
| `msgx.pipeline.event-retention` | `7d` | 发送成功的消息在数据库保留多久,从 `SENT_AT` 起算 | 暂定 |
### 1.3 信箱与外部依赖(成组登记)
### 写回信箱与编号扫描
| 参数 | 默认 | 依据 | 说明 |
| 参数 | 默认 | 用途与约束 | 依据 |
|---|---|---|---|
| `mailbox.processed-value` | `PROCESSED` | 契约(`Q8`) | 处理标记写入值;仅限库方认可值集;写入值形态(完成时刻 vs 常量)与 spec「处理标记=写完成时刻」存在张力,须核对旧系统真实写入内容后定案 |
| `mailbox.shared-mysql.enabled` | `false` | 安全默认 | 真实信箱接线门控 |
| `mailbox.shared-mysql.connect-timeout-ms` | `3000` | 假定 | 有界外部调用;Connector/J 默认无限等待,必须显式;同组 `socket-timeout-ms=30000` |
| `mailbox.shared-mysql.pool-connection-timeout-ms` | `5000` | 假定 | 池级连接超时;同组 `pool-validation-timeout-ms=3000` |
| `mailbox.shared-mysql.url` | 环境变量 | 安全 | 零入库,见 `.env.example`;同组 `username` / `password` |
| `datasources.default.connection-timeout` | `5000` | 假定 | 自有 PG 池(毫秒数,非 Duration 字面量);同组 `validation-timeout=3000``idle-timeout=300000``max-lifetime=1800000` |
| `datasources.default.data-source-properties.connectTimeout` | `3` | 假定 | 驱动级连接超时(秒);同组 `socketTimeout=30` |
| `kafka.producers.default.acks` | `all` | 实现(`D2` | 允许环境变量覆盖 |
| `kafka.producers.default.enable-idempotence` | `true` | 实现(`D2` | 允许环境变量覆盖 |
| `kafka.producers.default.max-in-flight-requests-per-connection` | `1` | 实现(`D2`) | 启动自检校验当前三项配置;允许环境变量覆盖 |
| `msgx.pipeline.overdue-backfill` | `30d` | 入队后超过期限 `R` 仍未写回处理时间,就立即再试;若仍失败,则停止自动重试。`R` 不得长于信箱行可保证的最短保留期 | 暂定 |
| `msgx.pipeline.backfill-batch` | `100` 条 | 一次最多检查多少条待写回信箱的记录 | 暂定 |
| `msgx.pipeline.backfill-max-attempts` | `100` 次 | 失败次数达到此值时告警,仍继续重试直到期限 `R` | 暂定 |
| `msgx.pipeline.backfill-backoff-ms` | `30000` ms | 首次写回失败后等多久再试 | 暂定 |
| `msgx.pipeline.backfill-backoff-cap-ms` | `900000` ms | 以后每次重试最长等多久 | 暂定 |
| `msgx.pipeline.max-commit-delay` | `5m` | 当前按编号读信箱时,遇到缺号最多等待多久;改用处理时间筛选后删除(`G-SCAN-PREDICATE` | 待替换 |
| `msgx.pipeline.cutover-watermark` | 不设置 | 首次启动时从哪个编号开始读信箱;可填 `min``zero``max` 或具体编号;改用处理时间筛选后删除(`G-SCAN-PREDICATE` | 待替换 |
环境变量清单以 `.env.example` 为准。
### 固定调度
## 2. 指标与健康
下列名称只用于查阅固定时间,不能写入配置文件。
| 指标 | 含义 | 期望方向 |
| 登记名 | 当前值 | 含义 |
|---|---|---|
| `msgx.pipeline.backlog.unfinished` | 未处理完的消息条数 | 长期 > 0 且不降 → 积压 |
| `msgx.pipeline.backlog.oldest_unprocessed_seconds` | 最老未处理消息的信龄 | 持续增长 → 队头卡住 |
| `msgx.pipeline.backfill.unmarked_terminal` | 已终态但未打标的条数 | 不降 → 回填失败 |
| `msgx.pipeline.backfill.abandoned` | 已放弃自动回填的条数 | **非 0 需人工对账** |
| `msgx.pipeline.backfill.oldest_unmarked_seconds` | 最老待回填年龄 | 决定实际回填延迟 |
| `msgx.pipeline.watermark.lag` | 水位落后信箱最新 ID 的距离;**待退役**(`G-SCAN-PREDICATE` | 增长 → 收报停滞 |
| `msgx.pipeline.job.heartbeat_age_seconds` | 距上一次作业 tick 完成的秒数(未跑过为 -1) | 持续增长 → 作业线程卡死 |
| `msgx.pipeline.job.last_failure_age_seconds` | 距最近一次作业 tick 失败的秒数(从未失败为 -1) | 配合 `failures.total` 增长判断扫描/历史作业异常 |
| `msgx.pipeline.job.ticks.total` | 作业 tick 完成次数 | 不增长 → 作业停摆 |
| `msgx.pipeline.job.failures.total` | 作业 tick 抛错次数 | 增长 → 扫描/历史作业异常 |
| `msgx.pipeline.job.last_sweep_selected` | 上一轮回填扫描选中的待办条数(扫描积压) | 持续顶到批次上限 → 扫描吃不消 |
| `msgx.pipeline.codec.srvt_seen.total` | 入站记录中出现 `SRVT` 段的条数(`G-SRVT-VIPF`) | > 0 → 真实流量确有该段,按真实报文闭合 `G-SRVT-VIPF` |
| `msgx.pipeline.codec.vipf_seen.total` | 入站记录中出现 `VIPF` 段的条数(`G-SRVT-VIPF` | 同上 |
| `msgx.pipeline.processing.ignored.total` | 命中 IgnoreRules 基线忽略清单(`LDM-*`/`REGN-*`/`RSTA-*`/`EROR-*`,见 `processing/IgnoreRules.kt`)的报文条数 | 增长是现行流量;但清单与目标设计冲突——`REGN`/`RSTA` 按设计必须分派 `US-13``EROR``US-09` AC3 必须定位请求标失败;该清单无 requirements 依据,处置待闭合 |
| `msgx.pipeline.backfill-scan-period` | `30s` | 每隔多久检查待写回信箱的记录;不是完成时限 |
| `msgx.history.sweep-time` | `03:30` | 按机场时间每天开始清理历史航班的时间 |
取数规则:统一走 `BacklogSnapshotProvider``PARAM:msgx.health.backlog-cache-ttl-ms`),`/health``/metrics` 共用同一快照——`backlog()``PROC_STATE` 的全表聚合,不能被高频抓取打穿;**无法取数上报 `NaN`,无可比记录的年龄/滞后类仪表上报 `-1`,都不伪造 0**。日志出口故障不得阻塞业务线程。进程内计数类指标不经快照,重启归零。
### 运行开关与航班域
作业健康:回填的唯一驱动是扫描作业,因此作业存活必须独立可观测——`msgx.pipeline.job.heartbeat_age_seconds` / `ticks.total` / `failures.total` 是作业心跳,`msgx.pipeline.job.last_sweep_selected` 是扫描积压,`msgx.pipeline.backfill.oldest_unmarked_seconds` 是实际回填延迟;作业线程停摆由 `/health` 的作业指示器判 `DOWN`(心跳超过 `3 × 扫描周期`,周期是代码常量)。
## 3. 模块与代码入口
| 关注点 | 主要入口 |
|---|---|
| 收报与兼容接口 | `ingress/InboxPoller.kt``ingress/InboxService.kt``ingress/InboxController.kt` |
| 解码与忽略规则 | `codec/JacksonXmlCodec.kt``codec/XmlCodec.kt``codec/SisWireMapper.kt``codec/SisMessageBody.kt``processing/IgnoreRules.kt` |
| 调度与处理 | `processing/Pump.kt`(含 `MessageProcessor`)、`processing/DynamicProcessors.kt`FLOP/FDEL/ADFT)、`processing/ScheduleProcessor.kt``processing/Identity.kt``processing/MessageLifecycleGate.kt` |
| 回填与投递 | `processing/BackfillService.kt``delivery/Dispatcher.kt``infra/kafka/KafkaDeliveryPort.kt` |
| 维护作业 | `jobs/JobRunner.kt``jobs/HistorySweepJob.kt``jobs/EventCleanupJob.kt``infra/persistence/SnapshotLogPurge.kt` |
| 持久化与恢复 | `infra/persistence/`(含 `jdbc/JdbcPgRepositories.kt``jdbc/JdbcCminmsgInboxRepository.kt`)、`infra/retry/``ProcFailure` / `ReplayService` / `FailureScheduler` |
| 启停与配置 | `PipelineLifecycle.kt``config/PipelineProps.kt``config/HistoryProps.kt``config/OperationDayProps.kt`(含运营日时区启动自检)、`config/MailboxProps.kt``config/KafkaD3Check.kt`Kafka 生产配置自检,语义见 `D2`;类名沿用历史命名) |
| 指标与健康 | `infra/metrics/PipelineMetrics.kt``infra/metrics/JobActivity.kt``infra/health/BacklogSnapshotProvider.kt``infra/health/JobRunnerHealthIndicator.kt` |
| 迁移 | `src/main/resources/db/migration/`(单基线 `V1__flight_state_baseline.sql``oracle11g/` 为占位) |
## 4. 错误分类与重放白名单
| 错误类别 | 触发 | 处置 | 可重放 |
| 参数 | 默认值 | 用途与约束 | 依据 |
|---|---|---|---|
| `MALFORMED` | 报文非法、原文缺失或缺少该类型必需的业务载荷 | 立即 `DEAD` | 否 |
| `PROTOCOL` | 载荷存在但整包违反业务协议(运营日冲突、声明数不符) | 立即 `DEAD`,整包不落地 | 否 |
| `CODEC_ERROR` | 解码能力问题 | `FAILED` 退避 | 是 |
| `UNSUPPORTED` | 处理器或快照能力未实现 | `FAILED` 退避,受尝试上限约束 | 是 |
| `INFRA` | 基础设施或执行异常 | `FAILED` 退避 | 是 |
| `EXHAUSTED` | 尝试次数耗尽 | `DEAD``ERROR_CLASS` 被覆写为 `EXHAUSTED`(原始类别不保留,`LAST_ERROR` 留文本) | |
| `msgx.service-name` | `msgexchangeapi` | 向 Eureka 注册时使用的服务名;用于对比的新实例须使用不同名称 | 沿用旧系统 |
| `msgx.register-eureka` | `true` | 是否向 Eureka 注册服务 | 当前实现 |
| `msgx.stubs` | `true`(dev) | 是否用内存数据代替真实数据库和消息系统;只在开发环境开启 | 开发环境 |
| `msgx.pipeline.autostart` | `false` | 服务启动时是否自动读信箱、处理和发送消息 | 默认关闭 |
| `msgx.operation-day.zone` | `Asia/Shanghai` | 计算运营日(按机场规则划分的航班日期)和清理历史航班时使用的时区;填错则拒绝启动 | 沿用旧系统 |
| `msgx.operation-day.cutoff-hour` | `0` | 按机场时间几点切换运营日;业务边界尚未确认 | 暂定 |
| `msgx.identity.include-day-boundary` | `false` | 去重时是否把日期算进消息身份;受 `C-3` 约束,不能随意改变 | 默认关闭 |
| `msgx.health.backlog-cache-ttl-ms` | `30000` ms | `/health``/metrics` 共用的未处理消息统计最多缓存多久;`0` 表示每次重算 | 暂定 |
| `msgx.history.history-store-enabled` | `false` | 是否启用历史航班写入;关闭时不删除实时航班 | 默认关闭 |
| `msgx.history.cancelled-hours` | `48` 小时 | 与 `US-14` AC2 的取消航班清理条件冲突 | 与需求冲突 |
| `msgx.history.terminal-hours` | `48` 小时 | `US-14` AC2 的清理条件不包含这个等待时间 | 与需求冲突 |
| `msgx.history.deleted-hours` | `48` 小时 | 已删除航班还要等待的时间;`US-14` AC2 没有此条件 | 与需求冲突 |
| `msgx.history.idle-hours` | `168` 小时 | 长时间未更新的航班也会被清理;`US-14` AC2 没有此条件 | 与需求冲突 |
| `msgx.history.snap-log-retention-days` | `90` 天 | 日计划处理记录保存多久 | 暂定 |
回填放弃原因:`MISSING_ROW`(信箱行不存在,确定性结论)、`TRANSIENT_DEADLINE`(暂时性故障持续到 `R` 仍未打标)。两者都不写 `BACKFILL_AT`,都不等于标记已确认;`R` 之前不放弃。
### 信箱与外部依赖(成组登记)
| 参数 | 默认值 | 用途与约束 | 依据 |
|---|---|---|---|
| `mailbox.processed-value` | `PROCESSED` | 写回处理时间时,同时写入 `CMINMSGS_STATUS``Q8` 未确认这个状态值 | 当前实现 |
| `mailbox.shared-mysql.enabled` | `false` | 是否连接共享 MySQL 信箱 | 默认关闭 |
| `mailbox.shared-mysql.url` | 环境变量 | 共享信箱地址;用户名和密码也从环境变量读取 | 当前实现 |
| `mailbox.shared-mysql.connect-timeout-ms` | `3000` ms | 连接 MySQL 最长等待多久;同组读取最长等待 `socket-timeout-ms=30000` ms | 暂定 |
| `mailbox.shared-mysql.pool-connection-timeout-ms` | `5000` ms | 等待一个可用的 MySQL 连接最长多久;同组校验连接最长等待 `pool-validation-timeout-ms=3000` ms | 暂定 |
| `datasources.default.connection-timeout` | `5000` ms | 等待一个可用的 PostgreSQL 连接最长多久;同组连接校验 `validation-timeout=3000`、空闲时间 `idle-timeout=300000`、连接寿命 `max-lifetime=1800000` ms | 暂定 |
| `datasources.default.data-source-properties.connectTimeout` | `3` 秒 | 连接 PostgreSQL 最长等待多久;同组读取最长等待 `socketTimeout=30` 秒 | 暂定 |
| `kafka.producers.default.acks` | `all` | Kafka 何时确认收到消息;与下两项一起满足 `D2` | 当前实现 |
| `kafka.producers.default.enable-idempotence` | `true` | 是否避免 Kafka 生产者自身重复发送,见 `D2` | 当前实现 |
| `kafka.producers.default.max-in-flight-requests-per-connection` | `1` | 一个连接同时最多发送多少个未确认请求,见 `D2` | 当前实现 |
环境变量名见 [`.env.example`](../.env.example);其余基础设施键见 `src/main/resources/application.yml`
## 指标与健康
| 指标 | 含义 | 关注信号 |
|---|---|---|
| `msgx.pipeline.backlog.unfinished` | 尚未处理完的消息数 | 长期不降:消息越积越多 |
| `msgx.pipeline.backlog.oldest_unprocessed_seconds` | 最早收到且尚未处理的消息距今多久 | 持续增长:最早的消息一直没处理 |
| `msgx.pipeline.backfill.unmarked_terminal` | 处理已结束、但信箱还没写入处理时间的消息数 | 长期不降:写回信箱受阻 |
| `msgx.pipeline.backfill.abandoned` | 已停止自动写回信箱的消息数 | 非零:需要人工对账 |
| `msgx.pipeline.backfill.oldest_unmarked_seconds` | 最早待写回信箱的消息距今多久 | 持续增长:写回越来越慢 |
| `msgx.pipeline.job.heartbeat_age_seconds` | 上次作业成功运行距今多久 | 持续增长:作业可能已停 |
| `msgx.pipeline.job.last_failure_age_seconds` | 上次作业出错距今多久 | 结合出错次数判断故障 |
| `msgx.pipeline.job.ticks.total` | 作业成功运行的次数 | 不增长:作业可能已停 |
| `msgx.pipeline.job.failures.total` | 作业出错次数 | 增长:作业持续出错 |
| `msgx.pipeline.job.last_sweep_selected` | 上次检查找到多少条待写回信箱的记录 | 持续达到每批上限:待办太多 |
| `msgx.pipeline.codec.srvt_seen.total` | 收到含 `SRVT` 段的消息数 | 非零:核对 `G-SRVT-VIPF` |
| `msgx.pipeline.codec.vipf_seen.total` | 收到含 `VIPF` 段的消息数 | 非零:核对 `G-SRVT-VIPF` |
| `msgx.pipeline.processing.ignored.total` | 被 `IgnoreRules` 跳过的消息数 | 清单含 `REGN``RSTA``EROR`,与 `US-13``US-09` 冲突 |
| `msgx.pipeline.watermark.lag` | 信箱最新编号比已扫描编号大多少 | 改用处理时间筛选后删除(`G-SCAN-PREDICATE` |
未处理消息的统计使用同一份缓存;统计不可用显示 `NaN`,没有记录可比时年龄与编号差显示 `-1`。作业计数在进程重启后归零;作业已启动却连续三个检查周期没有成功运行时,`/health``DOWN`
## 模块与代码入口
| 关注点 | 代码入口 |
|---|---|
| 读取信箱与 HTTP 入口 | `ingress/InboxPoller.kt``ingress/InboxController.kt` |
| XML 解析 | `codec/JacksonXmlCodec.kt``codec/SisWireMapper.kt` |
| 顺序处理与航班变更 | `processing/Pump.kt``processing/DynamicProcessors.kt``processing/ScheduleProcessor.kt` |
| 写回信箱与发送 Kafka | `processing/BackfillService.kt``delivery/Dispatcher.kt``infra/kafka/KafkaDeliveryPort.kt` |
| 定时任务 | `jobs/JobRunner.kt``jobs/HistorySweepJob.kt``jobs/EventCleanupJob.kt` |
| 数据库读写与手动重试 | `infra/persistence/jdbc/``infra/retry/` |
| 配置、指标与健康检查 | `config/``infra/metrics/``infra/health/` |
## 错误分类与重放白名单
| 错误类别 | 发生情况 | 处理结果 | 修复后可手动重试 |
|---|---|---|---|
| `MALFORMED` | 找不到原文,或 XML、必要字段无效 | 立即记 `DEAD`,停止自动处理 | 否 |
| `PROTOCOL` | 整份报文未通过业务校验,或运营日不一致 | 整份不写数据库,记 `DEAD` | 否 |
| `CODEC_ERROR` | 解码程序尚不能识别报文结构 | 记 `FAILED`,等待后重试 | 是 |
| `UNSUPPORTED` | 当前没有对应的处理程序 | 当前代码记 `FAILED` 并重试;`US-03` AC2 要求跳过合法未知类型 | 是 |
| `INFRA` | 数据库、网络或程序执行出错 | 记 `FAILED`,等待后重试 | 是 |
| `EXHAUSTED` | 自动重试次数已用尽 | 记 `DEAD`;原错误类别被覆盖,原因留在 `LAST_ERROR` | 是 |
停止自动写回信箱时,记录原因 `MISSING_ROW`(信箱行不存在)或 `TRANSIENT_DEADLINE`(故障持续到期限 `R`)。两种情况都没有确认处理时间已写入;恢复和清理条件见 implementation.md「回填」。