From c0bcedbdc3d4852ad75a89980735642b04839123 Mon Sep 17 00:00:00 2001 From: rcx <3084692334@qq.com> Date: Sat, 5 Sep 2026 13:38:06 +0800 Subject: [PATCH] =?UTF-8?q?fix(wms,oms):=20JSON=E6=97=A5=E6=9C=9F=E5=8F=8D?= =?UTF-8?q?=E5=BA=8F=E5=88=97=E5=8C=96=E5=85=BC=E5=AE=B9yyyy-MM-dd?= =?UTF-8?q?=E7=BA=AF=E6=97=A5=E6=9C=9F=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 库存查询日期只保留年月日后,前端回传纯日期(如 createTime=2026-07-06)触发 @JsonFormat("yyyy-MM-dd HH:mm:ss") 严格格式反序列化 InvalidFormatException - 新增 FlexibleDateDeserializer:字段 @JsonFormat 模式优先严格解析,失败后兼容 yyyy-MM-dd HH:mm:ss / yyyy-MM-dd HH:mm / yyyy-MM-dd / 时间戳 / ISO-8601 - 仅在 OMS/WMS 通过 Module Bean 注册生效,不改动 BaseVOEntity 全局行为 --- .../jackson/FlexibleDateDeserializer.java | 112 ++++++++++++++++++ .../oms/config/DateCompatibilityConfig.java | 33 ++++++ .../wms/config/DateCompatibilityConfig.java | 33 ++++++ 3 files changed, 178 insertions(+) create mode 100644 mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/jackson/FlexibleDateDeserializer.java create mode 100644 mhd_oms/src/main/java/com/mhd/oms/config/DateCompatibilityConfig.java create mode 100644 mhd_wms/src/main/java/com/mhd/wms/config/DateCompatibilityConfig.java diff --git a/mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/jackson/FlexibleDateDeserializer.java b/mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/jackson/FlexibleDateDeserializer.java new file mode 100644 index 000000000..91dc9d344 --- /dev/null +++ b/mhd-common/mhd-common-core/src/main/java/com/mhd/common/core/jackson/FlexibleDateDeserializer.java @@ -0,0 +1,112 @@ +package com.mhd.common.core.jackson; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.ContextualDeserializer; + +import java.io.IOException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 宽松日期反序列化:兼容 yyyy-MM-dd(纯日期)等常见格式。 + * + * 背景:库存查询等接口日期只保留年月日后,前端可能把 "2026-07-06" 这类纯日期回传, + * 而实体/DTO 上 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 是严格格式,反序列化直接报 + * InvalidFormatException。本反序列化器解析顺序: + * 1) 字段 @JsonFormat 指定的模式(严格全匹配,保持原行为); + * 2) 兜底常见格式:yyyy-MM-dd HH:mm:ss → yyyy-MM-dd HH:mm → yyyy-MM-dd; + * 3) 纯数字按毫秒时间戳; + * 4) 交给 Jackson 默认解析(ISO-8601 等)。 + * 仅放宽接受范围,不影响任何既有格式的解析结果;由各服务 DateCompatibilityConfig 显式注册生效。 + * + * @author rcx + * @date 2026-09-05 + */ +public class FlexibleDateDeserializer extends JsonDeserializer implements ContextualDeserializer { + + /** 兜底格式按长度降序排列,避免长文本被短格式部分解析 */ + private static final String[] FALLBACK_PATTERNS = { + "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd" + }; + + /** 字段 @JsonFormat 指定的模式,可为空 */ + private final String pattern; + + public FlexibleDateDeserializer() { + this(null); + } + + public FlexibleDateDeserializer(String pattern) { + this.pattern = pattern; + } + + @Override + public Date deserialize(JsonParser parser, DeserializationContext ctxt) throws IOException { + String value = parser.getText(); + if (value == null || value.trim().isEmpty()) { + return null; + } + value = value.trim(); + + // 纯数字按毫秒时间戳(10位秒级时间戳也兼容) + if (value.matches("\\d{10,}")) { + return new Date(Long.parseLong(value)); + } + + // 1) 字段 @JsonFormat 指定的模式优先 + Date parsed = tryParse(value, pattern); + // 2) 兜底常见格式 + if (parsed == null) { + for (String fallback : FALLBACK_PATTERNS) { + parsed = tryParse(value, fallback); + if (parsed != null) { + break; + } + } + } + // 3) 交给 Jackson 默认解析(ISO-8601 等) + if (parsed == null) { + try { + parsed = ctxt.parseDate(value); + } catch (IllegalArgumentException ignore) { + // 走不到默认解析时统一在下方抛出友好异常 + } + } + if (parsed == null) { + throw ctxt.weirdStringException(value, Date.class, + "支持格式:yyyy-MM-dd HH:mm:ss / yyyy-MM-dd HH:mm / yyyy-MM-dd"); + } + return parsed; + } + + /** + * 严格全匹配解析:解析失败或文本未完整消费(如按 yyyy-MM-dd 解析 "2026-07-06 10:00:00") + * 都视为不匹配返回 null,交由下一个格式尝试 + */ + private Date tryParse(String value, String pattern) { + if (pattern == null || pattern.isEmpty()) { + return null; + } + SimpleDateFormat sdf = new SimpleDateFormat(pattern); + sdf.setLenient(false); + ParsePosition pos = new ParsePosition(0); + Date date = sdf.parse(value, pos); + return (date != null && pos.getIndex() == value.length()) ? date : null; + } + + @Override + public JsonDeserializer createContextual(DeserializationContext ctxt, BeanProperty property) { + if (property != null) { + JsonFormat jsonFormat = property.getAnnotation(JsonFormat.class); + if (jsonFormat != null && jsonFormat.pattern() != null && !jsonFormat.pattern().isEmpty()) { + return new FlexibleDateDeserializer(jsonFormat.pattern()); + } + } + return this; + } +} diff --git a/mhd_oms/src/main/java/com/mhd/oms/config/DateCompatibilityConfig.java b/mhd_oms/src/main/java/com/mhd/oms/config/DateCompatibilityConfig.java new file mode 100644 index 000000000..714c4c851 --- /dev/null +++ b/mhd_oms/src/main/java/com/mhd/oms/config/DateCompatibilityConfig.java @@ -0,0 +1,33 @@ +package com.mhd.oms.config; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.mhd.common.core.jackson.FlexibleDateDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Date; + +/** + * JSON 日期反序列化兼容配置:Date 字段兼容 yyyy-MM-dd(纯日期)等格式。 + * + * 背景:库存查询日期只保留年月日后,前端回传纯日期(如 createTime=2026-07-06)时, + * 实体/DTO 上 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 严格格式反序列化报 + * InvalidFormatException。注册 FlexibleDateDeserializer 后优先按字段 @JsonFormat + * 严格解析,失败时兼容纯日期等常见格式(与 WMS 库存查询一致)。 + * + * Spring Boot 会自动把容器中的 Module Bean 注册进 MVC 使用的 ObjectMapper。 + * + * @author rcx + * @date 2026-09-05 + */ +@Configuration +public class DateCompatibilityConfig { + + @Bean + public Module flexibleDateModule() { + SimpleModule module = new SimpleModule("oms-flexible-date-module"); + module.addDeserializer(Date.class, new FlexibleDateDeserializer()); + return module; + } +} diff --git a/mhd_wms/src/main/java/com/mhd/wms/config/DateCompatibilityConfig.java b/mhd_wms/src/main/java/com/mhd/wms/config/DateCompatibilityConfig.java new file mode 100644 index 000000000..8763207b7 --- /dev/null +++ b/mhd_wms/src/main/java/com/mhd/wms/config/DateCompatibilityConfig.java @@ -0,0 +1,33 @@ +package com.mhd.wms.config; + +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.mhd.common.core.jackson.FlexibleDateDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Date; + +/** + * JSON 日期反序列化兼容配置:Date 字段兼容 yyyy-MM-dd(纯日期)等格式。 + * + * 背景:库存查询日期只保留年月日后,前端回传纯日期(如 createTime=2026-07-06)时, + * 实体/DTO 上 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 严格格式反序列化报 + * InvalidFormatException。注册 FlexibleDateDeserializer 后优先按字段 @JsonFormat + * 严格解析,失败时兼容纯日期等常见格式(与 OMS 库存查询一致)。 + * + * Spring Boot 会自动把容器中的 Module Bean 注册进 MVC 使用的 ObjectMapper。 + * + * @author rcx + * @date 2026-09-05 + */ +@Configuration +public class DateCompatibilityConfig { + + @Bean + public Module flexibleDateModule() { + SimpleModule module = new SimpleModule("wms-flexible-date-module"); + module.addDeserializer(Date.class, new FlexibleDateDeserializer()); + return module; + } +}