fix(wms,oms): JSON日期反序列化兼容yyyy-MM-dd纯日期格式
- 库存查询日期只保留年月日后,前端回传纯日期(如 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 全局行为
This commit is contained in:
+112
@@ -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<Date> 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user