feat(persistence): FLIGHT_SCHD_DISPLAY 兼容视图 + 生产读切明细权威 (ACM2-29 P2-2)

- V1.3.1 迁移:flight_schd_display 视图投影首个/第二个资源与总数(gate1/gate2/
  gate_total、chkc1/chkc2/checkin_total、belt1/belt_total、dely_code/strt),
  完整明细仍在各明细表可查;PG LATERAL 方言,11g 随 P3-B 单独提供
- 生产读路径 mapFlightRow 切 assembleDetailOnly:集合键仅由明细表重建,
  不再回退 V1.2.0 槽位/里程碑列(过渡双读仅保留在 FS7 对拍工具内)
- FlightStateEngine 正确性修复:序号属性 "0" 条目现在正确翻译为显式 Clear
  (原实现 items 非空走 Replace,会存出 GTNO=0 脏明细行);混入常规条目的
  0 标记 fail fast 拒绝猜测(v2 §4 非法结构显式失败)
- JDBC 用例全部切 v2 写路径(persistNextStates),删除 legacy 写路径断言;
  新增:显示视图投影/替换同步用例、明细级联删除断言、引擎 0 标记语义单测

验证:MSGX_PG_PORT=5433 真实 PG ./gradlew test --rerun-tasks
97 用例 0 失败 0 跳过
This commit is contained in:
windyboy
2026-09-08 12:11:57 +08:00
parent 7abe3fbcd7
commit 44fbac0d8b
5 changed files with 348 additions and 92 deletions
@@ -106,4 +106,40 @@ class FlightStateEngineTest {
)
assertEquals("NEW", next.collections["GTDT"]!![0]["GATE"])
}
@Test
fun `zero sequence marker alone translates to explicit clear`() {
val commands = FlightStateEngine.commandsFromFields(
"F1",
mapOf("GTDT" to """[{"GTNO":"0"}]"""),
snapshotReplace = false,
)
assertEquals(CollectionCommand.Clear, commands.collections["GTDT"])
}
@Test
fun `zero sequence marker mixed with regular items fails fast instead of guessing`() {
val error = runCatching {
FlightStateEngine.commandsFromFields(
"F1",
mapOf("GTDT" to """[{"GTNO":"1","GATE":"A1"},{"GTNO":"0"}]"""),
snapshotReplace = false,
)
}.exceptionOrNull()
assertTrue(error is IllegalArgumentException)
}
@Test
fun `empty array replaces collection with empty set keeping key semantics`() {
val current = FlightNextState(
"F1",
emptyMap(),
mapOf("GTDT" to listOf(mapOf("GTNO" to "1", "GATE" to "A1"))),
1L,
"m1",
)
val commands = FlightStateEngine.commandsFromFields("F1", mapOf("GTDT" to "[]"), snapshotReplace = false)
val next = FlightStateEngine.apply(current, commands, "m2", bumpVersion = true)
assertEquals(emptyList<Map<String, String>>(), next.collections["GTDT"])
}
}