feat(ingress): 实现 JDBC 信箱轮询、入站持久化与对拍测试 (U05)

This commit is contained in:
windyboy
2026-09-07 15:11:33 +08:00
parent dc68f1e1f8
commit c7b4b527ef
51 changed files with 1005 additions and 235 deletions
@@ -0,0 +1,82 @@
package com.gzzn.omms.msgexchange.jobs
import com.gzzn.omms.msgexchange.infra.redis.FlightRedisClient
import com.gzzn.omms.msgexchange.infra.redis.RedisScript
import com.gzzn.omms.msgexchange.infra.persistence.FlightStateRepository
import com.gzzn.omms.msgexchange.infra.persistence.MsgEventRepository
import com.gzzn.omms.msgexchange.infra.persistence.PumpJobRepository
import com.gzzn.omms.msgexchange.domain.MsgEvent
import com.gzzn.omms.msgexchange.domain.Targets
import jakarta.inject.Singleton
/**
* ACMA-8:泵作业执行器。cron 触发入队(PUMP_JOB),主泵 FIFO 执行(决策 1)——
* 产物不绕过队头顺序;阶段 A 的清场同步链与归档在此落地(I4/I5)。
*/
@Singleton
class JobExecutor(
private val historySweep: HistorySweepJob,
private val archive: ArchiveJob,
private val projectionRebuild: ProjectionRebuildJob,
) {
fun execute(job: PumpJobRepository.Job) = when (job.kind) {
"HISTORY_SWEEP" -> historySweep.run()
"ARCHIVE" -> archive.run()
"PROJECTION_REBUILD" -> projectionRebuild.run()
else -> error("unknown job ${job.kind}")
}
}
/**
* 流程 4 runJob(HISTORY_SWEEP):3:30 清场——主泵作业内同步链(I4,KEEP 现役
* FlightHisScheduled 同步语义):判史 → 同步写 ES(成功集)→ 仅删成功集。
*/
@Singleton
class HistorySweepJob(
private val redis: FlightRedisClient,
// TODO(阶段2): esFlightHts.saveSync(history) 返回成功集(同步写,不经事件 ack 回查)
) {
fun run() {
val all = redis.hgetAllFlightInfo()
val history = pickHistory(all) // U10/T07:saveSync 接线前判史为空集——禁止“全量可删”默认
val success = emptyList<String>() // TODO(阶段2): esFlightHts.saveSync(history)(返回成功集后再接线删除)
if (success.isNotEmpty()) {
redis.eval(RedisScript.BATCH_DELETE, delFields = success)
}
}
/** U10/T07(修订):接入 ES success 集之前的门禁——占位默认返回空集;
* 现役五条判史规则(SODT 3 天 / CNCL 1 小时 / 备降 / 离港 / 到港)golden 通过后才允许接线。 */
private fun pickHistory(all: Map<String, String>): Map<String, String> = emptyMap() // TODO(阶段2)
}
/** 流程 5 runJob(ARCHIVE):3:00 归档——1 天前且仅终态可迁(矩阵 #12)。 */
@Singleton
class ArchiveJob(
// TODO(阶段1后续): CMINMSGS⇆CMINMSGS_HST 迁移 SQL:JOIN PROC_STATE,STATE ∈ {SUCCEEDED,DEAD,SKIPPED}
) {
fun run() {
// TODO: 迁移(DEAD 带 ERROR_CLASS、SKIPPED 带 duplicate-of 审计随行保留)
}
}
/** 流程 7:阶段 B 投影重建——切入阶段 B 时全量重建一次,此后增量走事件。 */
@Singleton
class ProjectionRebuildJob(
private val flightState: FlightStateRepository,
private val msgEvents: MsgEventRepository,
) {
fun run() {
for (day in activeDays()) {
val flights = flightState.findByDay(day)
flights.forEach { (flid, payload) ->
msgEvents.insertSync(
listOf(MsgEvent(target = Targets.REDIS_FLIGHT_INFO, partitionKey = flid, payloadJson = payload)),
)
}
}
// 重建完成前旧 Redis 视图继续服务(数据同源,仅短暂滞后)
}
private fun activeDays(): List<String> = emptyList() // TODO(阶段B)
}