Files
msgexchange-v2/src/main/kotlin/com/gzzn/omms/msgexchange/codec/SisMessageBody.kt
T
windyboy 0e09365cf6 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 解码→合并往返用例
2026-09-21 12:20:50 +08:00

273 lines
16 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.gzzn.omms.msgexchange.codec
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText
import com.gzzn.omms.msgexchange.domain.flight.ScheduleRecord
/** Stable payload boundary used by processing. It is deliberately not an XML model. */
data class FlopPayload(
val flid: String,
val scalars: Map<String, String> = emptyMap(),
val collections: Map<String, List<Map<String, String>>> = emptyMap(),
)
data class ScheduleBody(
val recsDeclared: Int,
val records: List<ScheduleRecord>,
)
/**
* 只保留在 wire/domain、尚未映射到持久化明细的集合键(`[G-SRVT-VIPF]`)。
*
* 它们不参与合并、不进快照、不落库;保留的目的是不让入站事实在解码层被静默抹平,并让真实
* 流量里的出现情况可观测——决定"缺席是否等于删除"的 `Q2` 需要真实报文才能定案。
*/
private val UNPERSISTED_COLLECTION_KEYS: Set<String> = setOf("SRVT", "VIPF")
/**
* 一条解码载荷里各未落库集合命中的**记录数**`[G-SRVT-VIPF]`)。
*
* 按记录计(一条记录带该段即算 1),不按段内元素计;只返回命中项。载荷类型不在
* [ScheduleBody] / [FlopPayload] 之内时返回空——调用方不得据此改变处理结果。
*/
fun unpersistedCollectionHits(body: Any?): Map<String, Int> {
val perRecord = when (body) {
is ScheduleBody -> body.records.map { it.collections }
is FlopPayload -> listOf(body.collections)
else -> return emptyMap()
}
if (perRecord.isEmpty()) return emptyMap()
return UNPERSISTED_COLLECTION_KEYS
.mapNotNull { key -> perRecord.count { key in it }.takeIf { it > 0 }?.let { key to it } }
.toMap()
}
/**
* Wire DTOs for SIS in docs/legacy/SIS_AODB_RMS-V0.1.md.
* SIS permits a subsystem to ignore standard fields it does not use; those fields are
* therefore ignored here, while fields used by the state model are explicitly annotated.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JacksonXmlRootElement(localName = "MSG")
data class SisMessageXml(
@param:JacksonXmlProperty(localName = "META") val meta: SisMetaXml? = null,
@param:JacksonXmlProperty(localName = "SCHD") val schd: SchdXml? = null,
@param:JacksonXmlProperty(localName = "FLOP") val flop: FlightRecordXml? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class SisMetaXml(
@param:JacksonXmlProperty(localName = "SNDR") val sndr: String? = null,
@param:JacksonXmlProperty(localName = "SEQN") val seqn: Long? = null,
@param:JacksonXmlProperty(localName = "DTTM") val dttm: Long? = null,
@param:JacksonXmlProperty(localName = "TYPE") val type: String? = null,
@param:JacksonXmlProperty(localName = "STYP") val styp: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class SchdXml(
@param:JacksonXmlProperty(localName = "RECS") val recs: Int? = null,
@param:JacksonXmlElementWrapper(useWrapping = false)
@param:JacksonXmlProperty(localName = "FLTR") val fltr: List<FlightRecordXml> = emptyList(),
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class FlightRecordXml(
@param:JacksonXmlProperty(localName = "FLID") val flid: String? = null,
@param:JacksonXmlProperty(localName = "FDEL") val fdel: FdelXml? = null,
@param:JacksonXmlProperty(localName = "ALCD") val alcd: String? = null,
@param:JacksonXmlProperty(localName = "ALSC") val alsc: String? = null,
@param:JacksonXmlProperty(localName = "FLNO") val flno: String? = null,
@param:JacksonXmlProperty(localName = "MVIN") val mvin: String? = null,
@param:JacksonXmlProperty(localName = "SODT") val sodt: String? = null,
@param:JacksonXmlProperty(localName = "FLTY") val flty: String? = null,
@param:JacksonXmlProperty(localName = "FLIN") val flin: String? = null,
@param:JacksonXmlProperty(localName = "ACFT") val acft: String? = null,
@param:JacksonXmlProperty(localName = "RENO") val reno: String? = null,
@param:JacksonXmlProperty(localName = "TAOP") val taop: String? = null,
@param:JacksonXmlProperty(localName = "TAFL") val tafl: String? = null,
@param:JacksonXmlProperty(localName = "TAID") val taid: String? = null,
@param:JacksonXmlProperty(localName = "TRML") val trml: String? = null,
@param:JacksonXmlProperty(localName = "MAXP") val maxp: String? = null,
@param:JacksonXmlProperty(localName = "CSOP") val csop: String? = null,
@param:JacksonXmlProperty(localName = "CSFT") val csft: String? = null,
@param:JacksonXmlProperty(localName = "MAID") val maid: String? = null,
@param:JacksonXmlProperty(localName = "ESTT") val estt: String? = null,
@param:JacksonXmlProperty(localName = "ACTT") val actt: String? = null,
@param:JacksonXmlProperty(localName = "STND") val stnd: String? = null,
@param:JacksonXmlProperty(localName = "PHAG") val phag: String? = null,
@param:JacksonXmlProperty(localName = "CNCL") val cncl: String? = null,
@param:JacksonXmlProperty(localName = "REMC") val remc: String? = null,
@param:JacksonXmlProperty(localName = "BOTM") val botm: String? = null,
@param:JacksonXmlProperty(localName = "LACL") val lacl: String? = null,
@param:JacksonXmlProperty(localName = "FINT") val fint: String? = null,
@param:JacksonXmlProperty(localName = "APPT") val appt: String? = null,
@param:JacksonXmlProperty(localName = "EGSR") val egsr: String? = null,
@param:JacksonXmlProperty(localName = "EGST") val egst: String? = null,
@param:JacksonXmlProperty(localName = "FHAG") val fhag: String? = null,
@param:JacksonXmlProperty(localName = "MHAG") val mhag: String? = null,
@param:JacksonXmlProperty(localName = "VIPP") val vipp: String? = null,
@param:JacksonXmlProperty(localName = "VIPR") val vipr: String? = null,
@param:JacksonXmlProperty(localName = "LBNO") val lbno: String? = null,
@param:JacksonXmlProperty(localName = "LBWT") val lbwt: String? = null,
@param:JacksonXmlProperty(localName = "PAXC") val paxc: String? = null,
@param:JacksonXmlProperty(localName = "EXSC") val exsc: String? = null,
@param:JacksonXmlProperty(localName = "EXSR") val exsr: String? = null,
@param:JacksonXmlProperty(localName = "FTSS") val ftss: String? = null,
@param:JacksonXmlProperty(localName = "PEDT") val pedt: String? = null,
@param:JacksonXmlProperty(localName = "NAAT") val naat: String? = null,
@param:JacksonXmlProperty(localName = "NEAT") val neat: String? = null,
@param:JacksonXmlProperty(localName = "PADT") val padt: String? = null,
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "GTDT") val gtdt: List<GtdtXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "CKDT") val ckdt: List<CkdtXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "CLDT") val cldt: List<CldtXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "PSDT") val psdt: List<PsdtXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "CHDT") val chdt: List<ChdtXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "DELY") val dely: List<DelyXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "ABTM") val abtm: List<AbtmXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "CHOT") val chot: List<ChotXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "ROUT") val rout: List<RoutXml> = emptyList(),
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "ERUT") val erut: List<RoutXml> = emptyList(),
// SRVT/VIPF 用可空表达"段是否出现":null = 未出现;出现即为列表(空元素得到一行空行)。
// 两者都不落明细表、不参与合并,清空语义待 `Q2``[G-SRVT-VIPF]`)。
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "SRVT") val srvt: List<SrvtXml>? = null,
@param:JacksonXmlElementWrapper(useWrapping = false) @param:JacksonXmlProperty(localName = "VIPF") val vipf: List<VipfXml>? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
class FdelXml
@JsonIgnoreProperties(ignoreUnknown = true)
data class GtdtXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "GTNO") val gtno: String? = null,
@param:JacksonXmlProperty(localName = "GATE") val gate: String? = null,
@param:JacksonXmlProperty(localName = "PGOT") val pgot: String? = null,
@param:JacksonXmlProperty(localName = "PGCT") val pgct: String? = null,
@param:JacksonXmlProperty(localName = "GOTM") val gotm: String? = null,
@param:JacksonXmlProperty(localName = "GCTM") val gctm: String? = null,
@param:JacksonXmlProperty(localName = "GTYP") val gtyp: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class CkdtXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "CKNO") val ckno: String? = null,
@param:JacksonXmlProperty(localName = "CHKC") val chkc: String? = null,
@param:JacksonXmlProperty(localName = "CCLS") val ccls: String? = null,
@param:JacksonXmlProperty(localName = "PCOT") val pcot: String? = null,
@param:JacksonXmlProperty(localName = "PCCT") val pcct: String? = null,
@param:JacksonXmlProperty(localName = "COTM") val cotm: String? = null,
@param:JacksonXmlProperty(localName = "CCTM") val cctm: String? = null,
@param:JacksonXmlProperty(localName = "CTYP") val ctyp: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class CldtXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "CLNO") val clno: String? = null,
@param:JacksonXmlProperty(localName = "BELT") val belt: String? = null,
@param:JacksonXmlProperty(localName = "BCLS") val bcls: String? = null,
@param:JacksonXmlProperty(localName = "PCOT") val pcot: String? = null,
@param:JacksonXmlProperty(localName = "PCCT") val pcct: String? = null,
@param:JacksonXmlProperty(localName = "FBAG") val fbag: String? = null,
@param:JacksonXmlProperty(localName = "LBAG") val lbag: String? = null,
@param:JacksonXmlProperty(localName = "BTYP") val btyp: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class PsdtXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "PSNO") val psno: String? = null,
@param:JacksonXmlProperty(localName = "PSST") val psst: String? = null,
@param:JacksonXmlProperty(localName = "STST") val stst: String? = null,
@param:JacksonXmlProperty(localName = "STET") val stet: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class ChdtXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "CHNO") val chno: String? = null,
@param:JacksonXmlProperty(localName = "CHUT") val chut: String? = null,
// SIS 的 CHDT 类字段固定为 CCLS/CTYPimplementation.md「动态运行事件」);曾误绑 CHCLS/CHTYP
@param:JacksonXmlProperty(localName = "CCLS") val ccls: String? = null,
@param:JacksonXmlProperty(localName = "PCBT") val pcbt: String? = null,
@param:JacksonXmlProperty(localName = "PCET") val pcet: String? = null,
@param:JacksonXmlProperty(localName = "CBTM") val cbtm: String? = null,
@param:JacksonXmlProperty(localName = "CETM") val cetm: String? = null,
@param:JacksonXmlProperty(localName = "CTYP") val ctyp: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class DelyXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "CODE") val code: String? = null,
@param:JacksonXmlProperty(isAttribute = true, localName = "STRT") val strt: String? = null,
@param:JacksonXmlProperty(isAttribute = true, localName = "DURA") val dura: String? = null,
) {
@field:JacksonXmlText
var text: String? = null
}
@JsonIgnoreProperties(ignoreUnknown = true)
data class AbtmXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "ASNO") val asno: String? = null,
@param:JacksonXmlProperty(localName = "ABDG") val abdg: String? = null,
@param:JacksonXmlProperty(localName = "ABOP") val abop: String? = null,
@param:JacksonXmlProperty(localName = "AOTM") val aotm: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class ChotXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "CSNO") val csno: String? = null,
@param:JacksonXmlProperty(localName = "CHID") val chid: String? = null,
@param:JacksonXmlProperty(localName = "CHST") val chst: String? = null,
@param:JacksonXmlProperty(localName = "CHTM") val chtm: String? = null,
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class RoutXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "RTNO") val rtno: String? = null,
@param:JacksonXmlProperty(localName = "APCD") val apcd: String? = null,
@param:JacksonXmlProperty(localName = "SCAT") val scat: String? = null,
@param:JacksonXmlProperty(localName = "SCDT") val scdt: String? = null,
)
/**
* SIS `SRVT`/`SERVICEDATA`SCHD)与 `OPT_SERVICEDATA`FLOP):一次服务明细,可重复出现。
*
* 字段形态以 `docs/legacy/unisysaodbsis.xsd` 为准。`OPER` 是元素属性,SIS 正文未记载其值域;
* `SANR`/`SARR` 只出现在 FLOP 的 `OPT_SERVICEDATA`。协议未给序号属性,故没有源序号。
*/
@JsonIgnoreProperties(ignoreUnknown = true)
data class SrvtXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "OPER") val oper: String? = null,
@param:JacksonXmlProperty(localName = "SRTC") val srtc: String? = null,
@param:JacksonXmlProperty(localName = "SRQT") val srqt: String? = null,
@param:JacksonXmlProperty(localName = "SRST") val srst: String? = null,
@param:JacksonXmlProperty(localName = "SRET") val sret: String? = null,
@param:JacksonXmlProperty(localName = "SRPR") val srpr: String? = null,
@param:JacksonXmlProperty(localName = "SANR") val sanr: String? = null,
@param:JacksonXmlProperty(localName = "SARR") val sarr: String? = null,
)
/**
* SIS `VIPF`/`VIPDATA`SCHD)与 `OPT_VIPDATA`FLOP):一位 VIP,可重复出现。
*
* `VIPT` 按 XSD 至多一个;SIS 正文"每个 VIP 重复"的说法与之冲突,形态以 XSD 为准。
*/
@JsonIgnoreProperties(ignoreUnknown = true)
data class VipfXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "OPER") val oper: String? = null,
@param:JacksonXmlProperty(localName = "VPCD") val vpcd: String? = null,
@param:JacksonXmlProperty(localName = "VFES") val vfes: String? = null,
@param:JacksonXmlProperty(localName = "VIPT") val vipt: ViptXml? = null,
)
/** SIS `VIPT`/`VIPTXNDATA`VIP 关联的服务交易明细,嵌套在 [VipfXml] 内。 */
@JsonIgnoreProperties(ignoreUnknown = true)
data class ViptXml(
@param:JacksonXmlProperty(isAttribute = true, localName = "OPER") val oper: String? = null,
@param:JacksonXmlProperty(localName = "VSCD") val vscd: String? = null,
@param:JacksonXmlProperty(localName = "VTQY") val vtqy: String? = null,
@param:JacksonXmlProperty(localName = "VTST") val vtst: String? = null,
@param:JacksonXmlProperty(localName = "VTET") val vtet: String? = null,
)