package com.gzzn.omms.msgexchange.ingress import com.gzzn.omms.msgexchange.config.PipelineProps import com.gzzn.omms.msgexchange.infra.persistence.CminmsgInboxRepository import jakarta.inject.Singleton /** * ACMA-8 流程 1 · 主路径:JDBC 轮询共享 MySQL CMINMSGS(`DATE_PROCESSED IS NULL`), * 发现新信后入队自有 PG。与 legacy `MsgExchangeRunner.getNewMsgsAfterId(0L)` 同语义。 */ @Singleton class InboxPoller( private val inbox: CminmsgInboxRepository, private val enqueue: InboxEnqueue, private val props: PipelineProps, ) { private val log = org.slf4j.LoggerFactory.getLogger(InboxPoller::class.java) @Volatile private var running = false /** legacy 现役:afterId=0,每轮扫全部未处理行;PROC_STATE 判重防重复入队。 */ fun pollOnce(): Int { val batch = props.pipeline.claimBatch.coerceAtLeast(1) val ids = inbox.pollUnprocessed(afterId = 0L, limit = batch) var enqueued = 0 for (id in ids) { if (enqueue.enqueue(id)) { enqueued++ log.info("polled cminmsgsId={}", id) } } return enqueued } fun loop() { running = true log.info("inbox poller loop started") while (running) { try { pollOnce() sleepQuietly(props.pipeline.pollInterval) } catch (_: InterruptedException) { Thread.currentThread().interrupt() break } catch (e: Exception) { log.error("inbox poller tick failed", e) sleepQuietly(props.pipeline.pollInterval) } } log.info("inbox poller loop stopped") } fun stop() { running = false } private fun sleepQuietly(d: java.time.Duration) { try { Thread.sleep(d.toMillis().coerceAtLeast(1)) } catch (_: InterruptedException) { Thread.currentThread().interrupt() } } }