- ChdtXml wire 绑定与 SisWireMapper 映射键改为 CCLS/CTYP(implementation.md「动态运行事件」) - 新增 V2 迁移将自有 PG FLIGHT_CHUTE 列 chcls/chtyp 更名为 ccls/ctyp,不动已发布的 V1 - FlywayMigrationTest 更新迁移链断言并校验更名后列;补 CHDT 解码→合并往返用例
273 lines
16 KiB
Kotlin
273 lines
16 KiB
Kotlin
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/CTYP(implementation.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,
|
||
)
|