docs(kdoc): 其余模块注释统一为直白说明

把上一提交没覆盖到的模块也改完,至此仓库内不再有"只写编号、不写说明"的注释。

涉及 33 个文件(生产 24 + 测试 9),包括航班状态模型与合并引擎、事件与留痕模型、
运营日计算、投递器、历史归档作业、编解码与身份、重试与重放、健康指示器、
生命周期装配、以及各自的测试类。

改法与前面一致:先说这段代码做什么,再说为什么这么做,术语第一次出现就地解释
(FLID、OPERATION_DAY、航班版本号、删除通知、待发事件表等)。工单编号
(U07/N02/ACM2-10 之类)已全部移除;段落编号只剩 10 处,全部带文件名位于句尾,
用作延伸阅读,例如"见 docs/flight-state.md §5"。

保留了本来就自解释的行内注释、PgTestSupport 顶部的环境变量默认值表格,
以及 InboxController 里描述待补接口的 TODO。

校验:逐文件做"去注释后比对"(块注释 + 行注释清除、空白归一),33 个文件代码
零差异;另做逐行非注释代码比对,同样零差异。clean test 为 78 passed / 1 skipped。
This commit is contained in:
windyboy
2026-09-10 11:14:11 +08:00
parent 6e7d819034
commit 1183c7522b
33 changed files with 283 additions and 210 deletions
@@ -10,11 +10,13 @@ import jakarta.inject.Singleton
import org.reactivestreams.Publisher
/**
* U12R05):自定义健康指示器——
* Kafka(投递端口)。经 BeanProvider 可选解析:
* 缺 bean(如未用 stub 也未实装)时指示 DOWN 而非启动失败;
* UP 判据为真实 pingfalse/异常 → DOWN),而非仅 bean 存在(复审 P1 修正)。
* ACM2-28Redis 退出阶段 A 权威与写路径,redis-flight-store 指示器移除)
* 把 Kafka 投递端口的状态报给 /health。
*
* 用 BeanProvider 可选注入:没配 stub、也还没有真实实现时,端口 bean 根本不存在,这时报 DOWN
* 而不是让服务起不来。判 UP 的依据是真的调一次 ping——返回 false 或抛异常都算 DOWN,
* 不是"bean 在就算好"
*
* Redis 已经不再参与航班状态的写入和判定,对应的健康指示器也一并删掉了。
*/
@Singleton
class KafkaDeliveryHealthIndicator(
@@ -3,8 +3,8 @@ package com.gzzn.omms.msgexchange.infra.log
import org.slf4j.MDC
/**
* U12R05):处理路径入口写入 MDC traceId=cminmsgsId/eventId),
* 使一条消息全链路日志可串(logback %X{traceId} + logstash includeMdcKeyName
* 处理一条消息时把它的 ID(信箱 ID 或事件 ID)放进 MDC traceId,用 withTrace 把整段处理
* 逻辑包起来;日志配置里带上 %X{traceId},同一条消息的日志就能串到一起
*/
object TraceLog {
fun <T> withTrace(id: Any, body: () -> T): T {
@@ -233,7 +233,10 @@ interface ReqTrackRepository {
fun findLatest(reqType: String, operationDay: LocalDate, sender: String, states: List<ReqState>): Req?
/** RESP 完成请求(design.md §4.2):匹配最新一条 PENDING/SENT;无匹配返回 false(迟到不报错)。 */
/**
* 应答报文到达时把对应请求标记为已完成:匹配最近一条待应答的请求(PENDING 或 SENT)。
* 找不到返回 false——迟到或多余的应答不算错误。
*/
fun completeLatest(reqType: String, operationDay: LocalDate, sender: String): Boolean
fun linkCoutmsgs(reqId: Long, coutmsgsId: Long)
@@ -7,28 +7,29 @@ import java.time.Clock
import java.time.Instant
/**
* 统一重试策略(ACM2-10 U08):供 MessageProcessor / SnapshotFlowProcState 侧)与
* DispatcherMsgEvent 侧)共用——attempts 递增后按 backoff 表给 nextAttemptAt
* exhausted 判定与两侧同源(maxAttempts)。时间一律经可注入 Clock(测试用固定钟,避免脆弱睡眠)。
* 重试节奏的唯一出处:处理失败的入站消息和待发事件都用它算下次重试时间,保证两边口径一致。
*
* 重试次数加一之后,按退避表算出下次可以重试的时刻;"次数是否已经用尽"也在这里判断,
* 上限取 msgx.pipeline.max-attempts。时间一律走注入的 Clock,测试可以塞固定时钟,不用 sleep。
*/
@Singleton
class FailureScheduler(
private val props: PipelineProps,
private val clock: Clock,
) {
/** 便捷构造:默认系统时钟(生产路径)。 */
/** 生产环境用这个构造:时钟取系统 UTC 时间。 */
constructor(props: PipelineProps) : this(props, Clock.systemUTC())
fun now(): Instant = clock.instant()
fun exhausted(attempts: Int): Boolean = attempts >= props.pipeline.maxAttempts
/** attempts 指递增后的值;N28attempt ≤ 0 由 backoffFor 兜底为首档。 */
/** 传入的 attempts 是已经加一之后的值。若传 0 或负数(比如 FAILED 行还没记过次数),退避表会兜底给第一档。 */
fun nextAttemptAt(attemptsAfterIncrement: Int): Instant =
now().plusMillis(props.pipeline.backoffFor(attemptsAfterIncrement))
}
/** 提供可注入 Clockjava.time.Clock);测试可用 Clock.fixed(...) 或自定义可变钟覆盖。 */
/** Clock 注册成可注入的 bean;测试里可以换成 Clock.fixed(...) 或自己写的可推进时钟。 */
@Factory
class TimeFactory {
@Singleton
@@ -5,19 +5,21 @@ import com.gzzn.omms.msgexchange.infra.persistence.ProcStateRepository
import jakarta.inject.Singleton
/**
* U11 显式重放入口:只允许“可恢复”的错误类从 FAILED/DEAD 回 PENDING主泵重领)
* 不可恢复类(MALFORMED——报文非法,重放必再失败)与未知类一律不在白名单内。
* 人工重放入口:把失败的消息从 FAILED/DEAD 回 PENDING,让主泵重新领走处理
*
* 只有"再试一次有可能成功"的错误类才放行。报文本身不合法的(MALFORMED)重放多少次都一样,
* 所以不在白名单里;没列出的错误类也一律不放行。
*/
@Singleton
class ReplayService(
private val procState: ProcStateRepository,
) {
private val log = org.slf4j.LoggerFactory.getLogger(ReplayService::class.java)
/** 可恢复错误类:codec 修复可重放 / 未实装补齐可重放 / 基础设施抖动可重放 / 重试耗尽人工复核可重放。 */
/** 可以重放的错误类:解码逻辑修好后能过、处理器补齐后能过、基础设施抖动已恢复、以及重试耗尽人工复核认为还能再试的。 */
val replayableErrorClasses: Set<ErrorClass> =
setOf(ErrorClass.CODEC_ERROR, ErrorClass.UNSUPPORTED, ErrorClass.INFRA, ErrorClass.EXHAUSTED)
/** 只放白名单内的类;请求 MALFORMED 等非法类时静默忽略该类。 */
/** 只放白名单内的错误类;请求里带了 MALFORMED 这类不可重放的,直接忽略不报错。 */
fun replay(requested: Collection<ErrorClass>): Int {
val allowed = requested.filter { it in replayableErrorClasses }
if (allowed.isEmpty()) {
@@ -29,6 +31,6 @@ class ReplayService(
return n
}
/** 默认入口:重放全部可恢复类。 */
/** 什么都不指定时走这个:把白名单里的错误类全部重放一遍。 */
fun replayAll(): Int = replay(replayableErrorClasses)
}
@@ -5,8 +5,10 @@ import io.micronaut.context.annotation.Requires
import jakarta.inject.Singleton
/**
* stub 适配层——DeliveryPort 内存实现,仅在 msgx.stubs=true 时生效
* 记录 (topic, key, payload)payload = null 表示 TOMBSTONEflight-state.md §5 键缺失=删除旧值)。
* 投递端口的内存实现,只有配置 msgx.stubs=true 时才装配,本地开发和测试用
*
* 每次发送都往 sent 里记一条(topic、key、payload);payload 为 null 的那条就是删除通知
* tombstone,下游按"这个键没了"理解成删除)。见 docs/flight-state.md §5。
*/
@Requires(property = "msgx.stubs", value = "true")
@Singleton