fix(codec): CHDT 类字段统一为 SIS 口径 CCLS/CTYP(ACM2-90)

- ChdtXml wire 绑定与 SisWireMapper 映射键改为 CCLS/CTYP(implementation.md「动态运行事件」)
- 新增 V2 迁移将自有 PG FLIGHT_CHUTE 列 chcls/chtyp 更名为 ccls/ctyp,不动已发布的 V1
- FlywayMigrationTest 更新迁移链断言并校验更名后列;补 CHDT 解码→合并往返用例
This commit is contained in:
windyboy
2026-09-21 12:20:50 +08:00
parent 29465e2131
commit 0e09365cf6
6 changed files with 72 additions and 10 deletions
@@ -1,6 +1,10 @@
package com.gzzn.omms.msgexchange.codec
import com.gzzn.omms.msgexchange.domain.MsgKind
import com.gzzn.omms.msgexchange.domain.flight.FlightSnapshot
import com.gzzn.omms.msgexchange.domain.flight.FlightState
import com.gzzn.omms.msgexchange.domain.flight.FlightStateEngine
import com.gzzn.omms.msgexchange.domain.flight.MergeChange
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertInstanceOf
@@ -271,4 +275,36 @@ class JacksonXmlCodecTest {
assertEquals(MsgKind.Unsupported("FLOP-CNCL"), (codec.decode(raw("CNCL")) as DecodeResult.Ok).message.kind)
assertEquals(MsgKind.Unsupported("FLOP-"), (codec.decode(raw("")) as DecodeResult.Ok).message.kind)
}
@Test
fun `CHDT round trip keeps SIS class fields CCLS and CTYP from decode to merged state`() {
val raw = """
<MSG><META><SNDR>AODB</SNDR><SEQN>9</SEQN><DTTM>20021010090311</DTTM>
<TYPE>FLOP</TYPE><STYP>CHDT</STYP></META>
<FLOP><FLID>121112312</FLID>
<CHDT CHNO="1"><CHUT>C01</CHUT><CCLS>A</CCLS><CTYP>IN</CTYP></CHDT>
</FLOP></MSG>
""".trimIndent()
val payload = ((codec.decode(raw) as DecodeResult.Ok).message.body as FlopPayload)
// wire 层:CCLS/CTYP 键在、CHCLS/CHTYP 键不在
val row = payload.collections.getValue("CHDT").single()
assertEquals("A", row["CCLS"])
assertEquals("IN", row["CTYP"])
assertFalse(row.containsKey("CHCLS"))
assertFalse(row.containsKey("CHTYP"))
// 合并层:MergeChange 带进航班快照的 CHDT 明细
val current = FlightSnapshot(
"121112312", java.time.LocalDate.of(2026, 12, 15), FlightState.ACTIVE, 1,
scalars = mapOf("FLNO" to "CA001"),
collections = emptyMap(),
)
val next = FlightStateEngine.mergedState(
current,
MergeChange("121112312", scalars = emptyMap(), collections = payload.collections),
)
assertEquals(listOf(row), next.collections["CHDT"])
}
}
@@ -10,10 +10,11 @@ import org.junit.jupiter.api.Test
import java.sql.DriverManager
/**
* 在真实 PostgreSQL 上跑一遍迁移,确认结果符合预期:单基线 `V1__flight_state_baseline.sql`
* 执行成功且是唯一的迁移记录,该建的表和单行种子(PIPELINE_LOCK、INBOX_CURSOR)都在,
* 回填事实与收报水位都落在基线里,`BACKFILL_TODO`、`idx_evt_flid`、`PROC_STATE` 的处理开始
* 时间列都不复存在——这些正是原 V2–V10 合并后的净结构。
* 在真实 PostgreSQL 上跑一遍迁移,确认结果符合预期:`V1__flight_state_baseline.sql`
* 基线加 `V2__flight_chute_class_type_rename.sql` 更名依序执行成功,该建的表和单行种子
* PIPELINE_LOCK、INBOX_CURSOR)都在,回填事实与收报水位都落在基线里,`BACKFILL_TODO`、
* `idx_evt_flid`、`PROC_STATE` 的处理开始时间列都不复存在;FLIGHT_CHUTE 的类字段列
* 已由 V2 更名为 CCLS/CTYPSIS 口径)。
*
* 没有可用的 PostgreSQL 时跳过(不假装通过)。
*/
@@ -42,7 +43,7 @@ class FlywayMigrationTest {
DriverManager.getConnection(url, user, pass).use { conn ->
conn.createStatement().use { stmt ->
// 单基线:原 V1–V10 已合并为一条迁移,历史里只应留下 V1
// 迁移链:V1 基线 + V2 类字段更名,依序成功
stmt.executeQuery(
"SELECT version, script, success FROM flyway_schema_history ORDER BY installed_rank ASC",
).use { rs ->
@@ -50,12 +51,24 @@ class FlywayMigrationTest {
while (rs.next()) {
records.add(Triple(rs.getString("version"), rs.getString("script"), rs.getBoolean("success")))
}
assertEquals(1, records.size, "迁移链必须收敛为单基线,不得再有 V2+ 条目")
assertEquals(2, records.size, "迁移链应为 V1 基线 + V2 更名两条")
assertEquals("1", records[0].first)
assertEquals("V1__flight_state_baseline.sql", records[0].second)
assertEquals("2", records[1].first)
assertEquals("V2__flight_chute_class_type_rename.sql", records[1].second)
assertTrue(records.all { it.third })
}
// V2 更名后 FLIGHT_CHUTE 类字段为 SIS 口径的 ccls/ctypCHCLS/CHTYP 不复存在
stmt.executeQuery(
"SELECT column_name FROM information_schema.columns WHERE table_name = 'flight_chute' " +
"AND column_name IN ('ccls', 'ctyp', 'chcls', 'chtyp')",
).use { rs ->
val cols = mutableSetOf<String>()
while (rs.next()) cols.add(rs.getString("column_name"))
assertEquals(setOf("ccls", "ctyp"), cols, "FLIGHT_CHUTE 类字段必须已更名为 ccls/ctyp")
}
// 全表就绪(回填事实并入 PROC_STATE,基线不再建 BACKFILL_TODO
stmt.executeQuery(
"SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'",