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,328 +1,319 @@
1
1
  ---
2
2
  name: utils-toolkit
3
3
  description: |
4
- 后端工具类使用指南。包含 MapstructUtils、StringUtils、StreamUtils、TreeBuildUtils、DateUtils、RedisUtils 等核心工具类。
4
+ 后端工具类使用指南。包含对象转换、字符串处理、集合操作、树结构构建、日期时间、JSON、参数校验等通用工具类选型与使用规范。
5
5
 
6
6
  触发场景:
7
7
  - 对象转换(BO/VO/Entity)
8
8
  - 字符串处理、集合流操作
9
9
  - 树结构构建
10
- - Redis 缓存操作
11
- - Excel 导入导出
10
+ - JSON 序列化/反序列化
11
+ - 日期时间处理
12
+ - 参数校验
12
13
 
13
- 触发词:工具类、MapstructUtils、StringUtils、StreamUtils、TreeBuildUtils、DateUtils、RedisUtils、ExcelUtil、JsonUtils、LoginHelper、convert、对象转换、集合操作、树结构、缓存
14
+ 触发词:工具类、对象转换、BeanUtils、StringUtils、集合操作、树结构、DateUtils、JsonUtils、convert、字符串处理、日期时间
14
15
 
15
16
  注意:
16
- - 对象转换必须使用 MapstructUtils.convert(),禁止使用 BeanUtils。
17
- - 本项目是纯后端项目,无前端工具类。
17
+ - 对象转换工具需要根据项目实际选型统一使用,禁止混用多种方案。
18
+ - 本指南为通用 Java 后端工具类指南,适配各类 Spring Boot 项目。
18
19
  ---
19
20
 
20
21
  # 后端工具类大全
21
22
 
22
23
  ## 快速索引
23
24
 
24
- | 功能 | 工具类 | 包路径 | 常用方法 |
25
- |------|--------|--------|---------|
26
- | **对象转换** | `MapstructUtils` | `o.d.common.core.utils` | `convert()` |
27
- | 字符串 | `StringUtils` | `o.d.common.core.utils` | `isBlank()`, `format()` |
28
- | 集合/流 | `StreamUtils` | `o.d.common.core.utils` | `filter()`, `toList()`, `toMap()` |
29
- | 树结构 | `TreeBuildUtils` | `o.d.common.core.utils` | `build()` |
30
- | 日期时间 | `DateUtils` | `o.d.common.core.utils` | `getTime()`, `formatDateTime()` |
31
- | 参数校验 | `ValidatorUtils` | `o.d.common.core.utils` | `validate()` |
32
- | Redis缓存 | `RedisUtils` | `o.d.common.redis.utils` | `setCacheObject()`, `getCacheObject()` |
33
- | Excel导出 | `ExcelUtil` | `o.d.common.excel.utils` | `exportExcel()` |
34
- | JSON | `JsonUtils` | `o.d.common.json.utils` | `toJsonString()`, `parseObject()` |
35
- | 登录用户 | `LoginHelper` | `o.d.common.satoken.utils` | `getUserId()`, `getUsername()` |
36
- | 业务异常 | `ServiceException` | `o.d.common.core.exception` | `throw new ServiceException()` |
37
- | Spring容器 | `SpringUtils` | `o.d.common.core.utils` | `getBean()` |
38
-
39
- > **包路径说明**: `o.d` = `org.dromara`
25
+ | 功能 | 推荐方案 | 备选方案 | 常用方法 |
26
+ |------|---------|---------|---------|
27
+ | **对象转换** | `[你的对象转换工具]` | MapStruct / BeanUtils / ModelMapper | `convert()` / `copyProperties()` |
28
+ | 字符串 | Hutool `StrUtil` | Apache Commons `StringUtils` / Guava `Strings` | `isBlank()`, `format()` |
29
+ | 集合/流 | Hutool `CollUtil` + Java Stream | Guava `Collections2` | `isEmpty()`, `filter()`, `toList()` |
30
+ | 树结构 | Hutool `TreeUtil` | 自定义递归构建 | `build()` |
31
+ | 日期时间 | `java.time` API | Hutool `DateUtil` | `LocalDateTime.now()`, `format()` |
32
+ | 参数校验 | Jakarta Validation | Hutool `Validator` | `@NotNull`, `@NotBlank` |
33
+ | JSON | Jackson `ObjectMapper` | Gson / Hutool `JSONUtil` | `toJsonString()`, `parseObject()` |
34
+ | 业务异常 | `[你的异常类]` | 自定义 RuntimeException | `throw new [YourException]()` |
35
+ | 登录用户 | `[你的用户上下文工具]` | SecurityContextHolder / 自定义 ThreadLocal | `getUserId()`, `getUsername()` |
36
+ | Spring容器 | `ApplicationContext` | 自定义 `SpringUtils` | `getBean()` |
40
37
 
41
38
  ---
42
39
 
43
- ## 1. 对象转换 - MapstructUtils(必须使用!)
40
+ ## 1. 对象转换
44
41
 
45
- > **强制规范**: 禁止使用 `BeanUtils.copyProperties()`,必须使用 `MapstructUtils.convert()`
42
+ ### 选型对比
46
43
 
47
- ```java
48
- import org.dromara.common.core.utils.MapstructUtils;
44
+ | 方案 | 性能 | 易用性 | 特点 |
45
+ |------|------|--------|------|
46
+ | **MapStruct** | 极高(编译期生成) | 中(需定义接口) | 类型安全、零反射 |
47
+ | **Hutool BeanUtil** | 中(反射) | 高(一行调用) | 简单场景首选 |
48
+ | **Spring BeanUtils** | 中(反射) | 高 | Spring 内置,无额外依赖 |
49
+ | **ModelMapper** | 低 | 高(约定优于配置) | 深度映射、复杂场景 |
49
50
 
50
- // 单个对象转换
51
- XxxVo vo = MapstructUtils.convert(entity, XxxVo.class);
51
+ ### 通用用法
52
52
 
53
- // 集合转换
54
- List<XxxVo> voList = MapstructUtils.convert(entityList, XxxVo.class);
53
+ ```java
54
+ // 方案 A: Hutool BeanUtil(简单场景推荐)
55
+ import cn.hutool.core.bean.BeanUtil;
56
+
57
+ XxxVo vo = BeanUtil.copyProperties(entity, XxxVo.class);
58
+ List<XxxVo> voList = BeanUtil.copyToList(entityList, XxxVo.class);
59
+
60
+ // 方案 B: MapStruct(高性能场景推荐)
61
+ // 1. 定义 Mapper 接口
62
+ @Mapper(componentModel = "spring")
63
+ public interface XxxConverter {
64
+ XxxVo toVo(XxxEntity entity);
65
+ List<XxxVo> toVoList(List<XxxEntity> entities);
66
+ }
55
67
 
56
- // 转换到已有对象(合并属性)
57
- XxxVo vo = MapstructUtils.convert(source, existingVo);
68
+ // 2. 注入使用
69
+ @Resource
70
+ private XxxConverter xxxConverter;
71
+ XxxVo vo = xxxConverter.toVo(entity);
58
72
  ```
59
73
 
60
- **配合 @AutoMapper 注解**:
74
+ ---
75
+
76
+ ## 2. 字符串操作
77
+
78
+ ### Hutool StrUtil
61
79
 
62
80
  ```java
63
- @AutoMapper(target = Xxx.class, reverseConvertGenerate = false)
64
- public class XxxBo extends BaseEntity { }
81
+ import cn.hutool.core.util.StrUtil;
65
82
 
66
- @AutoMapper(target = Xxx.class)
67
- public class XxxVo implements Serializable { }
68
- ```
83
+ // 判空
84
+ StrUtil.isBlank(str); // null / "" / " " -> true
85
+ StrUtil.isNotBlank(str);
69
86
 
70
- ---
87
+ // 格式化(占位符 {})
88
+ StrUtil.format("用户 {} 不存在", userId);
71
89
 
72
- ## 2. 字符串操作 - StringUtils
90
+ // 分割/拼接
91
+ List<String> list = StrUtil.split("1,2,3", ',');
92
+ String joined = String.join(",", list);
93
+ ```
73
94
 
74
- ```java
75
- import org.dromara.common.core.utils.StringUtils;
95
+ ### Apache Commons StringUtils
76
96
 
77
- // 格式化(占位符 {})—— 项目特有
78
- StringUtils.format("用户 {} 不存在", userId);
97
+ ```java
98
+ import org.apache.commons.lang3.StringUtils;
79
99
 
80
- // 字符串分割/拼接 —— 项目特有
81
- List<String> list = StringUtils.splitList("1,2,3");
82
- List<Long> ids = StringUtils.splitTo("1,2,3", Convert::toLong);
83
- String str = StringUtils.joinComma(list);
100
+ StringUtils.isBlank(str);
101
+ StringUtils.substringBefore(str, "-");
102
+ StringUtils.leftPad("5", 3, '0'); // "005"
103
+ ```
84
104
 
85
- // 驼峰转换
86
- StringUtils.toCamelCase("user_name"); // "userName"
87
- StringUtils.toUnderScoreCase("userName"); // "user_name"
105
+ ### Guava Strings
88
106
 
89
- // Ant 风格路径匹配
90
- StringUtils.isMatch("/api/**", "/api/user/list");
91
- StringUtils.matches("/api/user", Arrays.asList("/api/**", "/admin/**"));
107
+ ```java
108
+ import com.google.common.base.Strings;
92
109
 
93
- // 左补零
94
- StringUtils.padl(5, 3); // "005"
110
+ Strings.isNullOrEmpty(str);
111
+ Strings.padStart("5", 3, '0'); // "005"
95
112
  ```
96
113
 
97
114
  ---
98
115
 
99
- ## 3. 集合与流操作 - StreamUtils
116
+ ## 3. 集合与流操作
117
+
118
+ ### Hutool CollUtil
100
119
 
101
120
  ```java
102
- import org.dromara.common.core.utils.StreamUtils;
121
+ import cn.hutool.core.collection.CollUtil;
103
122
 
104
- // 过滤
105
- List<User> active = StreamUtils.filter(users, u -> "1".equals(u.getStatus()));
123
+ CollUtil.isEmpty(list); // null 或空 -> true
124
+ CollUtil.isNotEmpty(list);
125
+ ```
106
126
 
107
- // 查找
108
- User user = StreamUtils.findFirstValue(users, u -> u.getId().equals(id));
127
+ ### Java Stream API
128
+
129
+ ```java
130
+ // 过滤
131
+ List<User> active = users.stream()
132
+ .filter(u -> "1".equals(u.getStatus()))
133
+ .toList();
109
134
 
110
135
  // 提取属性
111
- List<Long> ids = StreamUtils.toList(users, User::getId);
112
- Set<Long> deptIds = StreamUtils.toSet(users, User::getDeptId);
136
+ List<Long> ids = users.stream()
137
+ .map(User::getId)
138
+ .toList();
139
+
140
+ Set<Long> deptIds = users.stream()
141
+ .map(User::getDeptId)
142
+ .collect(Collectors.toSet());
113
143
 
114
144
  // 转 Map
115
- Map<Long, User> userMap = StreamUtils.toIdentityMap(users, User::getId);
116
- Map<Long, String> nameMap = StreamUtils.toMap(users, User::getId, User::getName);
145
+ Map<Long, User> userMap = users.stream()
146
+ .collect(Collectors.toMap(User::getId, Function.identity()));
147
+
148
+ Map<Long, String> nameMap = users.stream()
149
+ .collect(Collectors.toMap(User::getId, User::getName));
117
150
 
118
151
  // 分组
119
- Map<Long, List<User>> grouped = StreamUtils.groupByKey(users, User::getDeptId);
152
+ Map<Long, List<User>> grouped = users.stream()
153
+ .collect(Collectors.groupingBy(User::getDeptId));
120
154
 
121
155
  // 拼接字符串
122
- String names = StreamUtils.join(users, User::getName); // "张三,李四,王五"
123
- String names = StreamUtils.join(users, User::getName, "|"); // 自定义分隔符
124
-
125
- // 排序 / 合并 Map
126
- List<User> sorted = StreamUtils.sorted(users, Comparator.comparing(User::getCreateTime));
127
- Map<Long, String> merged = StreamUtils.merge(map1, map2, (v1, v2) -> v1 + v2);
156
+ String names = users.stream()
157
+ .map(User::getName)
158
+ .collect(Collectors.joining(","));
128
159
  ```
129
160
 
130
161
  ---
131
162
 
132
- ## 4. 树结构构建 - TreeBuildUtils
163
+ ## 4. 树结构构建
164
+
165
+ ### Hutool TreeUtil
133
166
 
134
167
  ```java
135
- import org.dromara.common.core.utils.TreeBuildUtils;
136
168
  import cn.hutool.core.lang.tree.Tree;
137
-
138
- List<Tree<Long>> tree = TreeBuildUtils.build(list, (node, item) -> {
139
- node.setId(item.getId());
140
- node.setParentId(item.getParentId());
141
- node.setName(item.getName());
142
- node.setWeight(item.getOrderNum());
143
- node.putExtra("icon", item.getIcon());
169
+ import cn.hutool.core.lang.tree.TreeUtil;
170
+ import cn.hutool.core.lang.tree.TreeNodeConfig;
171
+
172
+ List<Tree<Long>> tree = TreeUtil.build(list, 0L, (item, treeNode) -> {
173
+ treeNode.setId(item.getId());
174
+ treeNode.setParentId(item.getParentId());
175
+ treeNode.setName(item.getName());
176
+ treeNode.setWeight(item.getOrderNum());
177
+ treeNode.putExtra("icon", item.getIcon());
144
178
  });
179
+ ```
180
+
181
+ ### 自定义递归构建
145
182
 
146
- // 指定根节点 ID
147
- List<Tree<Long>> tree = TreeBuildUtils.build(list, 0L, (node, item) -> { ... });
183
+ ```java
184
+ public List<TreeNode> buildTree(List<TreeNode> nodes, Long parentId) {
185
+ return nodes.stream()
186
+ .filter(n -> Objects.equals(n.getParentId(), parentId))
187
+ .peek(n -> n.setChildren(buildTree(nodes, n.getId())))
188
+ .sorted(Comparator.comparingInt(TreeNode::getSort))
189
+ .toList();
190
+ }
148
191
  ```
149
192
 
150
193
  ---
151
194
 
152
- ## 5. 日期时间 - DateUtils
195
+ ## 5. 日期时间 - java.time API
153
196
 
154
197
  ```java
155
- import org.dromara.common.core.utils.DateUtils;
156
- import org.dromara.common.core.enums.FormatsType;
198
+ import java.time.LocalDateTime;
199
+ import java.time.LocalDate;
200
+ import java.time.format.DateTimeFormatter;
201
+ import java.time.Duration;
202
+ import java.time.temporal.ChronoUnit;
157
203
 
158
204
  // 获取当前时间
159
- String date = DateUtils.getDate(); // "2026-01-24"
160
- String time = DateUtils.getTime(); // "2026-01-24 15:30:00"
205
+ LocalDateTime now = LocalDateTime.now();
206
+ LocalDate today = LocalDate.now();
161
207
 
162
- // 格式化 / 解析
163
- DateUtils.formatDateTime(date);
164
- DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM, date);
165
- Date date = DateUtils.parseDateTime(FormatsType.YYYY_MM_DD_HH_MM_SS, "2026-01-24 15:30:00");
208
+ // 格式化
209
+ DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
210
+ String formatted = now.format(fmt);
166
211
 
167
- // 时间差
168
- long days = DateUtils.difference(start, end, TimeUnit.DAYS);
169
- String diff = DateUtils.getDatePoor(endDate, startDate); // "3天 2小时 30分钟"
170
-
171
- // 日期范围校验
172
- DateUtils.validateDateRange(startDate, endDate, 30, TimeUnit.DAYS);
212
+ // 解析
213
+ LocalDateTime parsed = LocalDateTime.parse("2026-01-24 15:30:00", fmt);
173
214
 
174
- // 友好时间(仿微信)
175
- DateUtils.formatFriendlyTime(date); // "刚刚" / "5分钟前" / "昨天 14:30"
215
+ // 时间差
216
+ long days = ChronoUnit.DAYS.between(start, end);
217
+ Duration duration = Duration.between(start, end);
218
+ long hours = duration.toHours();
176
219
 
177
- // 类型转换
178
- Date date = DateUtils.toDate(localDateTime);
220
+ // 日期加减
221
+ LocalDateTime tomorrow = now.plusDays(1);
222
+ LocalDateTime lastMonth = now.minusMonths(1);
179
223
  ```
180
224
 
181
- **FormatsType 枚举**: `YYYY_MM_DD` | `YYYY_MM_DD_HH_MM_SS` | `YYYY_MM_DD_HH_MM` | `YYYYMMDD` | `YYYYMMDDHHMMSS` | `HH_MM_SS`
182
-
183
- ---
184
-
185
- ## 6. 参数校验 - ValidatorUtils
225
+ ### Hutool DateUtil(兼容旧 Date 类型)
186
226
 
187
227
  ```java
188
- import org.dromara.common.core.utils.ValidatorUtils;
189
- import org.dromara.common.core.validate.AddGroup;
190
- import org.dromara.common.core.validate.EditGroup;
228
+ import cn.hutool.core.date.DateUtil;
191
229
 
192
- // Service 层手动校验
193
- ValidatorUtils.validate(bo, AddGroup.class);
230
+ // 自动识别多种格式
231
+ Date date = DateUtil.parse("2026-01-24 15:30:00");
232
+ String formatted = DateUtil.formatDateTime(date);
194
233
 
195
- // Controller 层(推荐)
196
- @PostMapping
197
- public R<Void> add(@Validated(AddGroup.class) @RequestBody XxxBo bo) { }
198
-
199
- @PutMapping
200
- public R<Void> edit(@Validated(EditGroup.class) @RequestBody XxxBo bo) { }
234
+ // 时间差
235
+ long between = DateUtil.between(start, end, DateUnit.DAY);
201
236
  ```
202
237
 
203
- **BO 类校验注解**:
238
+ ---
239
+
240
+ ## 6. 参数校验
241
+
242
+ ### Jakarta Validation(JDK 17+ 推荐)
204
243
 
205
244
  ```java
206
- public class XxxBo extends BaseEntity {
207
- @NotNull(message = "ID不能为空", groups = { EditGroup.class })
245
+ import jakarta.validation.constraints.*;
246
+
247
+ public class XxxDTO {
248
+ @NotNull(message = "ID不能为空", groups = {UpdateGroup.class})
208
249
  private Long id;
209
250
 
210
- @NotBlank(message = "名称不能为空", groups = { AddGroup.class, EditGroup.class })
251
+ @NotBlank(message = "名称不能为空", groups = {InsertGroup.class, UpdateGroup.class})
211
252
  @Size(max = 100, message = "名称长度不能超过100个字符")
212
253
  private String name;
254
+
255
+ @Min(value = 0, message = "数量不能为负数")
256
+ private Integer quantity;
213
257
  }
214
258
  ```
215
259
 
216
- ---
217
-
218
- ## 7. Redis 缓存 - RedisUtils
219
-
220
- > 详细 API 见 `references/redis-utils-api.md`
260
+ ### Controller 层使用
221
261
 
222
262
  ```java
223
- import org.dromara.common.redis.utils.RedisUtils;
224
-
225
- // 基本操作
226
- RedisUtils.setCacheObject("key", value);
227
- RedisUtils.setCacheObject("key", value, Duration.ofMinutes(30));
228
- RedisUtils.getCacheObject("key");
229
- RedisUtils.deleteObject("key");
230
- RedisUtils.hasKey("key");
231
-
232
- // 条件设置
233
- RedisUtils.setObjectIfAbsent("key", value, Duration.ofMinutes(5));
234
-
235
- // List / Set / Map 操作
236
- RedisUtils.setCacheList("listKey", dataList);
237
- RedisUtils.setCacheMap("mapKey", dataMap);
238
- RedisUtils.setCacheMapValue("mapKey", "field", value);
239
-
240
- // 原子操作
241
- long val = RedisUtils.incrAtomicValue("counter");
263
+ @PostMapping("/add")
264
+ public void add(@Validated(InsertGroup.class) @RequestBody XxxDTO dto) { }
242
265
 
243
- // 发布订阅
244
- RedisUtils.publish("channel", message);
245
- RedisUtils.subscribe("channel", Message.class, msg -> { });
246
-
247
- // 限流
248
- long remaining = RedisUtils.rateLimiter("api:user:list", RateType.OVERALL, 100, 60);
266
+ @PutMapping("/update")
267
+ public void edit(@Validated(UpdateGroup.class) @RequestBody XxxDTO dto) { }
249
268
  ```
250
269
 
251
270
  ---
252
271
 
253
- ## 8. 登录用户 - LoginHelper
254
-
255
- ```java
256
- import org.dromara.common.satoken.utils.LoginHelper;
257
-
258
- Long userId = LoginHelper.getUserId();
259
- String username = LoginHelper.getUsername();
260
- Long deptId = LoginHelper.getDeptId();
261
- String tenantId = LoginHelper.getTenantId();
262
- LoginUser loginUser = LoginHelper.getLoginUser();
263
- boolean isLogin = LoginHelper.isLogin();
264
- boolean isSuperAdmin = LoginHelper.isSuperAdmin();
265
- boolean isTenantAdmin = LoginHelper.isTenantAdmin();
266
- ```
267
-
268
- ---
272
+ ## 7. JSON 操作
269
273
 
270
- ## 9. 业务异常 - ServiceException
274
+ ### Jackson(Spring Boot 默认)
271
275
 
272
276
  ```java
273
- import org.dromara.common.core.exception.ServiceException;
274
-
275
- throw new ServiceException("用户不存在");
276
- throw new ServiceException("用户 {} 不存在", userId);
277
- throw new ServiceException("用户不存在", 500); // 带错误码
278
- ```
277
+ import com.fasterxml.jackson.databind.ObjectMapper;
279
278
 
280
- ---
281
-
282
- ## 10. Excel 导出 - ExcelUtil
279
+ // 推荐封装为项目统一的 JsonUtils
280
+ public class JsonUtils {
281
+ private static final ObjectMapper MAPPER = new ObjectMapper();
283
282
 
284
- ```java
285
- import org.dromara.common.excel.utils.ExcelUtil;
283
+ public static String toJsonString(Object obj) {
284
+ return MAPPER.writeValueAsString(obj);
285
+ }
286
286
 
287
- // 导出
288
- @PostMapping("/export")
289
- public void export(XxxBo bo, HttpServletResponse response) {
290
- List<XxxVo> list = xxxService.queryList(bo);
291
- ExcelUtil.exportExcel(list, "数据导出", XxxVo.class, response);
292
- }
287
+ public static <T> T parseObject(String json, Class<T> clazz) {
288
+ return MAPPER.readValue(json, clazz);
289
+ }
293
290
 
294
- // 导入
295
- @PostMapping("/import")
296
- public R<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
297
- List<XxxVo> list = ExcelUtil.importExcel(file.getInputStream(), XxxVo.class);
298
- return R.ok();
291
+ public static <T> List<T> parseArray(String json, Class<T> clazz) {
292
+ return MAPPER.readValue(json,
293
+ MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
294
+ }
299
295
  }
300
296
  ```
301
297
 
302
- **VO Excel 注解**:
298
+ ### Hutool JSONUtil
303
299
 
304
300
  ```java
305
- public class XxxVo implements Serializable {
306
- @ExcelProperty(value = "ID")
307
- private Long id;
301
+ import cn.hutool.json.JSONUtil;
308
302
 
309
- @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
310
- @ExcelDictFormat(dictType = "sys_normal_disable")
311
- private String status;
312
- }
303
+ String json = JSONUtil.toJsonStr(obj);
304
+ User user = JSONUtil.toBean(json, User.class);
313
305
  ```
314
306
 
315
307
  ---
316
308
 
317
- ## 11. JSON 操作 - JsonUtils
309
+ ## 8. 对象判空
318
310
 
319
311
  ```java
320
- import org.dromara.common.json.utils.JsonUtils;
312
+ import cn.hutool.core.util.ObjectUtil;
321
313
 
322
- String json = JsonUtils.toJsonString(obj);
323
- User user = JsonUtils.parseObject(json, User.class);
324
- List<User> list = JsonUtils.parseArray(json, User.class);
325
- Map<String, Object> map = JsonUtils.parseMap(json);
314
+ ObjectUtil.isNull(obj); // null -> true
315
+ ObjectUtil.isNotNull(obj);
316
+ ObjectUtil.defaultIfNull(obj, defaultValue);
326
317
  ```
327
318
 
328
319
  ---
@@ -331,18 +322,17 @@ Map<String, Object> map = JsonUtils.parseMap(json);
331
322
 
332
323
  | 需求 | 推荐工具 | 说明 |
333
324
  |------|---------|------|
334
- | BO/Entity/VO 转换 | `MapstructUtils.convert()` | **必须使用** |
335
- | 字符串判空 | `StringUtils.isBlank()` | 项目工具类 |
325
+ | BO/Entity/VO 转换 | `[你的对象转换工具]` | 项目统一选型 |
326
+ | 字符串判空 | `StrUtil.isBlank()` | Hutool |
336
327
  | 集合判空 | `CollUtil.isEmpty()` | Hutool |
337
328
  | 对象判空 | `ObjectUtil.isNull()` | Hutool |
338
- | 提取集合属性 | `StreamUtils.toList()` | 项目工具类 |
339
- | 集合转 Map | `StreamUtils.toIdentityMap()` | 项目工具类 |
340
- | 构建树 | `TreeBuildUtils.build()` | 项目工具类 |
341
- | 日期格式化 | `DateUtils.formatDateTime()` | 项目工具类 |
342
- | 抛业务异常 | `throw new ServiceException()` | 项目异常类 |
343
- | 获取登录用户 | `LoginHelper.getUserId()` | 项目工具类 |
344
- | Redis 缓存 | `RedisUtils.setCacheObject()` | 项目工具类 |
345
- | Excel 导出 | `ExcelUtil.exportExcel()` | 项目工具类 |
329
+ | 提取集合属性 | `Stream.map().toList()` | Java Stream |
330
+ | 集合转 Map | `Collectors.toMap()` | Java Stream |
331
+ | 构建树 | `TreeUtil.build()` | Hutool |
332
+ | 日期格式化 | `LocalDateTime.format()` | java.time |
333
+ | 抛业务异常 | `throw new [你的异常类]()` | 项目自定义 |
334
+ | 获取登录用户 | `[你的用户上下文工具]` | 项目自定义 |
335
+ | Excel 导出 | EasyExcel / Apache POI | 按需选型 |
346
336
  | 生成 ID | `IdUtil.getSnowflakeNextId()` | Hutool |
347
337
 
348
338
  ---
@@ -350,13 +340,15 @@ Map<String, Object> map = JsonUtils.parseMap(json);
350
340
  ## 禁止事项
351
341
 
352
342
  ```java
353
- // ❌ 禁止使用 BeanUtils
354
- BeanUtils.copyProperties(source, target);
355
-
356
343
  // ❌ 禁止使用 Map 传递业务数据
357
344
  public Map<String, Object> getXxx() { ... }
345
+ // ✅ 正确:使用 VO/DTO
346
+ public XxxVO getXxx() { ... }
347
+
348
+ // ❌ 禁止频繁创建 ObjectMapper
349
+ ObjectMapper mapper = new ObjectMapper();
350
+ // ✅ 正确:使用单例或项目统一封装的 JsonUtils
358
351
 
359
- // ❌ 禁止手写 stream 转换(应使用 StreamUtils)
360
- list.stream().map(User::getId).collect(Collectors.toList()); // 不推荐
361
- StreamUtils.toList(list, User::getId); // ✅ 推荐
352
+ // ❌ 禁止混用多种对象转换工具
353
+ // ✅ 正确:项目统一选型,保持一致
362
354
  ```