From d909a881672d417e4b00f5c09e92a9494ccfd52b Mon Sep 17 00:00:00 2001 From: windyboy Date: Fri, 11 Sep 2026 14:25:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(codec):=20=E8=A7=A3=E7=A0=81=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E5=A4=B1=E8=B4=A5=E4=BA=A7=E5=87=BA=20CODEC=5FERROR?= =?UTF-8?q?=EF=BC=8C=E8=90=BD=E5=AE=9E=E9=80=80=E9=81=BF=E9=87=8D=E8=AF=95?= =?UTF-8?q?=E4=B8=8E=E5=8F=AF=E9=87=8D=E6=94=BE=E7=99=BD=E5=90=8D=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../omms/msgexchange/codec/JacksonXmlCodec.kt | 9 +++++++++ .../msgexchange/codec/JacksonXmlCodecTest.kt | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/main/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodec.kt b/src/main/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodec.kt index 2b7248a..a310bf2 100644 --- a/src/main/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodec.kt +++ b/src/main/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodec.kt @@ -1,6 +1,7 @@ package com.gzzn.omms.msgexchange.codec import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.JsonMappingException import com.fasterxml.jackson.module.kotlin.registerKotlinModule import com.fasterxml.jackson.dataformat.xml.XmlFactory import com.fasterxml.jackson.dataformat.xml.XmlMapper @@ -34,6 +35,14 @@ class JacksonXmlCodec : XmlCodec { } val msg = try { mapper.readValue(trimmed, SisMessageXml::class.java) + } catch (e: JsonMappingException) { + // META 层绑定失败=发送方违反必填契约(MALFORMED);体层(SCHD/FLOP)结构不匹配 + // =wire 结构超出编解码器当前绑定能力(CODEC_ERROR:退避重试,修复后可重放,design §2.3)。 + val first = e.path.firstOrNull()?.fieldName ?: "" + if (first.equals("meta", ignoreCase = true)) { + return DecodeResult.Err(DecodeFailure(ErrorClass.MALFORMED, "xml-meta:${e.originalMessage.take(200)}")) + } + return DecodeResult.Err(DecodeFailure(ErrorClass.CODEC_ERROR, "xml-mapping:${e.originalMessage.take(200)}")) } catch (e: Exception) { return DecodeResult.Err( DecodeFailure(ErrorClass.MALFORMED, "xml-parse:${e.message?.take(200) ?: e.javaClass.simpleName}"), diff --git a/src/test/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodecTest.kt b/src/test/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodecTest.kt index 386a61e..259187b 100644 --- a/src/test/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodecTest.kt +++ b/src/test/kotlin/com/gzzn/omms/msgexchange/codec/JacksonXmlCodecTest.kt @@ -94,6 +94,26 @@ class JacksonXmlCodecTest { assertTrue(err.failure.detail.contains("empty-or-non-xml")) } + @Test + fun `body shape beyond binding capability is CODEC_ERROR while meta violations stay MALFORMED`() { + val meta = "AODB120021010090311SCHDDNLD" + val recsAsText = "$metanot-a-number" + val bodyErr = codec.decode(recsAsText) as DecodeResult.Err + assertEquals(com.gzzn.omms.msgexchange.domain.ErrorClass.CODEC_ERROR, bodyErr.failure.errorClass) + assertTrue(bodyErr.failure.detail.startsWith("xml-mapping")) + + val badSeqn = "AODBabc20021010090311SCHDDNLD" + val metaErr = codec.decode(badSeqn) as DecodeResult.Err + assertEquals(com.gzzn.omms.msgexchange.domain.ErrorClass.MALFORMED, metaErr.failure.errorClass) + } + + @Test + fun `broken token stream stays MALFORMED`() { + val err = codec.decode("") as DecodeResult.Err + assertEquals(com.gzzn.omms.msgexchange.domain.ErrorClass.MALFORMED, err.failure.errorClass) + assertTrue(err.failure.detail.startsWith("xml-parse")) + } + @Test fun `SIS documented attributes and mixed DELY text become typed payload`() { val raw = """