diff --git a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongArraySerializer.java b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongArraySerializer.java new file mode 100644 index 00000000..1b59ee0a --- /dev/null +++ b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongArraySerializer.java @@ -0,0 +1,43 @@ +package com.zt.plat.framework.common.util.json.databind; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +/** + * Long / long 数组序列化器,统一按字符串输出以规避 JS 精度问题。 + */ +public class LongArraySerializer extends StdSerializer { + + public static final LongArraySerializer INSTANCE = new LongArraySerializer(); + + private LongArraySerializer() { + super(Object.class); + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartArray(); + if (value instanceof long[]) { + long[] array = (long[]) value; + for (long element : array) { + // 原生 long 必定有值,直接走 NumberSerializer + NumberSerializer.INSTANCE.serialize(element, gen, provider); + } + gen.writeEndArray(); + return; + } + + Long[] array = (Long[]) value; + for (Long element : array) { + if (element == null) { + provider.defaultSerializeNull(gen); + continue; + } + NumberSerializer.INSTANCE.serialize(element, gen, provider); + } + gen.writeEndArray(); + } +} diff --git a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongCollectionSerializer.java b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongCollectionSerializer.java new file mode 100644 index 00000000..45d94283 --- /dev/null +++ b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongCollectionSerializer.java @@ -0,0 +1,37 @@ +package com.zt.plat.framework.common.util.json.databind; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import com.fasterxml.jackson.databind.type.TypeFactory; + +import java.io.IOException; +import java.util.Collection; + +/** + * 将 {@link Collection} 中的 Long 元素序列化成字符串,避免 JavaScript 精度问题。 + */ +public class LongCollectionSerializer extends StdSerializer> { + + public static final LongCollectionSerializer INSTANCE = new LongCollectionSerializer(); + + private LongCollectionSerializer() { + super(TypeFactory.defaultInstance().constructCollectionType(Collection.class, Object.class)); + } + + @Override + public void serialize(Collection value, JsonGenerator gen, SerializerProvider provider) throws IOException { + // 传入集合本身与元素数量,方便 Jackson 合理推测数组边界 + gen.writeStartArray(value, value.size()); + for (Object element : value) { + if (element == null) { + // 允许集合中存在 null,保持 Jackson 默认的 null 序列化行为 + provider.defaultSerializeNull(gen); + continue; + } + // 所有 Long/long 元素统一走 NumberSerializer,保证前端精度 + NumberSerializer.INSTANCE.serialize((Number) element, gen, provider); + } + gen.writeEndArray(); + } +} diff --git a/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongTypeSerializerModifier.java b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongTypeSerializerModifier.java new file mode 100644 index 00000000..f19f3024 --- /dev/null +++ b/zt-framework/zt-common/src/main/java/com/zt/plat/framework/common/util/json/databind/LongTypeSerializerModifier.java @@ -0,0 +1,52 @@ +package com.zt.plat.framework.common.util.json.databind; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.type.ArrayType; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.databind.type.CollectionLikeType; +import com.fasterxml.jackson.databind.JavaType; + +/** + * 针对 Long 相关集合、数组的序列化增强,确保统一走 Long 的自定义序列化逻辑。 + */ +public class LongTypeSerializerModifier extends BeanSerializerModifier { + + @Override + public JsonSerializer modifyCollectionSerializer(SerializationConfig config, CollectionType valueType, + BeanDescription beanDesc, JsonSerializer serializer) { + // List、Set 等容器若包含 Long,则切换到 LongCollectionSerializer + return needsLongCollectionSerializer(valueType.getContentType()) ? LongCollectionSerializer.INSTANCE : serializer; + } + + @Override + public JsonSerializer modifyCollectionLikeSerializer(SerializationConfig config, CollectionLikeType valueType, + BeanDescription beanDesc, JsonSerializer serializer) { + // 处理 CollectionLike(如 Page、Optional 等)中的 Long 元素 + return needsLongCollectionSerializer(valueType.getContentType()) ? LongCollectionSerializer.INSTANCE : serializer; + } + + @Override + public JsonSerializer modifyArraySerializer(SerializationConfig config, ArrayType valueType, + BeanDescription beanDesc, JsonSerializer serializer) { + // 针对 long[]、Long[] 两种数组使用统一的数组序列化器 + Class rawClass = valueType.getRawClass(); + if (long[].class.equals(rawClass)) { + return LongArraySerializer.INSTANCE; + } + if (Long[].class.equals(rawClass)) { + return LongArraySerializer.INSTANCE; + } + return serializer; + } + + private boolean needsLongCollectionSerializer(JavaType contentType) { + if (contentType == null) { + return false; + } + Class rawClass = contentType.getRawClass(); + return Long.class.equals(rawClass) || Long.TYPE.equals(rawClass); + } +} diff --git a/zt-gateway/src/main/java/com/zt/plat/gateway/jackson/JacksonAutoConfiguration.java b/zt-gateway/src/main/java/com/zt/plat/gateway/jackson/JacksonAutoConfiguration.java index 1bc54e1b..ef394ad0 100644 --- a/zt-gateway/src/main/java/com/zt/plat/gateway/jackson/JacksonAutoConfiguration.java +++ b/zt-gateway/src/main/java/com/zt/plat/gateway/jackson/JacksonAutoConfiguration.java @@ -2,6 +2,7 @@ package com.zt.plat.gateway.jackson; import cn.hutool.core.collection.CollUtil; import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.json.databind.LongTypeSerializerModifier; import com.zt.plat.framework.common.util.json.databind.NumberSerializer; import com.zt.plat.framework.common.util.json.databind.TimestampLocalDateTimeDeserializer; import com.zt.plat.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; @@ -39,6 +40,7 @@ public class JacksonAutoConfiguration { // 新增 LocalDateTime 序列化、反序列化规则,使用 Long 时间戳 .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE) .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE); + simpleModule.setSerializerModifier(new LongTypeSerializerModifier()); // 1.2 注册到 objectMapper objectMappers.forEach(objectMapper -> objectMapper.registerModule(simpleModule));