Bladeren bron

会员管理代码

wwh 1 week geleden
bovenliggende
commit
9ba1e50bbc

+ 38 - 21
baqing-shop/src/main/java/com/ruoyi/web/modules/openstats/service/impl/MallStatsOpenServiceImpl.java

@@ -14,6 +14,8 @@ import java.util.concurrent.ConcurrentHashMap;
14 14
 import java.util.concurrent.TimeUnit;
15 15
 import org.springframework.beans.factory.annotation.Autowired;
16 16
 import org.springframework.stereotype.Service;
17
+import com.alibaba.fastjson2.JSON;
18
+import com.alibaba.fastjson2.JSONObject;
17 19
 import com.ruoyi.common.core.redis.RedisCache;
18 20
 import com.ruoyi.common.utils.StringUtils;
19 21
 import com.ruoyi.web.modules.openstats.constant.OpenStatsConstants;
@@ -59,7 +61,7 @@ public class MallStatsOpenServiceImpl implements IMallStatsOpenService
59 61
     {
60 62
         int year = resolveStatYear(statYear);
61 63
         ensureYearlyStatsCached(year);
62
-        return redisCache.getCacheObject(StatsCacheKeys.categorySales(year));
64
+        return readCache(StatsCacheKeys.categorySales(year), CategorySalesVO.class);
63 65
     }
64 66
 
65 67
     @Override
@@ -67,7 +69,7 @@ public class MallStatsOpenServiceImpl implements IMallStatsOpenService
67 69
     {
68 70
         int year = resolveStatYear(statYear);
69 71
         ensureYearlyStatsCached(year);
70
-        return redisCache.getCacheObject(StatsCacheKeys.hotCategory(year));
72
+        return readCache(StatsCacheKeys.hotCategory(year), HotCategoryRankVO.class);
71 73
     }
72 74
 
73 75
     @Override
@@ -75,7 +77,7 @@ public class MallStatsOpenServiceImpl implements IMallStatsOpenService
75 77
     {
76 78
         int year = resolveStatYear(statYear);
77 79
         ensureYearlyStatsCached(year);
78
-        return redisCache.getCacheObject(StatsCacheKeys.orderTrend(year));
80
+        return readCache(StatsCacheKeys.orderTrend(year), OrderTrendVO.class);
79 81
     }
80 82
 
81 83
     @Override
@@ -83,7 +85,7 @@ public class MallStatsOpenServiceImpl implements IMallStatsOpenService
83 85
     {
84 86
         int year = resolveStatYear(statYear);
85 87
         ensureYearlyStatsCached(year);
86
-        return redisCache.getCacheObject(StatsCacheKeys.shopEntry(year));
88
+        return readCache(StatsCacheKeys.shopEntry(year), ShopEntryVO.class);
87 89
     }
88 90
 
89 91
     @Override
@@ -91,14 +93,14 @@ public class MallStatsOpenServiceImpl implements IMallStatsOpenService
91 93
     {
92 94
         int year = resolveStatYear(statYear);
93 95
         ensureYearlyStatsCached(year);
94
-        return redisCache.getCacheObject(StatsCacheKeys.regionRank(year));
96
+        return readCache(StatsCacheKeys.regionRank(year), RegionRankVO.class);
95 97
     }
96 98
 
97 99
     @Override
98 100
     public ReviewWordCloudVO getReviewWordCloud()
99 101
     {
100 102
         ensureWordCloudCached();
101
-        return redisCache.getCacheObject(StatsCacheKeys.wordCloud());
103
+        return readCache(StatsCacheKeys.wordCloud(), ReviewWordCloudVO.class);
102 104
     }
103 105
 
104 106
     @Override
@@ -107,12 +109,12 @@ public class MallStatsOpenServiceImpl implements IMallStatsOpenService
107 109
         int year = resolveStatYear(statYear);
108 110
         ensureOverviewStatsCached(year);
109 111
         StatsOverviewVO overview = new StatsOverviewVO();
110
-        overview.setCategorySales(redisCache.getCacheObject(StatsCacheKeys.categorySales(year)));
111
-        overview.setHotCategoryRank(redisCache.getCacheObject(StatsCacheKeys.hotCategory(year)));
112
-        overview.setOrderTrend(redisCache.getCacheObject(StatsCacheKeys.orderTrend(year)));
113
-        overview.setShopEntry(redisCache.getCacheObject(StatsCacheKeys.shopEntry(year)));
114
-        overview.setRegionRank(redisCache.getCacheObject(StatsCacheKeys.regionRank(year)));
115
-        overview.setReviewWordCloud(redisCache.getCacheObject(StatsCacheKeys.wordCloud()));
112
+        overview.setCategorySales(readCache(StatsCacheKeys.categorySales(year), CategorySalesVO.class));
113
+        overview.setHotCategoryRank(readCache(StatsCacheKeys.hotCategory(year), HotCategoryRankVO.class));
114
+        overview.setOrderTrend(readCache(StatsCacheKeys.orderTrend(year), OrderTrendVO.class));
115
+        overview.setShopEntry(readCache(StatsCacheKeys.shopEntry(year), ShopEntryVO.class));
116
+        overview.setRegionRank(readCache(StatsCacheKeys.regionRank(year), RegionRankVO.class));
117
+        overview.setReviewWordCloud(readCache(StatsCacheKeys.wordCloud(), ReviewWordCloudVO.class));
116 118
         return overview;
117 119
     }
118 120
 
@@ -169,19 +171,34 @@ public class MallStatsOpenServiceImpl implements IMallStatsOpenService
169 171
 
170 172
     private boolean isYearlyStatsCached(int year)
171 173
     {
172
-        for (String cacheKey : StatsCacheKeys.yearlyStatKeys(year))
173
-        {
174
-            if (redisCache.getCacheObject(cacheKey) == null)
175
-            {
176
-                return false;
177
-            }
178
-        }
179
-        return true;
174
+        return readCache(StatsCacheKeys.categorySales(year), CategorySalesVO.class) != null
175
+                && readCache(StatsCacheKeys.hotCategory(year), HotCategoryRankVO.class) != null
176
+                && readCache(StatsCacheKeys.orderTrend(year), OrderTrendVO.class) != null
177
+                && readCache(StatsCacheKeys.shopEntry(year), ShopEntryVO.class) != null
178
+                && readCache(StatsCacheKeys.regionRank(year), RegionRankVO.class) != null;
180 179
     }
181 180
 
182 181
     private boolean isWordCloudCached()
183 182
     {
184
-        return redisCache.getCacheObject(StatsCacheKeys.wordCloud()) != null;
183
+        return readCache(StatsCacheKeys.wordCloud(), ReviewWordCloudVO.class) != null;
184
+    }
185
+
186
+    private <T> T readCache(String key, Class<T> type)
187
+    {
188
+        Object cached = redisCache.getCacheObject(key);
189
+        if (cached == null)
190
+        {
191
+            return null;
192
+        }
193
+        if (type.isInstance(cached))
194
+        {
195
+            return type.cast(cached);
196
+        }
197
+        if (cached instanceof JSONObject)
198
+        {
199
+            return ((JSONObject) cached).to(type);
200
+        }
201
+        return JSON.parseObject(JSON.toJSONString(cached), type);
185 202
     }
186 203
 
187 204
     private void refreshYearlyStatsCache(int year)

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

@@ -0,0 +1,54 @@
1
+package com.ruoyi.web;
2
+
3
+import com.alibaba.fastjson2.JSONObject;
4
+import com.ruoyi.framework.config.FastJson2JsonRedisSerializer;
5
+import com.ruoyi.web.modules.openstats.vo.CategorySalesItemVO;
6
+import com.ruoyi.web.modules.openstats.vo.CategorySalesVO;
7
+import java.nio.charset.StandardCharsets;
8
+import java.util.Collections;
9
+import org.junit.jupiter.api.Assertions;
10
+import org.junit.jupiter.api.Test;
11
+
12
+class FastJson2OpenStatsCacheTest
13
+{
14
+    private static final String CATEGORY_SALES_JSON =
15
+            "{\"@type\":\"com.ruoyi.web.modules.openstats.vo.CategorySalesVO\",\"items\":[{\"categoryId\":1L,\"categoryName\":\"兽药\",\"qty\":15L,\"ratio\":100.0}],\"statYear\":2026,\"totalQty\":15L}";
16
+
17
+    @Test
18
+    void deserializeCategorySalesAsTypedObject()
19
+    {
20
+        FastJson2JsonRedisSerializer<Object> serializer = new FastJson2JsonRedisSerializer<>(Object.class);
21
+        Object cached = serializer.deserialize(CATEGORY_SALES_JSON.getBytes(StandardCharsets.UTF_8));
22
+        Assertions.assertNotNull(cached);
23
+        Assertions.assertTrue(cached instanceof CategorySalesVO);
24
+        Assertions.assertEquals(2026, ((CategorySalesVO) cached).getStatYear());
25
+    }
26
+
27
+    @Test
28
+    void jsonObjectCanConvertToCategorySales()
29
+    {
30
+        JSONObject jsonObject = JSONObject.parseObject(CATEGORY_SALES_JSON);
31
+        CategorySalesVO vo = jsonObject.to(CategorySalesVO.class);
32
+        Assertions.assertEquals(15L, vo.getTotalQty());
33
+        Assertions.assertEquals("兽药", vo.getItems().get(0).getCategoryName());
34
+    }
35
+
36
+    @Test
37
+    void roundTripCategorySales()
38
+    {
39
+        CategorySalesItemVO item = new CategorySalesItemVO();
40
+        item.setCategoryId(1L);
41
+        item.setCategoryName("兽药");
42
+        item.setQty(15L);
43
+        item.setRatio(100.0);
44
+
45
+        CategorySalesVO vo = new CategorySalesVO();
46
+        vo.setStatYear(2026);
47
+        vo.setTotalQty(15L);
48
+        vo.setItems(Collections.singletonList(item));
49
+
50
+        FastJson2JsonRedisSerializer<Object> serializer = new FastJson2JsonRedisSerializer<>(Object.class);
51
+        Object cached = serializer.deserialize(serializer.serialize(vo));
52
+        Assertions.assertTrue(cached instanceof CategorySalesVO);
53
+    }
54
+}

+ 67 - 2
ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java

@@ -6,6 +6,7 @@ import org.springframework.data.redis.serializer.SerializationException;
6 6
 import com.alibaba.fastjson2.JSON;
7 7
 import com.alibaba.fastjson2.JSONReader;
8 8
 import com.alibaba.fastjson2.JSONWriter;
9
+import com.alibaba.fastjson2.JSONObject;
9 10
 import com.alibaba.fastjson2.filter.Filter;
10 11
 import com.ruoyi.common.constant.Constants;
11 12
 
@@ -45,8 +46,72 @@ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
45 46
         {
46 47
             return null;
47 48
         }
48
-        String str = new String(bytes, DEFAULT_CHARSET);
49
+        String str = normalizeLegacyCollectionSyntax(new String(bytes, DEFAULT_CHARSET));
50
+        try
51
+        {
52
+            Object parsed = JSON.parseObject(str, clazz, AUTO_TYPE_FILTER, JSONReader.Feature.SupportAutoType);
53
+            return castParsedValue(parsed);
54
+        }
55
+        catch (Exception ex)
56
+        {
57
+            try
58
+            {
59
+                JSONObject jsonObject = JSON.parseObject(str, JSONObject.class, AUTO_TYPE_FILTER,
60
+                        JSONReader.Feature.SupportAutoType);
61
+                if (jsonObject == null)
62
+                {
63
+                    return null;
64
+                }
65
+                return castParsedValue(jsonObject);
66
+            }
67
+            catch (Exception fallbackEx)
68
+            {
69
+                throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
70
+            }
71
+        }
72
+    }
73
+
74
+    @SuppressWarnings("unchecked")
75
+    private T castParsedValue(Object parsed)
76
+    {
77
+        if (parsed == null)
78
+        {
79
+            return null;
80
+        }
81
+        if (clazz.isInstance(parsed))
82
+        {
83
+            return clazz.cast(parsed);
84
+        }
85
+        if (parsed instanceof JSONObject)
86
+        {
87
+            JSONObject jsonObject = (JSONObject) parsed;
88
+            String typeName = jsonObject.getString("@type");
89
+            if (typeName != null)
90
+            {
91
+                try
92
+                {
93
+                    Class<?> targetType = Class.forName(typeName);
94
+                    return (T) jsonObject.to(targetType);
95
+                }
96
+                catch (ClassNotFoundException ignored)
97
+                {
98
+                    // fall through
99
+                }
100
+            }
101
+            if (clazz != Object.class)
102
+            {
103
+                return jsonObject.toJavaObject(clazz);
104
+            }
105
+        }
106
+        return (T) parsed;
107
+    }
49 108
 
50
-        return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER);
109
+    private static String normalizeLegacyCollectionSyntax(String str)
110
+    {
111
+        if (str == null || str.isEmpty())
112
+        {
113
+            return str;
114
+        }
115
+        return str.replace("LinkedHashSet[", "[").replace("HashSet[", "[").replace("Set[", "[");
51 116
     }
52 117
 }