feat(ingress): 实现 JDBC 信箱轮询、入站持久化与对拍测试 (U05)
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user