ai-engineering-init 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.claude/hooks/skill-forced-eval.js +46 -62
  2. package/.claude/settings.json +10 -1
  3. package/.claude/skills/api-development/SKILL.md +179 -130
  4. package/.claude/skills/architecture-design/SKILL.md +102 -212
  5. package/.claude/skills/backend-annotations/SKILL.md +166 -220
  6. package/.claude/skills/bug-detective/SKILL.md +225 -186
  7. package/.claude/skills/code-patterns/SKILL.md +127 -244
  8. package/.claude/skills/collaborating-with-codex/SKILL.md +96 -113
  9. package/.claude/skills/crud-development/SKILL.md +226 -307
  10. package/.claude/skills/data-permission/SKILL.md +131 -202
  11. package/.claude/skills/database-ops/SKILL.md +158 -355
  12. package/.claude/skills/error-handler/SKILL.md +224 -285
  13. package/.claude/skills/file-oss-management/SKILL.md +174 -169
  14. package/.claude/skills/git-workflow/SKILL.md +123 -341
  15. package/.claude/skills/json-serialization/SKILL.md +121 -137
  16. package/.claude/skills/performance-doctor/SKILL.md +83 -89
  17. package/.claude/skills/redis-cache/SKILL.md +134 -185
  18. package/.claude/skills/scheduled-jobs/SKILL.md +187 -224
  19. package/.claude/skills/security-guard/SKILL.md +168 -276
  20. package/.claude/skills/sms-mail/SKILL.md +266 -228
  21. package/.claude/skills/social-login/SKILL.md +257 -195
  22. package/.claude/skills/tenant-management/SKILL.md +172 -188
  23. package/.claude/skills/utils-toolkit/SKILL.md +214 -222
  24. package/.claude/skills/websocket-sse/SKILL.md +251 -172
  25. package/.claude/skills/workflow-engine/SKILL.md +178 -250
  26. package/.codex/skills/api-development/SKILL.md +179 -130
  27. package/.codex/skills/architecture-design/SKILL.md +102 -212
  28. package/.codex/skills/backend-annotations/SKILL.md +166 -220
  29. package/.codex/skills/bug-detective/SKILL.md +225 -186
  30. package/.codex/skills/code-patterns/SKILL.md +127 -244
  31. package/.codex/skills/collaborating-with-codex/SKILL.md +96 -113
  32. package/.codex/skills/crud-development/SKILL.md +226 -307
  33. package/.codex/skills/data-permission/SKILL.md +131 -202
  34. package/.codex/skills/database-ops/SKILL.md +158 -355
  35. package/.codex/skills/error-handler/SKILL.md +224 -285
  36. package/.codex/skills/file-oss-management/SKILL.md +174 -169
  37. package/.codex/skills/git-workflow/SKILL.md +123 -341
  38. package/.codex/skills/json-serialization/SKILL.md +121 -137
  39. package/.codex/skills/performance-doctor/SKILL.md +83 -89
  40. package/.codex/skills/redis-cache/SKILL.md +134 -185
  41. package/.codex/skills/scheduled-jobs/SKILL.md +187 -224
  42. package/.codex/skills/security-guard/SKILL.md +168 -276
  43. package/.codex/skills/sms-mail/SKILL.md +266 -228
  44. package/.codex/skills/social-login/SKILL.md +257 -195
  45. package/.codex/skills/tenant-management/SKILL.md +172 -188
  46. package/.codex/skills/utils-toolkit/SKILL.md +214 -222
  47. package/.codex/skills/websocket-sse/SKILL.md +251 -172
  48. package/.codex/skills/workflow-engine/SKILL.md +178 -250
  49. package/.cursor/hooks/cursor-skill-eval.js +66 -6
  50. package/.cursor/skills/api-development/SKILL.md +179 -130
  51. package/.cursor/skills/architecture-design/SKILL.md +102 -212
  52. package/.cursor/skills/backend-annotations/SKILL.md +166 -220
  53. package/.cursor/skills/bug-detective/SKILL.md +225 -186
  54. package/.cursor/skills/code-patterns/SKILL.md +127 -244
  55. package/.cursor/skills/collaborating-with-codex/SKILL.md +96 -113
  56. package/.cursor/skills/crud-development/SKILL.md +226 -307
  57. package/.cursor/skills/data-permission/SKILL.md +131 -202
  58. package/.cursor/skills/database-ops/SKILL.md +158 -355
  59. package/.cursor/skills/error-handler/SKILL.md +224 -285
  60. package/.cursor/skills/file-oss-management/SKILL.md +174 -169
  61. package/.cursor/skills/git-workflow/SKILL.md +123 -341
  62. package/.cursor/skills/json-serialization/SKILL.md +121 -137
  63. package/.cursor/skills/performance-doctor/SKILL.md +83 -89
  64. package/.cursor/skills/redis-cache/SKILL.md +134 -185
  65. package/.cursor/skills/scheduled-jobs/SKILL.md +187 -224
  66. package/.cursor/skills/security-guard/SKILL.md +168 -276
  67. package/.cursor/skills/sms-mail/SKILL.md +266 -228
  68. package/.cursor/skills/social-login/SKILL.md +257 -195
  69. package/.cursor/skills/tenant-management/SKILL.md +172 -188
  70. package/.cursor/skills/utils-toolkit/SKILL.md +214 -222
  71. package/.cursor/skills/websocket-sse/SKILL.md +251 -172
  72. package/.cursor/skills/workflow-engine/SKILL.md +178 -250
  73. package/AGENTS.md +49 -540
  74. package/CLAUDE.md +73 -119
  75. package/README.md +37 -6
  76. package/bin/index.js +5 -1
  77. package/package.json +1 -1
  78. package/src/skills/api-development/SKILL.md +179 -130
  79. package/src/skills/architecture-design/SKILL.md +102 -212
  80. package/src/skills/backend-annotations/SKILL.md +166 -220
  81. package/src/skills/bug-detective/SKILL.md +225 -186
  82. package/src/skills/code-patterns/SKILL.md +127 -244
  83. package/src/skills/collaborating-with-codex/SKILL.md +96 -113
  84. package/src/skills/crud-development/SKILL.md +226 -307
  85. package/src/skills/data-permission/SKILL.md +131 -202
  86. package/src/skills/database-ops/SKILL.md +158 -355
  87. package/src/skills/error-handler/SKILL.md +224 -285
  88. package/src/skills/file-oss-management/SKILL.md +174 -169
  89. package/src/skills/git-workflow/SKILL.md +123 -341
  90. package/src/skills/json-serialization/SKILL.md +121 -137
  91. package/src/skills/performance-doctor/SKILL.md +83 -89
  92. package/src/skills/redis-cache/SKILL.md +134 -185
  93. package/src/skills/scheduled-jobs/SKILL.md +187 -224
  94. package/src/skills/security-guard/SKILL.md +168 -276
  95. package/src/skills/sms-mail/SKILL.md +266 -228
  96. package/src/skills/social-login/SKILL.md +257 -195
  97. package/src/skills/tenant-management/SKILL.md +172 -188
  98. package/src/skills/utils-toolkit/SKILL.md +214 -222
  99. package/src/skills/websocket-sse/SKILL.md +251 -172
  100. package/src/skills/workflow-engine/SKILL.md +178 -250
  101. package/.claude/skills/skill-creator/LICENSE.txt +0 -202
  102. package/.claude/skills/skill-creator/SKILL.md +0 -479
  103. package/.claude/skills/skill-creator/agents/analyzer.md +0 -274
  104. package/.claude/skills/skill-creator/agents/comparator.md +0 -202
  105. package/.claude/skills/skill-creator/agents/grader.md +0 -223
  106. package/.claude/skills/skill-creator/assets/eval_review.html +0 -146
  107. package/.claude/skills/skill-creator/eval-viewer/generate_review.py +0 -471
  108. package/.claude/skills/skill-creator/eval-viewer/viewer.html +0 -1325
  109. package/.claude/skills/skill-creator/references/schemas.md +0 -430
  110. package/.claude/skills/skill-creator/scripts/__init__.py +0 -0
  111. package/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
  112. package/.claude/skills/skill-creator/scripts/generate_report.py +0 -326
  113. package/.claude/skills/skill-creator/scripts/improve_description.py +0 -248
  114. package/.claude/skills/skill-creator/scripts/package_skill.py +0 -136
  115. package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -103
  116. package/.claude/skills/skill-creator/scripts/run_eval.py +0 -310
  117. package/.claude/skills/skill-creator/scripts/run_loop.py +0 -332
  118. package/.claude/skills/skill-creator/scripts/utils.py +0 -47
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: redis-cache
3
3
  description: |
4
- 当需要使用Redis缓存、分布式锁、限流等功能时自动使用此Skill。包含RedisUtils工具类、CacheUtils工具类、缓存注解使用规范、分布式锁实现、缓存key命名规范等。
4
+ 当需要使用Redis缓存、分布式锁、限流等功能时自动使用此Skill。包含 Redisson API、Spring Cache 注解使用规范、分布式锁实现、缓存key命名规范等。
5
5
 
6
6
  触发场景:
7
7
  - 使用Redis缓存数据
@@ -12,265 +12,245 @@ description: |
12
12
  - 缓存key设计和命名
13
13
  - 缓存清理和刷新
14
14
 
15
- 触发词:Redis、缓存、Cache、@Cacheable、@CacheEvict、@CachePut、RedisUtils、CacheUtils、分布式锁、RLock、限流、RateLimiter、发布订阅、缓存key、缓存过期
15
+ 触发词:Redis、缓存、Cache、@Cacheable、@CacheEvict、@CachePut、Redisson、分布式锁、RLock、限流、RateLimiter、发布订阅、缓存key、缓存过期
16
16
 
17
17
  核心警告:
18
18
  - @Cacheable返回值不能使用不可变集合(List.of()、Set.of()、Map.of())
19
- - 分布式锁必须在finally中释放,且需注入RedissonClient(RedisUtils无getLock)
20
- - keys()和deleteKeys()会忽略租户隔离
19
+ - 分布式锁必须在finally中释放,且需注入RedissonClient
21
20
  ---
22
21
 
23
22
  # Redis 缓存开发指南
24
23
 
25
- > 模块位置:`ruoyi-common/ruoyi-common-redis`
26
-
27
24
  ## 快速索引
28
25
 
29
26
  | 功能 | 工具类/注解 | 说明 |
30
27
  |------|-------------|------|
31
- | 对象缓存 | `RedisUtils.setCacheObject()` | 基于 Redisson |
32
- | 集合缓存 | `RedisUtils.setCacheList/Set/Map()` | List/Set/Map 操作 |
33
- | Spring Cache | `CacheUtils.get/put/evict()` | Spring Cache 封装 |
34
- | 缓存注解 | `@Cacheable/@CachePut/@CacheEvict` | 声明式缓存 |
28
+ | 对象缓存 | `RedissonClient` / `[你的RedisUtils]` | 基于 Redisson |
29
+ | Spring Cache | `@Cacheable/@CachePut/@CacheEvict` | 声明式缓存 |
35
30
  | 分布式锁 | `redissonClient.getLock()` | 需注入 RedissonClient |
36
- | 限流控制 | `RedisUtils.rateLimiter()` | 基于 Redisson |
37
- | 原子操作 | `RedisUtils.incrAtomicValue()` | 原子递增/递减 |
38
- | 发布订阅 | `RedisUtils.publish/subscribe()` | 消息通信 |
31
+ | 限流控制 | Redisson `RRateLimiter` | 基于 Redisson |
32
+ | 原子操作 | Redisson `RAtomicLong` | 原子递增/递减 |
33
+ | 发布订阅 | Redisson `RTopic` | 消息通信 |
39
34
 
40
35
  ---
41
36
 
42
- ## 一、RedisUtils 工具类
43
-
44
- ```java
45
- import org.dromara.common.redis.utils.RedisUtils;
46
- ```
37
+ ## 一、Redisson 基础操作
47
38
 
48
39
  ### 1.1 基础缓存操作
49
40
 
50
41
  ```java
51
- RedisUtils.setCacheObject("user:123", userObj); // 永不过期
52
- RedisUtils.setCacheObject("user:123", userObj, Duration.ofMinutes(30)); // 带过期时间
53
- RedisUtils.setCacheObject("user:123", userObj, true); // 保留原有TTL(Redis 6.0+)
54
- boolean ok = RedisUtils.setObjectIfAbsent("user:123", userObj, Duration.ofMinutes(30)); // 不存在时设置
55
- boolean ok = RedisUtils.setObjectIfExists("user:123", userObj, Duration.ofMinutes(30)); // 存在时设置
56
-
57
- User user = RedisUtils.getCacheObject("user:123"); // 获取
58
- long ttl = RedisUtils.getTimeToLive("user:123"); // TTL毫秒(-1永不过期,-2不存在)
59
- boolean deleted = RedisUtils.deleteObject("user:123"); // 删除
60
- RedisUtils.deleteObject(Arrays.asList("user:123", "user:456")); // 批量删除
61
- boolean exists = RedisUtils.isExistsObject("user:123"); // 是否存在
62
- RedisUtils.expire("user:123", Duration.ofMinutes(30)); // 设置过期
42
+ import org.redisson.api.RedissonClient;
43
+ import org.redisson.api.RBucket;
44
+
45
+ @Service
46
+ @RequiredArgsConstructor
47
+ public class CacheService {
48
+
49
+ private final RedissonClient redissonClient;
50
+
51
+ // 设置缓存
52
+ public void set(String key, Object value, Duration ttl) {
53
+ RBucket<Object> bucket = redissonClient.getBucket(key);
54
+ bucket.set(value, ttl);
55
+ }
56
+
57
+ // 获取缓存
58
+ public <T> T get(String key) {
59
+ RBucket<T> bucket = redissonClient.getBucket(key);
60
+ return bucket.get();
61
+ }
62
+
63
+ // 删除缓存
64
+ public boolean delete(String key) {
65
+ return redissonClient.getBucket(key).delete();
66
+ }
67
+
68
+ // 判断是否存在
69
+ public boolean exists(String key) {
70
+ return redissonClient.getBucket(key).isExists();
71
+ }
72
+
73
+ // 不存在时设置(原子操作)
74
+ public boolean setIfAbsent(String key, Object value, Duration ttl) {
75
+ RBucket<Object> bucket = redissonClient.getBucket(key);
76
+ return bucket.trySet(value, ttl);
77
+ }
78
+ }
63
79
  ```
64
80
 
65
81
  ### 1.2 集合操作
66
82
 
67
83
  ```java
68
84
  // List
69
- RedisUtils.setCacheList("myList", dataList);
70
- RedisUtils.addCacheList("myList", "item3");
71
- List<String> result = RedisUtils.getCacheList("myList");
72
- List<String> range = RedisUtils.getCacheListRange("myList", 0, 10);
85
+ RList<String> list = redissonClient.getList("myList");
86
+ list.addAll(dataList);
87
+ List<String> result = list.readAll();
73
88
 
74
89
  // Set
75
- RedisUtils.setCacheSet("mySet", dataSet);
76
- boolean added = RedisUtils.addCacheSet("mySet", "value3");
77
- Set<String> result = RedisUtils.getCacheSet("mySet");
90
+ RSet<String> set = redissonClient.getSet("mySet");
91
+ set.addAll(dataSet);
92
+ Set<String> result = set.readAll();
78
93
 
79
94
  // Map
80
- RedisUtils.setCacheMap("myMap", dataMap);
81
- RedisUtils.setCacheMapValue("myMap", "key3", "value3");
82
- String value = RedisUtils.getCacheMapValue("myMap", "key1");
83
- Map<String, Object> result = RedisUtils.getCacheMap("myMap");
84
- Set<String> keys = RedisUtils.getCacheMapKeySet("myMap");
85
- RedisUtils.delCacheMapValue("myMap", "key1");
86
- RedisUtils.delMultiCacheMapValue("myMap", new HashSet<>(Arrays.asList("key1", "key2")));
87
- Map<String, Object> values = RedisUtils.getMultiCacheMapValue("myMap", keySet);
95
+ RMap<String, Object> map = redissonClient.getMap("myMap");
96
+ map.putAll(dataMap);
97
+ map.put("key3", "value3");
98
+ Object value = map.get("key1");
88
99
  ```
89
100
 
90
101
  ### 1.3 发布订阅
91
102
 
92
103
  ```java
93
- RedisUtils.publish("notification:channel", messageObj);
94
- RedisUtils.publish("notification:channel", messageObj, msg -> { log.info("已发布: {}", msg); });
95
- RedisUtils.subscribe("notification:channel", MessageDTO.class, msg -> { /* 处理消息 */ });
104
+ // 发布消息
105
+ RTopic topic = redissonClient.getTopic("notification:channel");
106
+ topic.publish(messageObj);
107
+
108
+ // 订阅消息
109
+ RTopic topic = redissonClient.getTopic("notification:channel");
110
+ topic.addListener(MessageDTO.class, (channel, msg) -> {
111
+ // 处理消息
112
+ });
96
113
  ```
97
114
 
98
115
  ### 1.4 限流控制
99
116
 
100
117
  ```java
118
+ import org.redisson.api.RRateLimiter;
101
119
  import org.redisson.api.RateType;
120
+ import org.redisson.api.RateIntervalUnit;
102
121
 
122
+ RRateLimiter limiter = redissonClient.getRateLimiter("api:limit:user:" + userId);
103
123
  // 每10秒最多100个请求
104
- long remaining = RedisUtils.rateLimiter("api:limit:user:123", RateType.OVERALL, 100, 10);
105
- if (remaining == -1) {
106
- throw new ServiceException("请求过于频繁");
107
- }
124
+ limiter.trySetRate(RateType.OVERALL, 100, 10, RateIntervalUnit.SECONDS);
108
125
 
109
- // 带超时版本(5秒超时)
110
- long remaining = RedisUtils.rateLimiter("api:limit:user:123", RateType.OVERALL, 100, 10, 5);
126
+ if (!limiter.tryAcquire()) {
127
+ throw new [你的异常类]("请求过于频繁");
128
+ }
111
129
  ```
112
130
 
113
131
  ### 1.5 原子操作
114
132
 
115
133
  ```java
116
- RedisUtils.setAtomicValue("counter:view", 0L);
117
- long value = RedisUtils.getAtomicValue("counter:view");
118
- long newVal = RedisUtils.incrAtomicValue("counter:view");
119
- long newVal = RedisUtils.decrAtomicValue("counter:view");
134
+ RAtomicLong counter = redissonClient.getAtomicLong("counter:view");
135
+ counter.set(0L);
136
+ long value = counter.get();
137
+ long newVal = counter.incrementAndGet();
138
+ long newVal = counter.decrementAndGet();
120
139
  ```
121
140
 
122
- ### 1.6 Key 操作
141
+ ---
123
142
 
124
- ```java
125
- // ⚠️ keys() 和 deleteKeys() 会忽略租户隔离
126
- Collection<String> keys = RedisUtils.keys("user:*");
143
+ ## 二、Spring Cache 注解使用规范
127
144
 
128
- KeysScanOptions options = KeysScanOptions.defaults().pattern("user:*").chunkSize(1000).limit(100);
129
- Collection<String> keys = RedisUtils.keys(options);
145
+ ### @Cacheable - 查询缓存
130
146
 
131
- RedisUtils.deleteKeys("temp:*");
132
- Boolean exists = RedisUtils.hasKey("user:123");
147
+ ```java
148
+ @Cacheable(value = "dictData", key = "#dictType")
149
+ @Override
150
+ public List<DictDataVo> listDictDataByType(String dictType) {
151
+ if (dictType == null || dictType.isBlank()) {
152
+ return Collections.emptyList();
153
+ }
154
+ List<DictData> dictDataList = dictDataMapper.selectByType(dictType);
155
+ if (dictDataList.isEmpty()) {
156
+ return Collections.emptyList(); // 返回空列表防止缓存穿透
157
+ }
158
+ return dictDataList.stream()
159
+ .map(d -> BeanUtil.copyProperties(d, DictDataVo.class))
160
+ .toList();
161
+ }
133
162
  ```
134
163
 
135
- ### 1.7 监听机制
164
+ ### @CachePut / @CacheEvict
165
+
166
+ ```java
167
+ @CachePut(value = "dictData", key = "#bo.dictType")
168
+ public List<DictDataVo> insertDictType(DictTypeBo bo) { ... }
136
169
 
137
- 详见 [references/listeners.md](references/listeners.md)
170
+ @CacheEvict(value = "dictData", key = "#dictType")
171
+ public void deleteDictType(String dictType) { ... }
138
172
 
139
- ---
173
+ @CacheEvict(value = "dictData", allEntries = true)
174
+ public void clearAllDictCache() { ... }
175
+ ```
140
176
 
141
- ## 二、CacheUtils 工具类
177
+ ### 返回值禁止使用不可变集合
142
178
 
143
179
  ```java
144
- import org.dromara.common.redis.utils.CacheUtils;
180
+ // ❌ 禁止! @Cacheable 返回值序列化失败
181
+ return List.of(data1, data2);
182
+ return Set.of("admin", "user");
183
+ return Map.of("key1", "value1");
145
184
 
146
- CacheUtils.put("userCache", "user:123", userObj); // 保存
147
- User user = CacheUtils.get("userCache", "user:123"); // 获取
148
- CacheUtils.evict("userCache", "user:123"); // 删除
149
- CacheUtils.clear("userCache"); // 清空整组
185
+ // 正确:使用可变集合
186
+ List<DictDataVo> result = new ArrayList<>();
187
+ result.add(data1);
188
+ return result;
150
189
  ```
151
190
 
152
191
  ---
153
192
 
154
- ## 三、CacheNames 缓存名称常量
193
+ ## 三、CacheNames 命名规范
155
194
 
156
- > 位置:`ruoyi-common/ruoyi-common-core/.../constant/CacheNames.java`
157
-
158
- ### 命名格式
195
+ ### 推荐格式
159
196
 
160
197
  ```
161
- cacheNames#ttl#maxIdleTime#maxSize#local
198
+ cacheNames#ttl#maxIdleTime#maxSize
162
199
  - ttl: 过期时间(0=不过期,默认0)
163
200
  - maxIdleTime: 最大空闲时间(0=不检测,默认0)
164
201
  - maxSize: 最大条数(0=无限,默认0)
165
- - local: 本地缓存(1开启,0关闭,默认1)
166
202
  ```
167
203
 
168
204
  ```java
169
205
  "test#60s" // 60秒过期
170
206
  "test#0#60s" // 不过期,60秒空闲清理
171
207
  "test#0#1m#1000" // 不过期,1分钟空闲,最大1000条
172
- "test#1h#0#500#0" // 1小时过期,最大500条,关闭本地缓存
208
+ "test#1h#0#500" // 1小时过期,最大500
173
209
  ```
174
210
 
175
- ### 已定义常量
211
+ ### 常量定义示例
176
212
 
177
213
  ```java
178
214
  public interface CacheNames {
179
- String DEMO_CACHE = "demo:cache#60s#10m#20";
180
215
  String SYS_CONFIG = "sys_config";
181
216
  String SYS_DICT = "sys_dict";
182
- String SYS_DICT_TYPE = "sys_dict_type";
183
- String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
184
- String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
185
217
  String SYS_USER_NAME = "sys_user_name#30d";
186
- String SYS_NICKNAME = "sys_nickname#30d";
187
218
  String SYS_DEPT = "sys_dept#30d";
188
- String SYS_OSS = "sys_oss#30d";
189
- String SYS_ROLE_CUSTOM = "sys_role_custom#30d";
190
- String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d";
191
- String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
192
- String ONLINE_TOKEN = "online_tokens";
193
219
  }
194
220
  ```
195
221
 
196
222
  ---
197
223
 
198
- ## 四、缓存注解使用规范
224
+ ## 四、分布式锁
199
225
 
200
- ### @Cacheable - 查询缓存
201
-
202
- ```java
203
- @Cacheable(cacheNames = CacheNames.SYS_DICT, key = "#dictType")
204
- @Override
205
- public List<SysDictDataVo> listDictDataByType(String dictType) {
206
- if (StringUtils.isBlank(dictType)) {
207
- return Collections.emptyList();
208
- }
209
- List<SysDictData> dictDataList = dictDataMapper.selectByType(dictType);
210
- if (CollUtil.isEmpty(dictDataList)) {
211
- return Collections.emptyList(); // 返回空列表防止缓存穿透
212
- }
213
- return MapstructUtils.convert(dictDataList, SysDictDataVo.class);
214
- }
215
- ```
216
-
217
- ### @CachePut / @CacheEvict
218
-
219
- ```java
220
- @CachePut(cacheNames = CacheNames.SYS_DICT, key = "#bo.dictType")
221
- public List<SysDictDataVo> insertDictType(SysDictTypeBo bo) { ... }
222
-
223
- @CacheEvict(cacheNames = CacheNames.SYS_DICT, key = "#dictType")
224
- public void deleteDictType(String dictType) { ... }
225
-
226
- @CacheEvict(cacheNames = CacheNames.SYS_DICT, allEntries = true)
227
- public void clearAllDictCache() { ... }
228
- ```
229
-
230
- ### 返回值禁止使用不可变集合
231
-
232
- ```java
233
- // ❌ 禁止!@Cacheable 返回值序列化失败
234
- return List.of(data1, data2);
235
- return Set.of("admin", "user");
236
- return Map.of("key1", "value1");
237
-
238
- // ✅ 正确:使用可变集合
239
- List<SysDictDataVo> result = new ArrayList<>();
240
- result.add(data1);
241
- return result;
242
- ```
243
-
244
- ---
245
-
246
- ## 五、分布式锁
247
-
248
- > RedisUtils **不提供** `getLock()`,需直接注入 `RedissonClient`。
226
+ > 直接注入 `RedissonClient` 使用。
249
227
 
250
228
  ```java
251
229
  @Service
252
230
  @RequiredArgsConstructor
253
- public class OrderServiceImpl implements IOrderService {
231
+ public class OrderServiceImpl {
254
232
 
255
233
  private final RedissonClient redissonClient;
256
234
 
257
235
  public void submitOrder(OrderBo orderBo) {
258
- String lockKey = "lock:submit:order:" + LoginHelper.getUserId();
236
+ String lockKey = "lock:submit:order:" + orderBo.getUserId();
259
237
  RLock lock = redissonClient.getLock(lockKey);
260
238
 
261
239
  try {
262
240
  boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
263
241
  if (!locked) {
264
- throw new ServiceException("请勿重复提交订单");
242
+ throw new [你的异常类]("请勿重复提交订单");
265
243
  }
266
244
  try {
267
245
  orderMapper.insert(orderBo);
268
246
  } finally {
269
- lock.unlock(); // 必须在 finally 中释放
247
+ if (lock.isLocked() && lock.isHeldByCurrentThread()) {
248
+ lock.unlock(); // 必须在 finally 中释放
249
+ }
270
250
  }
271
251
  } catch (InterruptedException e) {
272
252
  Thread.currentThread().interrupt();
273
- throw new ServiceException("订单提交被中断");
253
+ throw new [你的异常类]("订单提交被中断");
274
254
  }
275
255
  }
276
256
  }
@@ -278,7 +258,7 @@ public class OrderServiceImpl implements IOrderService {
278
258
 
279
259
  ---
280
260
 
281
- ## 六、缓存 Key 命名规范
261
+ ## 五、缓存 Key 命名规范
282
262
 
283
263
  ```
284
264
  格式:{业务模块}:{功能}:{具体标识}
@@ -292,42 +272,11 @@ limit:api:user:123 limit:sms:18888888888
292
272
 
293
273
  ---
294
274
 
295
- ## 七、租户隔离注意
296
-
297
- ```java
298
- // ⚠️ keys() 和 deleteKeys() 会忽略租户隔离,需手动拼接
299
- String tenantId = LoginHelper.getTenantId();
300
- Collection<String> keys = RedisUtils.keys(tenantId + ":user:*");
301
-
302
- // CacheUtils 会自动处理租户隔离
303
- CacheUtils.clear(CacheNames.SYS_USER_NAME);
304
- ```
305
-
306
- ---
307
-
308
- ## 八、注意事项速查
275
+ ## 六、注意事项速查
309
276
 
310
277
  1. **@Cacheable 返回值不能使用不可变集合**(List.of()、Set.of()、Map.of())
311
278
  2. **分布式锁必须在 finally 中释放**
312
- 3. **keys() 和 deleteKeys() 会忽略租户隔离**
313
- 4. **返回空列表而不是 null**,防止缓存穿透
314
- 5. **设置随机过期时间偏移**,避免缓存雪崩
315
- 6. **热点数据用分布式锁 + 双重检查**,防止缓存击穿
316
-
317
- ---
318
-
319
- ## 九、核心文件位置
320
-
321
- | 文件 | 位置 |
322
- |------|------|
323
- | RedisUtils | `ruoyi-common/ruoyi-common-redis/.../utils/RedisUtils.java` |
324
- | CacheUtils | `ruoyi-common/ruoyi-common-redis/.../utils/CacheUtils.java` |
325
- | CacheNames | `ruoyi-common/ruoyi-common-core/.../constant/CacheNames.java` |
326
- | GlobalConstants | `ruoyi-common/ruoyi-common-core/.../constant/GlobalConstants.java` |
327
-
328
- ---
329
-
330
- ## 多项目适配说明
331
-
332
- - 如果需要 leniu-tengyun-core 项目的 Redis 开发规范,请使用 `leniu-redis-cache` skill
333
- - leniu-tengyun-core 的 RedisUtil 工具类方法名与 RuoYi-Vue-Plus 的 RedisUtils 不同
279
+ 3. **返回空列表而不是 null**,防止缓存穿透
280
+ 4. **设置随机过期时间偏移**,避免缓存雪崩
281
+ 5. **热点数据用分布式锁 + 双重检查**,防止缓存击穿
282
+ 6. **keys() 模式匹配要谨慎**,生产环境避免大范围扫描