wwh 1 тиждень тому
батько
коміт
ae4b06938d

+ 19 - 0
baqing-shop/src/test/java/com/ruoyi/web/FastJson2OpenStatsCacheTest.java

@@ -51,4 +51,23 @@ class FastJson2OpenStatsCacheTest
51 51
         Object cached = serializer.deserialize(serializer.serialize(vo));
52 52
         Assertions.assertTrue(cached instanceof CategorySalesVO);
53 53
     }
54
+
55
+    @Test
56
+    void deserializeJsonWithJavaLongLiteralSuffix()
57
+    {
58
+        String legacy = CATEGORY_SALES_JSON.replace(":15L", ":15").replace(":1L", ":1");
59
+        String withLongSuffix = legacy.replace("\"qty\":15", "\"qty\":15L").replace("\"categoryId\":1", "\"categoryId\":1L");
60
+        FastJson2JsonRedisSerializer<Object> serializer = new FastJson2JsonRedisSerializer<>(Object.class);
61
+        Object cached = serializer.deserialize(withLongSuffix.getBytes(StandardCharsets.UTF_8));
62
+        Assertions.assertTrue(cached instanceof CategorySalesVO);
63
+    }
64
+
65
+    @Test
66
+    void deserializeJacksonFormatWithoutTypeHint()
67
+    {
68
+        String jacksonJson = "{\"statYear\":2026,\"totalQty\":15,\"items\":[{\"categoryId\":1,\"categoryName\":\"兽药\",\"qty\":15,\"ratio\":100.0}]}";
69
+        FastJson2JsonRedisSerializer<Object> serializer = new FastJson2JsonRedisSerializer<>(Object.class);
70
+        Object cached = serializer.deserialize(jacksonJson.getBytes(StandardCharsets.UTF_8));
71
+        Assertions.assertNotNull(cached);
72
+    }
54 73
 }

+ 15 - 1
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java

@@ -11,7 +11,10 @@ import org.springframework.data.redis.core.BoundSetOperations;
11 11
 import org.springframework.data.redis.core.HashOperations;
12 12
 import org.springframework.data.redis.core.RedisTemplate;
13 13
 import org.springframework.data.redis.core.ValueOperations;
14
+import org.springframework.data.redis.serializer.SerializationException;
14 15
 import org.springframework.stereotype.Component;
16
+import org.slf4j.Logger;
17
+import org.slf4j.LoggerFactory;
15 18
 
16 19
 /**
17 20
  * spring redis 工具类
@@ -22,6 +25,8 @@ import org.springframework.stereotype.Component;
22 25
 @Component
23 26
 public class RedisCache
24 27
 {
28
+    private static final Logger log = LoggerFactory.getLogger(RedisCache.class);
29
+
25 30
     @Autowired
26 31
     public RedisTemplate redisTemplate;
27 32
 
@@ -105,7 +110,16 @@ public class RedisCache
105 110
     public <T> T getCacheObject(final String key)
106 111
     {
107 112
         ValueOperations<String, T> operation = redisTemplate.opsForValue();
108
-        return operation.get(key);
113
+        try
114
+        {
115
+            return operation.get(key);
116
+        }
117
+        catch (SerializationException ex)
118
+        {
119
+            log.warn("Redis 缓存反序列化失败,已删除损坏 key={},原因={}", key, ex.getMessage());
120
+            deleteObject(key);
121
+            return null;
122
+        }
109 123
     }
110 124
 
111 125
     /**

+ 18 - 4
ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java

@@ -4,7 +4,9 @@ import java.util.Collection;
4 4
 import java.util.HashMap;
5 5
 import java.util.List;
6 6
 import java.util.Map;
7
+import com.alibaba.fastjson2.JSON;
7 8
 import com.alibaba.fastjson2.JSONArray;
9
+import com.alibaba.fastjson2.JSONObject;
8 10
 import com.ruoyi.common.constant.CacheConstants;
9 11
 import com.ruoyi.common.core.domain.entity.SysDictData;
10 12
 import com.ruoyi.common.core.redis.RedisCache;
@@ -41,12 +43,24 @@ public class DictUtils
41 43
      */
42 44
     public static List<SysDictData> getDictCache(String key)
43 45
     {
44
-        JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
45
-        if (StringUtils.isNotNull(arrayCache))
46
+        Object arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
47
+        if (StringUtils.isNull(arrayCache))
46 48
         {
47
-            return arrayCache.toList(SysDictData.class);
49
+            return null;
48 50
         }
49
-        return null;
51
+        if (arrayCache instanceof List)
52
+        {
53
+            return JSON.parseArray(JSON.toJSONString(arrayCache), SysDictData.class);
54
+        }
55
+        if (arrayCache instanceof JSONArray)
56
+        {
57
+            return ((JSONArray) arrayCache).toList(SysDictData.class);
58
+        }
59
+        if (arrayCache instanceof JSONObject)
60
+        {
61
+            return JSON.parseArray(JSON.toJSONString(arrayCache), SysDictData.class);
62
+        }
63
+        return JSON.parseArray(JSON.toJSONString(arrayCache), SysDictData.class);
50 64
     }
51 65
 
52 66
     /**

+ 60 - 3
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java

@@ -1,6 +1,7 @@
1 1
 package com.ruoyi.framework.config;
2 2
 
3 3
 import java.nio.charset.Charset;
4
+import java.util.regex.Pattern;
4 5
 import org.springframework.data.redis.serializer.RedisSerializer;
5 6
 import org.springframework.data.redis.serializer.SerializationException;
6 7
 import com.alibaba.fastjson2.JSON;
@@ -8,6 +9,8 @@ import com.alibaba.fastjson2.JSONReader;
8 9
 import com.alibaba.fastjson2.JSONWriter;
9 10
 import com.alibaba.fastjson2.JSONObject;
10 11
 import com.alibaba.fastjson2.filter.Filter;
12
+import com.fasterxml.jackson.databind.DeserializationFeature;
13
+import com.fasterxml.jackson.databind.ObjectMapper;
11 14
 import com.ruoyi.common.constant.Constants;
12 15
 
13 16
 /**
@@ -21,6 +24,11 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
21 24
 
22 25
     static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR);
23 26
 
27
+    private static final Pattern LONG_LITERAL = Pattern.compile("(\\d+)L(?=[,\\}\\]\\s])");
28
+
29
+    private static final ObjectMapper JACKSON = new ObjectMapper()
30
+            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
31
+
24 32
     private Class<T> clazz;
25 33
 
26 34
     public FastJson2JsonRedisSerializer(Class<T> clazz)
@@ -46,7 +54,7 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
46 54
         {
47 55
             return null;
48 56
         }
49
-        String str = normalizeLegacyCollectionSyntax(new String(bytes, DEFAULT_CHARSET));
57
+        String str = unwrapNestedJsonString(normalizeLegacyCollectionSyntax(new String(bytes, DEFAULT_CHARSET)));
50 58
         try
51 59
         {
52 60
             Object parsed = JSON.parseObject(str, clazz, AUTO_TYPE_FILTER, JSONReader.Feature.SupportAutoType);
@@ -66,9 +74,52 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
66 74
             }
67 75
             catch (Exception fallbackEx)
68 76
             {
69
-                throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
77
+                try
78
+                {
79
+                    return readWithJackson(str);
80
+                }
81
+                catch (Exception jacksonEx)
82
+                {
83
+                    throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
84
+                }
85
+            }
86
+        }
87
+    }
88
+
89
+    @SuppressWarnings("unchecked")
90
+    private T readWithJackson(String str) throws Exception
91
+    {
92
+        if (clazz == Object.class || clazz == null)
93
+        {
94
+            return (T) JACKSON.readValue(str, Object.class);
95
+        }
96
+        return JACKSON.readValue(str, clazz);
97
+    }
98
+
99
+    /** 外层被 FastJSON 再包一层引号时解包 */
100
+    private static String unwrapNestedJsonString(String str)
101
+    {
102
+        if (str == null)
103
+        {
104
+            return null;
105
+        }
106
+        String trimmed = str.trim();
107
+        if (trimmed.length() >= 2 && trimmed.startsWith("\"") && trimmed.endsWith("\""))
108
+        {
109
+            try
110
+            {
111
+                String inner = JSON.parseObject(trimmed, String.class);
112
+                if (inner != null && (inner.startsWith("{") || inner.startsWith("[")))
113
+                {
114
+                    return inner;
115
+                }
116
+            }
117
+            catch (Exception ignored)
118
+            {
119
+                // keep original
70 120
             }
71 121
         }
122
+        return str;
72 123
     }
73 124
 
74 125
     @SuppressWarnings("unchecked")
@@ -112,6 +163,12 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
112 163
         {
113 164
             return str;
114 165
         }
115
-        return str.replace("LinkedHashSet[", "[").replace("HashSet[", "[").replace("Set[", "[");
166
+        return LONG_LITERAL.matcher(str
167
+                .replace("LinkedHashSet[", "[")
168
+                .replace("HashSet[", "[")
169
+                .replace("TreeSet[", "[")
170
+                .replace("Set[", "[")
171
+                .replace("ArrayList[", "[")
172
+                .replace("LinkedList[", "[")).replaceAll("$1");
116 173
     }
117 174
 }