diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java index 64005fc8d..adc6fbee1 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java @@ -5,6 +5,7 @@ import tools.jackson.databind.json.JsonMapper; import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Supplier; /** * 单次响应增强上下文。 @@ -16,6 +17,8 @@ public class JsonEnhancementContext { private final Map attributes = new LinkedHashMap<>(); + private boolean processingRequired; + /** * 构造响应增强上下文。 * @@ -37,6 +40,20 @@ public class JsonEnhancementContext { return (T) attributes.get(key); } + /** + * 获取上下文属性,不存在时创建并写入。 + * + * @param key 属性键 + * @param supplier 属性值创建器 + * @param 属性值类型 + * @return 属性值 + */ + @SuppressWarnings("unchecked") + public T getOrCreateAttribute(String key, Supplier supplier) { + Object value = attributes.computeIfAbsent(key, ignored -> supplier.get()); + return (T) value; + } + /** * 设置上下文属性。 * @@ -47,4 +64,25 @@ public class JsonEnhancementContext { attributes.put(key, value); } + /** + * 判断上下文是否包含指定属性。 + */ + public boolean containsAttribute(String key) { + return attributes.containsKey(key); + } + + /** + * 移除上下文属性。 + */ + public void removeAttribute(String key) { + attributes.remove(key); + } + + /** + * 标记本次响应存在需要处理的字段。 + */ + public void markProcessingRequired() { + this.processingRequired = true; + } + } diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java index 9613023ab..ad165908d 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java @@ -20,6 +20,14 @@ package org.dromara.common.json.enhance; */ public interface JsonFieldProcessor { + /** + * 判断当前处理器是否需要处理该字段。 + * 默认返回 true 以兼容无注解驱动的自定义处理器。 + */ + default boolean supports(JsonFieldContext fieldContext) { + return true; + } + /** * 收集阶段:扫描字段,将需要处理的 key 存入 context。 * 每个字段调用一次,整个对象树扫描完成后才会进入 prepare 阶段。 diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java index 68dd8da22..62c93975f 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java @@ -54,7 +54,13 @@ public class JsonValueEnhancer { if (body == null || body instanceof JsonNode || processors.isEmpty()) { return body; } - return enhanceTree(body); + JsonEnhancementContext context = new JsonEnhancementContext(jsonMapper); + collectValue(body, context, new IdentityHashMap<>()); + if (!context.isProcessingRequired()) { + return body; + } + processors.forEach(processor -> processor.prepare(context)); + return renderValue(body, context, new IdentityHashMap<>()); } /** @@ -73,6 +79,9 @@ public class JsonValueEnhancer { private JsonNode enhanceTree(Object value) { JsonEnhancementContext context = new JsonEnhancementContext(jsonMapper); collectValue(value, context, new IdentityHashMap<>()); + if (!context.isProcessingRequired()) { + return jsonMapper.valueToTree(value); + } processors.forEach(processor -> processor.prepare(context)); return renderValue(value, context, new IdentityHashMap<>()); } @@ -103,7 +112,7 @@ public class JsonValueEnhancer { for (PropertyMetadata metadata : getProperties(value.getClass())) { Object propertyValue = metadata.getValue(value); JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), propertyValue); - processors.forEach(processor -> processor.collect(fieldContext, context)); + collectField(fieldContext, context); collectValue(propertyValue, context, visited); } } finally { @@ -111,6 +120,15 @@ public class JsonValueEnhancer { } } + private void collectField(JsonFieldContext fieldContext, JsonEnhancementContext context) { + for (JsonFieldProcessor processor : processors) { + if (processor.supports(fieldContext)) { + context.markProcessingRequired(); + processor.collect(fieldContext, context); + } + } + } + private JsonNode renderValue(Object value, JsonEnhancementContext context, IdentityHashMap visited) { switch (value) { case null -> { @@ -120,27 +138,16 @@ public class JsonValueEnhancer { return jsonNode; } case Map map -> { - ObjectNode objectNode = jsonMapper.createObjectNode(); - map.forEach((key, childValue) -> objectNode.set(String.valueOf(key), renderValue(childValue, context, visited))); - return objectNode; + return renderMap(map, context, visited); } case Iterable iterable -> { - ArrayNode arrayNode = jsonMapper.createArrayNode(); - for (Object child : iterable) { - arrayNode.add(renderValue(child, context, visited)); - } - return arrayNode; + return renderIterable(iterable, context, visited); } default -> { } } if (value.getClass().isArray()) { - ArrayNode arrayNode = jsonMapper.createArrayNode(); - int length = Array.getLength(value); - for (int i = 0; i < length; i++) { - arrayNode.add(renderValue(Array.get(value, i), context, visited)); - } - return arrayNode; + return renderArray(value, context, visited); } if (isSimpleValue(value.getClass())) { return jsonMapper.valueToTree(value); @@ -149,28 +156,58 @@ public class JsonValueEnhancer { return jsonMapper.valueToTree(value); } try { - ObjectNode objectNode = jsonMapper.createObjectNode(); - for (PropertyMetadata metadata : getProperties(value.getClass())) { - Object originalValue = metadata.getValue(value); - JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), originalValue); - Object processedValue = originalValue; - boolean changed = false; - for (JsonFieldProcessor processor : processors) { - Object nextValue = processor.process(fieldContext, processedValue, context); - changed = changed || !Objects.equals(processedValue, nextValue); - processedValue = nextValue; - } - JsonNode childNode = changed - ? enhanceTranslatedValue(processedValue, context, visited) - : renderValue(processedValue, context, visited); - objectNode.set(metadata.propertyName(), childNode); - } - return objectNode; + return renderPojo(value, context, visited); } finally { visited.remove(value); } } + private ObjectNode renderMap(Map map, JsonEnhancementContext context, IdentityHashMap visited) { + ObjectNode objectNode = jsonMapper.createObjectNode(); + map.forEach((key, childValue) -> objectNode.set(String.valueOf(key), renderValue(childValue, context, visited))); + return objectNode; + } + + private ArrayNode renderIterable(Iterable iterable, JsonEnhancementContext context, IdentityHashMap visited) { + ArrayNode arrayNode = jsonMapper.createArrayNode(); + for (Object child : iterable) { + arrayNode.add(renderValue(child, context, visited)); + } + return arrayNode; + } + + private ArrayNode renderArray(Object value, JsonEnhancementContext context, IdentityHashMap visited) { + ArrayNode arrayNode = jsonMapper.createArrayNode(); + int length = Array.getLength(value); + for (int i = 0; i < length; i++) { + arrayNode.add(renderValue(Array.get(value, i), context, visited)); + } + return arrayNode; + } + + private ObjectNode renderPojo(Object value, JsonEnhancementContext context, IdentityHashMap visited) { + ObjectNode objectNode = jsonMapper.createObjectNode(); + for (PropertyMetadata metadata : getProperties(value.getClass())) { + Object originalValue = metadata.getValue(value); + JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), originalValue); + Object processedValue = originalValue; + boolean changed = false; + for (JsonFieldProcessor processor : processors) { + if (!processor.supports(fieldContext)) { + continue; + } + Object nextValue = processor.process(fieldContext, processedValue, context); + changed = changed || !Objects.equals(processedValue, nextValue); + processedValue = nextValue; + } + JsonNode childNode = changed + ? enhanceTranslatedValue(processedValue, context, visited) + : renderValue(processedValue, context, visited); + objectNode.set(metadata.propertyName(), childNode); + } + return objectNode; + } + private JsonNode enhanceTranslatedValue(Object value, JsonEnhancementContext context, IdentityHashMap visited) { if (value == null || value instanceof JsonNode || isSimpleValue(value.getClass())) { return renderValue(value, context, visited); diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java index 7e8a61522..65a35063f 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java @@ -2,7 +2,6 @@ package org.dromara.common.json.handler; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; -import org.dromara.common.core.utils.ObjectUtils; import tools.jackson.core.JsonParser; import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.ValueDeserializer; @@ -25,10 +24,11 @@ public class CustomDateDeserializer extends ValueDeserializer { */ @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) { - DateTime parse = DateUtil.parse(p.getString()); - if (ObjectUtils.isNull(parse)) { + String text = p.getString(); + if (text == null || text.isBlank()) { return null; } + DateTime parse = DateUtil.parse(text.trim()); return parse.toJdkDate(); } diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java index aa327cf92..b3863d5e2 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java @@ -19,6 +19,10 @@ import java.util.List; */ public class CustomLocalDateTimeDeserializer extends ValueDeserializer { + private static final int SECOND_TIMESTAMP_LENGTH = 10; + + private static final int MILLIS_TIMESTAMP_LENGTH = 13; + /** 支持时间的格式列表(直接解析为 LocalDateTime) */ private static final List DATETIME_FORMATTERS = List.of( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), @@ -52,11 +56,10 @@ public class CustomLocalDateTimeDeserializer extends ValueDeserializer> getOrCreateBatches(JsonEnhancementContext context) { - Map> batches = context.getAttribute(ATTR_BATCHES); - if (batches == null) { - batches = new LinkedHashMap<>(); - context.setAttribute(ATTR_BATCHES, batches); - } - return batches; + return context.getOrCreateAttribute(ATTR_BATCHES, LinkedHashMap::new); } /**