From 2af4b2c7a32e2cec645249792af76e5d692d338a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Sat, 16 May 2026 14:08:52 +0800 Subject: [PATCH] =?UTF-8?q?refactor(common-json):=20=E4=BC=98=E5=8C=96=20J?= =?UTF-8?q?SON=20=E5=A2=9E=E5=BC=BA=E5=A4=84=E7=90=86=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抽取 JSON 字符串校验的重复解析逻辑 - 为 JsonFieldProcessor 增加 supports 判断,减少无关字段处理 - 优化 JsonValueEnhancer,未命中增强字段时直接返回原对象 - 拆分响应增强渲染逻辑,提升代码可读性 - 增强上下文属性管理能力 - 优化翻译、脱敏处理器的字段匹配逻辑 - 修正 Date 反序列化空字符串处理 - 支持 LocalDateTime 秒级、毫秒级及负时间戳反序列化 --- .../json/enhance/JsonEnhancementContext.java | 38 +++++++ .../json/enhance/JsonFieldProcessor.java | 8 ++ .../json/enhance/JsonValueEnhancer.java | 103 ++++++++++++------ .../json/handler/CustomDateDeserializer.java | 6 +- .../CustomLocalDateTimeDeserializer.java | 36 +++++- .../dromara/common/json/utils/JsonUtils.java | 34 +++--- .../handler/SensitiveJsonFieldProcessor.java | 5 + .../TranslationJsonFieldProcessor.java | 18 ++- 8 files changed, 180 insertions(+), 68 deletions(-) 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); } /**