feat(acm2-92): RQRD 人工登记与 POST /refdata/sync(C-12)

- 新增 POST /refdata/sync:STYP 14 类校验,RSTA 须带 RTYP
- REQ_TRACK 加 styp/rtyp 列(V8 迁移);RQRD/RQFD 各自单开放槽
- OutboundRequestService 加 registerRqrdSync,派发按行目标编码
- Pump 加 REF-RESP 守卫:无在途 RQRD 时 SKIPPED
- 回归测试覆盖 C-12 登记/派发/400/409/独立槽/守卫
This commit is contained in:
windyboy
2026-09-23 16:10:54 +08:00
parent f45a0ac898
commit 4fbb974307
28 changed files with 568 additions and 68 deletions
@@ -5,6 +5,8 @@ import com.gzzn.omms.msgexchange.codec.JacksonXmlCodec
import com.gzzn.omms.msgexchange.config.OperationDayProps
import com.gzzn.omms.msgexchange.config.PipelineProps
import com.gzzn.omms.msgexchange.domain.OutboundRequestKeys
import com.gzzn.omms.msgexchange.domain.RqfdTimeFilter
import com.gzzn.omms.msgexchange.ingress.SchdSyncService
import com.gzzn.omms.msgexchange.infra.persistence.CoutmsgOutboxRepository
import com.gzzn.omms.msgexchange.infra.persistence.ReqTrackRepository
import com.gzzn.omms.msgexchange.infra.stub.StubCoutmsgOutbox
@@ -45,6 +47,28 @@ class OutboundRequestTest {
assertTrue(outbox.messages.values.first().contains("<TYPE>RQFD</TYPE>"))
}
@Test
fun `schd sync writes the page time filter into COUTMSGS and leaves out the rest`() {
val req = StubReqTrack(clock)
val outbox = StubCoutmsgOutbox()
val svc = service(req, outbox)
svc.registerRqfdSync(RqfdTimeFilter(stdb = "12JAN041730"))
val xml = outbox.messages.values.first()
assertTrue(xml.contains("<STDB>12JAN041730</STDB>"))
assertFalse(xml.contains("STDE"))
assertFalse(xml.contains("ETDB"))
assertFalse(xml.contains("ETDE"))
}
@Test
fun `invalid time filter is rejected before registration`() {
val req = StubReqTrack(clock)
val sync = SchdSyncService(service(req, StubCoutmsgOutbox()))
assertEquals(SchdSyncService.Outcome.InvalidTime, sync.trigger(stdb = "today"))
assertTrue(req.rows.isEmpty())
}
@Test
fun `open slot rejects duplicate schd sync registration`() {
val req = StubReqTrack(clock)
@@ -108,4 +132,71 @@ class OutboundRequestTest {
assertTrue(svc.failFromEror(42L, "RQFD", "NONE"))
assertEquals(ReqTrackRepository.ReqState.FAILED, req.rows[reqId]!!.state)
}
@Test
fun `refdata sync registers AIRL and dispatches RQRD into COUTMSGS`() {
val req = StubReqTrack(clock)
val outbox = StubCoutmsgOutbox()
val sync = com.gzzn.omms.msgexchange.ingress.RefdataSyncService(service(req, outbox))
val outcome = sync.trigger("AIRL", null)
assertTrue(outcome is com.gzzn.omms.msgexchange.ingress.RefdataSyncService.Outcome.Registered)
val xml = outbox.messages.values.first()
assertTrue(xml.contains("<TYPE>RQRD</TYPE>"))
assertTrue(xml.contains("<STYP>AIRL</STYP>"))
}
@Test
fun `refdata sync writes RTYP only for RSTA`() {
val req = StubReqTrack(clock)
val outbox = StubCoutmsgOutbox()
val svc = service(req, outbox)
svc.registerRqrdSync(com.gzzn.omms.msgexchange.domain.RqrdTarget("RSTA", "GATE"))
val xml = outbox.messages.values.first()
assertTrue(xml.contains("<STYP>RSTA</STYP>"))
assertTrue(xml.contains("<RTYP>GATE</RTYP>"))
}
@Test
fun `refdata sync rejects unknown styp and rtyp misuse before registration`() {
val req = StubReqTrack(clock)
val sync = com.gzzn.omms.msgexchange.ingress.RefdataSyncService(service(req, StubCoutmsgOutbox()))
assertEquals(
com.gzzn.omms.msgexchange.ingress.RefdataSyncService.Outcome.UnknownStyp,
sync.trigger("NOPE", null),
)
assertEquals(
com.gzzn.omms.msgexchange.ingress.RefdataSyncService.Outcome.MissingRtyp,
sync.trigger("RSTA", null),
)
assertEquals(
com.gzzn.omms.msgexchange.ingress.RefdataSyncService.Outcome.UnexpectedRtyp,
sync.trigger("AIRL", "GATE"),
)
assertTrue(req.rows.isEmpty())
}
@Test
fun `open slot rejects duplicate refdata sync registration`() {
val req = StubReqTrack(clock)
val sync = com.gzzn.omms.msgexchange.ingress.RefdataSyncService(service(req, StubCoutmsgOutbox()))
sync.trigger("AIRL", null)
assertEquals(
com.gzzn.omms.msgexchange.ingress.RefdataSyncService.Outcome.OpenExists,
sync.trigger("AIRL", null),
)
}
@Test
fun `RQRD and RQFD use separate open slots`() {
val req = StubReqTrack(clock)
val rqrd = com.gzzn.omms.msgexchange.ingress.RefdataSyncService(service(req, StubCoutmsgOutbox()))
val rqfd = SchdSyncService(service(req, StubCoutmsgOutbox()))
rqrd.trigger("AIRL", null)
assertTrue(rqfd.trigger() is SchdSyncService.Outcome.Registered)
}
}