package cn.iocoder.yudao.framework.common.util.json.databind; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.extern.slf4j.Slf4j; import tools.jackson.core.JacksonException; import tools.jackson.core.JsonGenerator; import tools.jackson.databind.SerializationContext; import tools.jackson.databind.ser.std.StdScalarSerializer; import java.lang.reflect.Field; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 基于时间戳的 LocalDateTime 序列化器 * * @author 老五 */ @Slf4j public class TimestampLocalDateTimeSerializer extends StdScalarSerializer { public static final TimestampLocalDateTimeSerializer INSTANCE = new TimestampLocalDateTimeSerializer(); private static final Map, Map> FIELD_CACHE = new ConcurrentHashMap<>(); public TimestampLocalDateTimeSerializer() { super(LocalDateTime.class); } @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializationContext serializers) throws JacksonException { // 情况一:有 JsonFormat 自定义注解,则使用它。https://github.com/YunaiV/ruoyi-vue-pro/pull/1019 String fieldName = gen.streamWriteContext().currentName(); if (fieldName != null) { Object currentValue = gen.currentValue(); if (currentValue != null) { Class clazz = currentValue.getClass(); Map fieldMap = FIELD_CACHE.computeIfAbsent(clazz, this::buildFieldMap); Field field = fieldMap.get(fieldName); // 进一步修复:https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1480 if (field != null && field.isAnnotationPresent(JsonFormat.class)) { JsonFormat jsonFormat = field.getAnnotation(JsonFormat.class); try { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(jsonFormat.pattern()); gen.writeString(formatter.format(value)); return; } catch (Exception ex) { log.warn("[serialize][({}#{}) 使用 JsonFormat pattern 失败,尝试使用默认的 Long 时间戳]", clazz.getName(), fieldName, ex); } } } } // 情况二:默认将 LocalDateTime 对象,转换为 Long 时间戳 gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); } /** * 构建字段映射(缓存) * * @param clazz 类 * @return 字段映射 */ private Map buildFieldMap(Class clazz) { Map fieldMap = new HashMap<>(); for (Field field : ReflectUtil.getFields(clazz)) { String fieldName = field.getName(); JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class); if (jsonProperty != null) { String value = jsonProperty.value(); if (StrUtil.isNotEmpty(value) && ObjUtil.notEqual("\u0000", value)) { fieldName = value; } } fieldMap.put(fieldName, field); } return fieldMap; } }