docs(kdoc): 重写信箱生命周期相关 KDoc,改为直白说明并去掉内部编号

原有注释大量引用 §5.1/§5.2/Q2/US-01 这类文档编号和内部简称,跳过了"这段代码在做什么、
为什么这么做",没有读过设计文档的人基本读不懂。本次统一改成先讲清这件事本身、
再说为什么要这样做,编号只在末尾留一处指路。

覆盖本次改动涉及的 24 个 Kotlin 文件(生产 16 个 + 测试 8 个):

- 领域与端口:ProcState(补齐全量字段说明与状态/错误分类逐项注释)、
  ProcStateRepository / InboxCursorRepository / CminmsgInboxRepository 及
  MailboxRow / BackfillDue / Backlog;
- 收报:InboxPoller(把"水位连续、遇缺口停下、缺口老化"用大白话讲透)、
  InboxService、JdbcCminmsgInboxRepository;
- 处理:Pump / MessageProcessor、ScheduleProcessor、DynamicProcessors、
  ProcFailure、JdbcProcStateRepository 与游标实现;
- 回填与观测:BackfillService、InboxLifecycleHealthIndicator、JobRunner;
- 配置与 stub:PipelineProps(三个新增参数说清取值理由)、MailboxProps、
  StubRepositories;
- 测试:8 个测试类改为"这些用例在守哪几条规矩",并保留 H2 不覆盖
  ON CONFLICT 的说明。

术语统一按第一次出现就地解释:水位、处理标记、回填、死信、队头、终态。
纯注释改动;除拆分枚举时按仓库风格补的两个行尾逗号外无代码变更
(已用剥离注释后比对 HEAD 的方式逐文件核对)。测试仍为 78 passed / 1 skipped。
This commit is contained in:
windyboy
2026-09-10 11:05:55 +08:00
parent 8c6bb83b62
commit d475feb790
24 changed files with 332 additions and 186 deletions
@@ -3,32 +3,72 @@ package com.gzzn.omms.msgexchange.domain
import java.time.Instant
/**
* PROC_STATE 处理伴生状态(docs/design.md §2.1/§2.3):每消息一行,
* MSG_ID = 信箱 ID 主键防重复入队;IDENTITY_KEY 唯一约束防业务重复;
* 处理状态机与错误分类同设计文档 §2.3。SUCCEEDED 终态兼作 SCHD 快照
* 重放判定(docs/flight-state.md §4 步骤 / design.md §4.1)。
* 一条入站消息的处理记录,一行对应共享信箱 CMINMSGS 里的一条报文。
*
* 回填事实(message-lifecycle.md §5.2/§11)与处理事实同体同行:终态与回填意图
* 由同一条 UPDATE 落下,因此不存在"业务已提交、回填待办未记"的崩溃窗口。
* 这张表同时承担四件事:
* 1. 防重复入队——主键是信箱 ID,同一条报文只会有一行;
* 2. 防业务重复——IDENTITY_KEY 唯一,同一条业务报文只处理一次;
* 3. 记录处理进度——状态、重试次数、下次重试时间和失败原因;
* 4. 记录回填进度——处理完要把"已处理"标记写回共享信箱,写成功之前一直留着待办意图。
*
* 第 4 条和终态写在同一条 UPDATE 里,所以不会出现"业务处理完了,却没人记得去回填"。
*/
enum class ProcStatus { PENDING, FAILED, SUCCEEDED, SKIPPED, DEAD }
enum class ProcStatus {
/** 已入队,等待处理。 */
PENDING,
/** 错误分类(design.md §2.3):MALFORMED/PROTOCOL 直接 DEAD 不重试;其余退避重试。 */
enum class ErrorClass { MALFORMED, PROTOCOL, CODEC_ERROR, EXHAUSTED, INFRA, UNSUPPORTED }
/** 处理失败,等退避时间到了再重试。 */
FAILED,
/** 处理成功。 */
SUCCEEDED,
/** 判定为业务重复,跳过不处理。 */
SKIPPED,
/** 处理失败且不再重试,等人工处置。 */
DEAD,
}
/** 失败原因分类,决定失败后是重试还是直接进死信。 */
enum class ErrorClass {
/** 报文本身不合法,重试也没用,直接进死信。 */
MALFORMED,
/** 整包被拒绝(运营日冲突、声明条数不符等),整包不落地,直接进死信。 */
PROTOCOL,
/** 解码逻辑的问题;修好 codec 之后可以重放。 */
CODEC_ERROR,
/** 重试次数用尽或队头滞留超时;人工复核后可以重放。 */
EXHAUSTED,
/** 数据库、网络等基础设施抖动,重试通常就能过。 */
INFRA,
/** 报文类型还没有对应处理器;属于能力未实现,先退避重试等补齐。 */
UNSUPPORTED,
}
data class ProcState(
/** 信箱 CMINMSGS_ID,也是本表主键。 */
val msgId: Long,
val state: ProcStatus,
val identityKey: String? = null, // SNDR|TYPE|STYP|SEQNdesign.md §2.2);decode 后首次绑定,FAILED 重试不重绑
/** 业务身份 SNDR|TYPE|STYP|SEQN;解码成功后绑定一次,重试不重绑。 */
val identityKey: String? = null,
/** 处理失败次数,用来算退避档位和判断是否已到上限。 */
val attempts: Int = 0,
/** FAILED 状态下,下次可以重试的时刻。 */
val nextAttemptAt: Instant? = null,
val errorClass: ErrorClass? = null,
/** 最近一次失败的原因(截断后落库,供排查)。 */
val lastError: String? = null,
/** 信箱 CMINMSGS_DATE_RECEIVED:§5.2 超期补写的 R 判据与 OPS-2「最老未处理信龄」锚点。 */
/** 信箱里的接收时间:用来判断"超期仍未回填",也是最老未处理信龄的计算依据。 */
val receivedAt: Instant? = null,
/** 非空 = 已确认信箱行持有处理标记(回填完成)。 */
/** 非空表示已确认信箱行带上了处理标记。 */
val backfillAt: Instant? = null,
/** 非空 = 待回填;终态事务内登记为 now,失败退避推后。 */
/** 非空表示还欠一次回填:写终态时置为当前时间,失败退避推后。 */
val backfillNextAt: Instant? = null,
val backfillAttempts: Int = 0,
val backfillError: String? = null,