package com.poyee.config; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; /** * 全局 Jackson 配置 *
* 注册时区感知序列化器 {@link LocalDateTimeZoneSerializerConfig}: * 所有响应中的 {@link LocalDateTime} 字段将根据请求头 {@code Accept-Language} * 自动从 UTC 转换为客户端对应的区域时间后输出。 *
* ** 注意:不使用 {@code JavaTimeModule} 注册自定义序列化器, * 因为 Spring Boot 自动配置的 {@code Jackson2ObjectMapperBuilder} 已注册过 {@code JavaTimeModule}, * Jackson 的模块去重机制会导致第二次注册同类型模块被静默跳过。 * 改用独立命名的 {@code SimpleModule} 可绕过去重检查,确保自定义序列化器生效。 *
*/ @Configuration public class JacksonConfig { private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; @Bean public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) { // 保留 Spring Boot 自动配置的所有默认模块,在此基础上叠加 ObjectMapper mapper = builder.createXmlMapper(false).build(); // 使用 SimpleModule(独立类名)注册,绕过 Jackson 对 JavaTimeModule 的去重跳过机制 SimpleModule overrideModule = new SimpleModule("LocalDateTimeZoneOverride"); // 序列化:UTC LocalDateTime → 客户端时区时间(根据 Accept-Language 自动转换) overrideModule.addSerializer(LocalDateTime.class, new LocalDateTimeZoneSerializerConfig()); // 反序列化:客户端传入时间字符串 → LocalDateTime(业务层保证传入 UTC) overrideModule.addDeserializer( LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN)) ); mapper.registerModule(overrideModule); // 禁用将时间序列化为时间戳 mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return mapper; } }