ai-engineering-init 1.4.2 → 1.5.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.
- package/.claude/skills/leniu-java-export/SKILL.md +389 -95
- package/.codex/skills/leniu-java-export/SKILL.md +389 -95
- package/.cursor/skills/bug-detective/SKILL.md +19 -19
- package/.cursor/skills/leniu-java-export/SKILL.md +389 -95
- package/.cursor/skills/project-navigator/SKILL.md +164 -258
- package/package.json +7 -1
- package/scripts/build-skills.js +180 -0
- package/src/platform-map.json +56 -0
- package/src/skills/add-skill/SKILL.md +488 -0
- package/src/skills/add-todo/SKILL.md +269 -0
- package/src/skills/api-development/SKILL.md +266 -0
- package/src/skills/architecture-design/SKILL.md +262 -0
- package/src/skills/backend-annotations/SKILL.md +302 -0
- package/src/skills/banana-image/CHANGELOG.md +37 -0
- package/src/skills/banana-image/README.md +146 -0
- package/src/skills/banana-image/SKILL.md +171 -0
- package/src/skills/banana-image/assets/logo.png +0 -0
- package/src/skills/banana-image/references/advanced-usage.md +189 -0
- package/src/skills/banana-image/scripts/apply_template.py +125 -0
- package/src/skills/banana-image/scripts/banana_image_exec.ts +412 -0
- package/src/skills/banana-image/scripts/batch_prep.py +82 -0
- package/src/skills/banana-image/scripts/package-lock.json +1437 -0
- package/src/skills/banana-image/scripts/package.json +18 -0
- package/src/skills/banana-image/scripts/requirements.txt +10 -0
- package/src/skills/banana-image/templates/poster.json +22 -0
- package/src/skills/banana-image/templates/product.json +17 -0
- package/src/skills/banana-image/templates/social.json +22 -0
- package/src/skills/banana-image/templates/thumbnail.json +17 -0
- package/src/skills/brainstorm/SKILL.md +216 -0
- package/src/skills/bug-detective/SKILL.md +256 -0
- package/src/skills/bug-detective/references/error-patterns.md +242 -0
- package/src/skills/check/SKILL.md +367 -0
- package/src/skills/code-patterns/SKILL.md +280 -0
- package/src/skills/code-patterns/references/leniu-code-patterns.md +87 -0
- package/src/skills/codex-code-review/SKILL.md +135 -0
- package/src/skills/collaborating-with-codex/SKILL.md +174 -0
- package/src/skills/collaborating-with-codex/scripts/codex_bridge.py +275 -0
- package/src/skills/collaborating-with-gemini/SKILL.md +194 -0
- package/src/skills/collaborating-with-gemini/scripts/gemini_bridge.py +275 -0
- package/src/skills/crud/SKILL.md +265 -0
- package/src/skills/crud-development/SKILL.md +409 -0
- package/src/skills/data-permission/SKILL.md +292 -0
- package/src/skills/data-permission/references/custom-data-scope.md +90 -0
- package/src/skills/database-ops/SKILL.md +407 -0
- package/src/skills/dev/SKILL.md +187 -0
- package/src/skills/error-handler/SKILL.md +371 -0
- package/src/skills/file-oss-management/SKILL.md +255 -0
- package/src/skills/file-oss-management/references/entities.md +105 -0
- package/src/skills/file-oss-management/references/service-impl.md +104 -0
- package/src/skills/git-workflow/SKILL.md +397 -0
- package/src/skills/init-docs/SKILL.md +194 -0
- package/src/skills/json-serialization/SKILL.md +357 -0
- package/src/skills/leniu-api-development/SKILL.md +319 -0
- package/src/skills/leniu-api-development/references/real-examples.md +273 -0
- package/src/skills/leniu-architecture-design/SKILL.md +383 -0
- package/src/skills/leniu-backend-annotations/SKILL.md +277 -0
- package/src/skills/leniu-brainstorm/SKILL.md +242 -0
- package/src/skills/leniu-brainstorm/references/business-scenarios.md +162 -0
- package/src/skills/leniu-code-patterns/SKILL.md +411 -0
- package/src/skills/leniu-crud-development/SKILL.md +404 -0
- package/src/skills/leniu-crud-development/references/templates.md +597 -0
- package/src/skills/leniu-customization-location/SKILL.md +410 -0
- package/src/skills/leniu-data-permission/SKILL.md +341 -0
- package/src/skills/leniu-database-ops/SKILL.md +426 -0
- package/src/skills/leniu-error-handler/SKILL.md +462 -0
- package/src/skills/leniu-java-amount-handling/SKILL.md +461 -0
- package/src/skills/leniu-java-code-style/SKILL.md +510 -0
- package/src/skills/leniu-java-concurrent/SKILL.md +400 -0
- package/src/skills/leniu-java-entity/SKILL.md +237 -0
- package/src/skills/leniu-java-entity/references/templates.md +237 -0
- package/src/skills/leniu-java-export/SKILL.md +570 -0
- package/src/skills/leniu-java-logging/SKILL.md +229 -0
- package/src/skills/leniu-java-logging/references/data-mask.md +46 -0
- package/src/skills/leniu-java-logging/references/logging-scenarios.md +113 -0
- package/src/skills/leniu-java-mq/SKILL.md +338 -0
- package/src/skills/leniu-java-mybatis/SKILL.md +267 -0
- package/src/skills/leniu-java-mybatis/references/report-mapper.md +88 -0
- package/src/skills/leniu-java-report-query-param/SKILL.md +291 -0
- package/src/skills/leniu-java-task/SKILL.md +367 -0
- package/src/skills/leniu-java-total-line/SKILL.md +196 -0
- package/src/skills/leniu-marketing-price-rule-customizer/SKILL.md +301 -0
- package/src/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +285 -0
- package/src/skills/leniu-mealtime/SKILL.md +215 -0
- package/src/skills/leniu-redis-cache/SKILL.md +331 -0
- package/src/skills/leniu-report-customization/SKILL.md +335 -0
- package/src/skills/leniu-report-customization/references/table-fields.md +93 -0
- package/src/skills/leniu-report-standard-customization/SKILL.md +328 -0
- package/src/skills/leniu-report-standard-customization/references/analysis-module.md +64 -0
- package/src/skills/leniu-report-standard-customization/references/table-fields.md +113 -0
- package/src/skills/leniu-security-guard/SKILL.md +306 -0
- package/src/skills/leniu-utils-toolkit/SKILL.md +380 -0
- package/src/skills/mysql-debug/SKILL.md +364 -0
- package/src/skills/next/SKILL.md +137 -0
- package/src/skills/openspec-apply-change/SKILL.md +165 -0
- package/src/skills/openspec-archive-change/SKILL.md +122 -0
- package/src/skills/openspec-bulk-archive-change/SKILL.md +254 -0
- package/src/skills/openspec-continue-change/SKILL.md +126 -0
- package/src/skills/openspec-explore/SKILL.md +299 -0
- package/src/skills/openspec-ff-change/SKILL.md +109 -0
- package/src/skills/openspec-new-change/SKILL.md +82 -0
- package/src/skills/openspec-onboard/SKILL.md +414 -0
- package/src/skills/openspec-sync-specs/SKILL.md +146 -0
- package/src/skills/openspec-verify-change/SKILL.md +176 -0
- package/src/skills/performance-doctor/SKILL.md +303 -0
- package/src/skills/progress/SKILL.md +193 -0
- package/src/skills/project-navigator/SKILL.md +211 -0
- package/src/skills/redis-cache/SKILL.md +333 -0
- package/src/skills/redis-cache/references/listeners.md +23 -0
- package/src/skills/scheduled-jobs/SKILL.md +314 -0
- package/src/skills/security-guard/SKILL.md +353 -0
- package/src/skills/security-guard/references/encrypt-config.md +103 -0
- package/src/skills/security-guard/references/sensitive-strategies.md +42 -0
- package/src/skills/sms-mail/SKILL.md +308 -0
- package/src/skills/sms-mail/references/mail-config.md +88 -0
- package/src/skills/sms-mail/references/sms-config.md +74 -0
- package/src/skills/social-login/SKILL.md +266 -0
- package/src/skills/social-login/references/provider-configs.md +118 -0
- package/src/skills/start/SKILL.md +154 -0
- package/src/skills/store-pc/SKILL.md +366 -0
- package/src/skills/sync/SKILL.md +149 -0
- package/src/skills/task-tracker/SKILL.md +307 -0
- package/src/skills/tech-decision/SKILL.md +393 -0
- package/src/skills/tenant-management/SKILL.md +288 -0
- package/src/skills/tenant-management/references/tenant-scenarios.md +91 -0
- package/src/skills/test-development/SKILL.md +301 -0
- package/src/skills/test-development/references/parameterized-examples.md +119 -0
- package/src/skills/ui-pc/SKILL.md +438 -0
- package/src/skills/update-status/SKILL.md +159 -0
- package/src/skills/utils-toolkit/SKILL.md +362 -0
- package/src/skills/utils-toolkit/references/redis-utils-api.md +56 -0
- package/src/skills/websocket-sse/SKILL.md +271 -0
- package/src/skills/workflow-engine/SKILL.md +321 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: leniu-security-guard
|
|
3
|
+
description: |
|
|
4
|
+
leniu-tengyun-core / leniu-yunshitang 项目安全权限控制规范。包含认证注解体系、TokenManager API、数据权限校验、SQL注入防护。
|
|
5
|
+
|
|
6
|
+
触发场景:
|
|
7
|
+
- 配置接口认证注解(@RequiresAuthentication/@RequiresGuest/@RequiresPermissions)
|
|
8
|
+
- 使用 TokenManager 获取用户信息或校验权限
|
|
9
|
+
- 防 SQL 注入(MyBatis #{} 参数化查询)
|
|
10
|
+
- 数据归属校验(防越权)
|
|
11
|
+
- VO 敏感字段脱敏处理
|
|
12
|
+
|
|
13
|
+
适用项目:
|
|
14
|
+
- leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
|
|
15
|
+
- leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
|
|
16
|
+
|
|
17
|
+
触发词:安全认证、权限注解、TokenManager、SQL注入防护、数据脱敏、接口安全、RequiresAuthentication
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# leniu-security-guard
|
|
21
|
+
|
|
22
|
+
## 认证注解
|
|
23
|
+
|
|
24
|
+
所有注解包路径:`net.xnzn.framework.secure.filter.annotation.*`
|
|
25
|
+
|
|
26
|
+
| 注解 | 用途 |
|
|
27
|
+
|------|------|
|
|
28
|
+
| `@RequiresAuthentication` | 需要登录认证(最常用,类/方法级) |
|
|
29
|
+
| `@RequiresGuest` | 允许游客访问(公开接口) |
|
|
30
|
+
| `@RequiresPermissions` | 需要指定权限码 |
|
|
31
|
+
| `@RequiresRoles` | 需要指定角色 |
|
|
32
|
+
| `@RequiresUser` | 需要用户登录 |
|
|
33
|
+
| `@RequiresHeader` | 需要指定请求头 |
|
|
34
|
+
|
|
35
|
+
### Controller 认证示例
|
|
36
|
+
|
|
37
|
+
```java
|
|
38
|
+
import net.xnzn.framework.secure.filter.annotation.RequiresAuthentication;
|
|
39
|
+
import net.xnzn.framework.secure.filter.annotation.RequiresGuest;
|
|
40
|
+
import net.xnzn.framework.secure.filter.annotation.RequiresPermissions;
|
|
41
|
+
import net.xnzn.framework.secure.filter.annotation.RequiresRoles;
|
|
42
|
+
|
|
43
|
+
// 类级别:整个 Controller 需要登录
|
|
44
|
+
@RestController
|
|
45
|
+
@RequestMapping("/api/v2/web/xxx")
|
|
46
|
+
@RequiresAuthentication
|
|
47
|
+
public class XxxWebController { }
|
|
48
|
+
|
|
49
|
+
// 方法级别:允许游客访问
|
|
50
|
+
@GetMapping("/public/info")
|
|
51
|
+
@RequiresGuest
|
|
52
|
+
public LeResponse<String> getPublicInfo() { }
|
|
53
|
+
|
|
54
|
+
// 需要特定权限
|
|
55
|
+
@GetMapping("/admin/users")
|
|
56
|
+
@RequiresPermissions("system:user:list")
|
|
57
|
+
public LeResponse<List<User>> listUsers() { }
|
|
58
|
+
|
|
59
|
+
// AND 逻辑(默认):需要所有权限
|
|
60
|
+
@RequiresPermissions(value = {"system:user:add", "system:user:edit"}, logical = Logical.AND)
|
|
61
|
+
|
|
62
|
+
// OR 逻辑:需要任一权限
|
|
63
|
+
@RequiresPermissions(value = {"system:user:add", "system:user:edit"}, logical = Logical.OR)
|
|
64
|
+
|
|
65
|
+
// 需要指定角色
|
|
66
|
+
@RequiresRoles("admin")
|
|
67
|
+
|
|
68
|
+
// 需要所有角色
|
|
69
|
+
@RequiresRoles(value = {"admin", "manager"}, logical = Logical.AND)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
> **注意**:`@RequiresPermissions` 不指定 `value` 时,自动使用 `@RequestMapping` 路径作为权限码。
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## TokenManager API
|
|
77
|
+
|
|
78
|
+
```java
|
|
79
|
+
import net.xnzn.framework.secure.token.TokenManager;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 用户信息
|
|
83
|
+
|
|
84
|
+
```java
|
|
85
|
+
// 登录状态
|
|
86
|
+
TokenManager.isLogin();
|
|
87
|
+
|
|
88
|
+
// 用户 ID
|
|
89
|
+
Long userId = TokenManager.getSubjectId().orElse(null);
|
|
90
|
+
|
|
91
|
+
// 用户名
|
|
92
|
+
String userName = TokenManager.getSubjectName().orElse(null);
|
|
93
|
+
|
|
94
|
+
// 附加数据
|
|
95
|
+
Map<String, String> userData = TokenManager.getSubjectData();
|
|
96
|
+
String orgId = userData.get("orgId");
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 权限/角色校验
|
|
100
|
+
|
|
101
|
+
```java
|
|
102
|
+
// 单个权限
|
|
103
|
+
TokenManager.hasPermission("system:user:add");
|
|
104
|
+
|
|
105
|
+
// 多个权限(AND)
|
|
106
|
+
TokenManager.hasPermission("system:user:add", "system:user:edit");
|
|
107
|
+
|
|
108
|
+
// 任一权限(OR)
|
|
109
|
+
TokenManager.hasAnyPermission("system:user:add", "system:user:edit");
|
|
110
|
+
|
|
111
|
+
// 角色
|
|
112
|
+
TokenManager.hasRole("admin");
|
|
113
|
+
TokenManager.hasAnyRole("admin", "manager");
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 附加数据管理
|
|
117
|
+
|
|
118
|
+
```java
|
|
119
|
+
// 添加
|
|
120
|
+
TokenManager.attachData("orgId", "12345");
|
|
121
|
+
|
|
122
|
+
// 批量添加
|
|
123
|
+
TokenManager.attachData(Map.of("orgId", "12345", "deptId", "67890"));
|
|
124
|
+
|
|
125
|
+
// 获取
|
|
126
|
+
String orgId = TokenManager.getSubjectData().get("orgId");
|
|
127
|
+
|
|
128
|
+
// 移除
|
|
129
|
+
TokenManager.removeData("orgId", "deptId");
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 登出与缓存
|
|
133
|
+
|
|
134
|
+
```java
|
|
135
|
+
// 登出
|
|
136
|
+
TokenManager.logout();
|
|
137
|
+
|
|
138
|
+
// 强制下线
|
|
139
|
+
TokenManager.revokeAuthenticate();
|
|
140
|
+
|
|
141
|
+
// 撤销旧 Token(保留最近 N 个)
|
|
142
|
+
TokenManager.revokeAuthenticate(userId, 3);
|
|
143
|
+
|
|
144
|
+
// 清除权限/角色缓存
|
|
145
|
+
TokenManager.clearPermission(userId);
|
|
146
|
+
TokenManager.clearRole(userId);
|
|
147
|
+
TokenManager.clearRoleAndPermission(userId);
|
|
148
|
+
TokenManager.clearAllRoleAndPermission();
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## WebContext
|
|
154
|
+
|
|
155
|
+
```java
|
|
156
|
+
import net.xnzn.framework.secure.WebContext;
|
|
157
|
+
|
|
158
|
+
HttpServletRequest request = WebContext.get().getRequest().orElse(null);
|
|
159
|
+
HttpServletResponse response = WebContext.get().getResponse().orElse(null);
|
|
160
|
+
Optional<AccessToken> token = WebContext.get().getAccessToken();
|
|
161
|
+
|
|
162
|
+
// 属性存取
|
|
163
|
+
WebContext.get().setAttribute("key", "value");
|
|
164
|
+
Object value = WebContext.get().getAttribute("key");
|
|
165
|
+
WebContext.reset();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 数据权限校验(防越权)
|
|
171
|
+
|
|
172
|
+
```java
|
|
173
|
+
@Transactional(rollbackFor = Exception.class)
|
|
174
|
+
public void delete(Long id) {
|
|
175
|
+
Order order = orderMapper.selectById(id);
|
|
176
|
+
Assert.notNull(order, () -> new LeException("订单不存在"));
|
|
177
|
+
|
|
178
|
+
// 校验数据归属
|
|
179
|
+
Long currentUserId = TokenManager.getSubjectId()
|
|
180
|
+
.orElseThrow(() -> new LeException("用户未登录"));
|
|
181
|
+
if (!order.getUserId().equals(currentUserId)) {
|
|
182
|
+
throw new LeException("无权操作该订单");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
orderMapper.deleteById(id);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 最小权限原则
|
|
190
|
+
|
|
191
|
+
```java
|
|
192
|
+
@RequiresAuthentication
|
|
193
|
+
@RequiresPermissions("order:view")
|
|
194
|
+
public PageVO<OrderVO> pageList(OrderPageParam param) {
|
|
195
|
+
Long userId = TokenManager.getSubjectId()
|
|
196
|
+
.orElseThrow(() -> new LeException("用户未登录"));
|
|
197
|
+
param.setUserId(userId); // 限制只查自己的数据
|
|
198
|
+
return orderService.pageList(param);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## SQL 注入防护
|
|
205
|
+
|
|
206
|
+
### MyBatis XML 必须用 `#{}`
|
|
207
|
+
|
|
208
|
+
```xml
|
|
209
|
+
<!-- 正确:参数化查询 -->
|
|
210
|
+
<select id="selectByName" resultType="User">
|
|
211
|
+
SELECT id, name, mobile FROM user WHERE name = #{name}
|
|
212
|
+
</select>
|
|
213
|
+
|
|
214
|
+
<!-- 错误:${} 有注入风险 -->
|
|
215
|
+
<select id="selectByName" resultType="User">
|
|
216
|
+
SELECT * FROM user WHERE name = '${name}'
|
|
217
|
+
</select>
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 禁止 SELECT *
|
|
221
|
+
|
|
222
|
+
```xml
|
|
223
|
+
<!-- 错误 -->
|
|
224
|
+
SELECT * FROM user WHERE status = #{status}
|
|
225
|
+
|
|
226
|
+
<!-- 正确:明确指定字段 -->
|
|
227
|
+
SELECT id, name, mobile, status FROM user WHERE status = #{status}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## VO 敏感字段处理
|
|
233
|
+
|
|
234
|
+
```java
|
|
235
|
+
@Data
|
|
236
|
+
public class UserVO {
|
|
237
|
+
@JsonIgnore
|
|
238
|
+
private String password; // 密码绝不返回
|
|
239
|
+
|
|
240
|
+
@JsonSerialize(using = MobileSerializer.class)
|
|
241
|
+
private String mobile; // 手机号脱敏
|
|
242
|
+
|
|
243
|
+
@JsonSerialize(using = IdCardSerializer.class)
|
|
244
|
+
private String idCard; // 身份证脱敏
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 日志脱敏
|
|
249
|
+
|
|
250
|
+
```java
|
|
251
|
+
// 错误:记录敏感信息
|
|
252
|
+
log.info("用户登录, username:{}, password:{}", username, password);
|
|
253
|
+
|
|
254
|
+
// 正确:不记录密码
|
|
255
|
+
log.info("用户登录, username:{}", username);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 输入验证
|
|
261
|
+
|
|
262
|
+
```java
|
|
263
|
+
@Data
|
|
264
|
+
public class UserParam {
|
|
265
|
+
@NotBlank(message = "用户名不能为空")
|
|
266
|
+
@Pattern(regexp = "^[a-zA-Z0-9_]{4,20}$", message = "用户名格式不正确")
|
|
267
|
+
private String username;
|
|
268
|
+
|
|
269
|
+
@NotBlank(message = "密码不能为空")
|
|
270
|
+
@Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
|
|
271
|
+
private String password;
|
|
272
|
+
|
|
273
|
+
@NotBlank(message = "手机号不能为空")
|
|
274
|
+
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
|
|
275
|
+
private String mobile;
|
|
276
|
+
|
|
277
|
+
@Email(message = "邮箱格式不正确")
|
|
278
|
+
private String email;
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 限流防护
|
|
285
|
+
|
|
286
|
+
```java
|
|
287
|
+
// Redis 计数器限流
|
|
288
|
+
public void checkRateLimit(Long userId, String api, int limit, int seconds) {
|
|
289
|
+
String key = String.format("rate:%d:%s", userId, api);
|
|
290
|
+
Integer count = RedisUtil.incr(key, (long) seconds);
|
|
291
|
+
if (count > limit) {
|
|
292
|
+
throw new LeException("请求过于频繁,请稍后重试");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 注意事项
|
|
300
|
+
|
|
301
|
+
1. 权限和角色数据自动缓存到 Redis,通过 `clearPermission/clearRole` 手动清除
|
|
302
|
+
2. `TokenManager` 方法需在已登录上下文中使用
|
|
303
|
+
3. `@RequiresGuest` 用于公开接口,不需要登录
|
|
304
|
+
4. MyBatis XML 中必须使用 `#{}` 而非 `${}`
|
|
305
|
+
5. VO 中密码字段必须 `@JsonIgnore`,手机号等用序列化脱敏
|
|
306
|
+
6. 限流 key 格式:`rate:{userId}:{api}`
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: leniu-utils-toolkit
|
|
3
|
+
description: |
|
|
4
|
+
leniu-yunshitang-core 项目工具类使用指南。包含 BeanUtil、CollUtil、ObjectUtil、StrUtil、RedisUtil、JacksonUtil、LeBeanUtil 等核心工具类。
|
|
5
|
+
|
|
6
|
+
触发场景:
|
|
7
|
+
- 对象转换(DTO/VO/Entity)
|
|
8
|
+
- 字符串处理
|
|
9
|
+
- 集合操作
|
|
10
|
+
- 日期时间处理
|
|
11
|
+
- Redis 缓存操作
|
|
12
|
+
- JSON 序列化
|
|
13
|
+
- 模糊查询处理
|
|
14
|
+
|
|
15
|
+
适用项目:
|
|
16
|
+
- leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
|
|
17
|
+
- leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
|
|
18
|
+
|
|
19
|
+
触发词:工具类、BeanUtil、StrUtil、CollUtil、ObjectUtil、RedisUtil、JacksonUtil、LeBeanUtil
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# leniu-yunshitang-core 工具类大全
|
|
24
|
+
|
|
25
|
+
> 本文档专注于 leniu-tengyun-core 项目的 Java 后端工具类。
|
|
26
|
+
|
|
27
|
+
## 快速索引
|
|
28
|
+
|
|
29
|
+
| 功能 | 工具类 | 包路径 | 常用方法 |
|
|
30
|
+
|------|--------|--------|---------|
|
|
31
|
+
| **对象转换** | `BeanUtil` | `cn.hutool.core.bean` | `copyProperties()`, `copyToList()` |
|
|
32
|
+
| 字符串 | `StrUtil` | `cn.hutool.core.util` | `isBlank()`, `format()` |
|
|
33
|
+
| 集合 | `CollUtil` | `cn.hutool.core.collection` | `isEmpty()`, `newArrayList()` |
|
|
34
|
+
| 对象 | `ObjectUtil` | `cn.hutool.core.util` | `isNull()`, `isEmpty()` |
|
|
35
|
+
| Redis缓存 | `RedisUtil` | `net.xnzn.core.base.redis` | `setString()`, `getString()` |
|
|
36
|
+
| JSON | `JacksonUtil` | `net.xnzn.core.common.utils` | `writeValueAsString()`, `readValue()` |
|
|
37
|
+
| 模糊查询 | `LeBeanUtil` | `net.xnzn.core.common.utils` | `fieldLikeHandle()` |
|
|
38
|
+
| 租户上下文 | `TenantContextHolder` | `net.xnzn.framework.data.tenant` | `getTenantId()` |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 1. 对象转换 - BeanUtil(Hutool)
|
|
43
|
+
|
|
44
|
+
> ⚠️ **强制规范**: leniu 项目使用 Hutool 的 `BeanUtil`,不是 MapstructUtils
|
|
45
|
+
|
|
46
|
+
```java
|
|
47
|
+
import cn.hutool.core.bean.BeanUtil;
|
|
48
|
+
|
|
49
|
+
// ✅ 单个对象转换
|
|
50
|
+
XxxVO vo = BeanUtil.copyProperties(entity, XxxVO.class);
|
|
51
|
+
XxxEntity entity = BeanUtil.copyProperties(dto, XxxEntity.class);
|
|
52
|
+
|
|
53
|
+
// ✅ 集合转换
|
|
54
|
+
List<XxxVO> voList = BeanUtil.copyToList(entityList, XxxVO.class);
|
|
55
|
+
List<XxxEntity> entityList = BeanUtil.copyToList(dtoList, XxxEntity.class);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. 字符串操作 - StrUtil(Hutool)
|
|
61
|
+
|
|
62
|
+
```java
|
|
63
|
+
import cn.hutool.core.util.StrUtil;
|
|
64
|
+
|
|
65
|
+
// 判空
|
|
66
|
+
StrUtil.isBlank(str); // null / "" / 空白 都返回 true
|
|
67
|
+
StrUtil.isNotBlank(str);
|
|
68
|
+
StrUtil.isEmpty(str); // null / "" 返回 true
|
|
69
|
+
StrUtil.isNotEmpty(str);
|
|
70
|
+
|
|
71
|
+
// 字符串拼接
|
|
72
|
+
String str = StrUtil.join(",", list); // "1,2,3"
|
|
73
|
+
|
|
74
|
+
// 常量
|
|
75
|
+
StrUtil.COMMA // ","
|
|
76
|
+
StrUtil.DOT // "."
|
|
77
|
+
StrUtil.SLASH // "/"
|
|
78
|
+
|
|
79
|
+
// 截取
|
|
80
|
+
String str = StrUtil.sub(s, 0, 10);
|
|
81
|
+
|
|
82
|
+
// 去除前后缀
|
|
83
|
+
StrUtil.removePrefix(str, "prefix_");
|
|
84
|
+
StrUtil.removeSuffix(str, "_suffix");
|
|
85
|
+
|
|
86
|
+
// 判断前后缀
|
|
87
|
+
StrUtil.startWith(str, "prefix_");
|
|
88
|
+
StrUtil.endWith(str, "_suffix");
|
|
89
|
+
|
|
90
|
+
// 大小写转换
|
|
91
|
+
StrUtil.upperCase(str);
|
|
92
|
+
StrUtil.lowerCase(str);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 3. 集合操作 - CollUtil(Hutool)
|
|
98
|
+
|
|
99
|
+
```java
|
|
100
|
+
import cn.hutool.core.collection.CollUtil;
|
|
101
|
+
|
|
102
|
+
// 判空
|
|
103
|
+
CollUtil.isEmpty(list);
|
|
104
|
+
CollUtil.isNotEmpty(list);
|
|
105
|
+
|
|
106
|
+
// 创建集合
|
|
107
|
+
CollUtil.newArrayList(1, 2, 3);
|
|
108
|
+
CollUtil.newHashSet("a", "b");
|
|
109
|
+
|
|
110
|
+
// 集合转数组
|
|
111
|
+
String[] array = CollUtil.toArray(list, String.class);
|
|
112
|
+
|
|
113
|
+
// 集合转字符串
|
|
114
|
+
String str = CollUtil.join(list, ",");
|
|
115
|
+
|
|
116
|
+
// 分割
|
|
117
|
+
List<String> list = CollUtil.split("1,2,3", ",");
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 4. 对象操作 - ObjectUtil(Hutool)
|
|
123
|
+
|
|
124
|
+
```java
|
|
125
|
+
import cn.hutool.core.util.ObjectUtil;
|
|
126
|
+
|
|
127
|
+
// 判空
|
|
128
|
+
ObjectUtil.isNull(obj);
|
|
129
|
+
ObjectUtil.isNotNull(obj);
|
|
130
|
+
ObjectUtil.isEmpty(obj); // null / 空字符串 / 空集合
|
|
131
|
+
ObjectUtil.isNotEmpty(obj);
|
|
132
|
+
|
|
133
|
+
// 默认值
|
|
134
|
+
ObjectUtil.defaultIfNull(obj, defaultValue);
|
|
135
|
+
|
|
136
|
+
// 相等比较
|
|
137
|
+
ObjectUtil.equal(a, b);
|
|
138
|
+
|
|
139
|
+
// 克隆
|
|
140
|
+
ObjectUtil.clone(obj);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 5. Redis 缓存 - RedisUtil
|
|
146
|
+
|
|
147
|
+
```java
|
|
148
|
+
import net.xnzn.core.base.redis.RedisUtil;
|
|
149
|
+
import org.redisson.api.RLock;
|
|
150
|
+
|
|
151
|
+
// ========== String 操作 ==========
|
|
152
|
+
RedisUtil.setString(key, value); // 存字符串
|
|
153
|
+
RedisUtil.setString(key, value, timeout); // 存字符串(秒)
|
|
154
|
+
RedisUtil.getString(key); // 取字符串
|
|
155
|
+
|
|
156
|
+
// ========== Object 操作 ==========
|
|
157
|
+
RedisUtil.setObj(key, obj); // 存对象
|
|
158
|
+
RedisUtil.setObj(key, obj, timeout); // 存对象(秒)
|
|
159
|
+
RedisUtil.getObj(key); // 取对象
|
|
160
|
+
RedisUtil.getObjOrNull(key); // 取对象(异常返回null)
|
|
161
|
+
|
|
162
|
+
// ========== 删除操作 ==========
|
|
163
|
+
RedisUtil.delete(key); // 删除单个key
|
|
164
|
+
RedisUtil.delete(keys); // 批量删除
|
|
165
|
+
RedisUtil.deleteByPattern("cache:user:*"); // 模式匹配删除
|
|
166
|
+
RedisUtil.deleteKeysInPipeline(keys); // 管道批量删除
|
|
167
|
+
|
|
168
|
+
// ========== 分布式锁 ==========
|
|
169
|
+
RLock lock = RedisUtil.getLock(key); // 获取普通锁
|
|
170
|
+
RLock lock = RedisUtil.getFairLock(key); // 获取公平锁
|
|
171
|
+
RedisUtil.safeUnLock(lock); // 安全解锁
|
|
172
|
+
RedisUtil.isLock(key); // 检查是否锁定
|
|
173
|
+
|
|
174
|
+
// ========== 其他操作 ==========
|
|
175
|
+
RedisUtil.setNx(key, value, expireTime); // SETNX
|
|
176
|
+
RedisUtil.hasKey(key); // 判断key存在
|
|
177
|
+
RedisUtil.incr(key, liveTime); // 自增
|
|
178
|
+
RedisUtil.decr(key, liveTime); // 自减
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 6. JSON 操作 - JacksonUtil
|
|
184
|
+
|
|
185
|
+
```java
|
|
186
|
+
import net.xnzn.core.common.utils.JacksonUtil;
|
|
187
|
+
|
|
188
|
+
// 序列化
|
|
189
|
+
String json = JacksonUtil.writeValueAsString(obj); // 对象->JSON字符串
|
|
190
|
+
String json = JacksonUtil.writeValueAsStringIgnoreNull(obj); // 忽略null字段
|
|
191
|
+
|
|
192
|
+
// 反序列化
|
|
193
|
+
User user = JacksonUtil.readValue(json, User.class); // JSON->对象
|
|
194
|
+
List<User> list = JacksonUtil.readList(json, User.class); // JSON->List
|
|
195
|
+
Map<String, Object> map = JacksonUtil.readValue(json, Map.class);
|
|
196
|
+
|
|
197
|
+
// JsonNode 操作
|
|
198
|
+
JsonNode node = JacksonUtil.readTree(json); // JSON->JsonNode
|
|
199
|
+
User user = JacksonUtil.treeToValue(node, User.class); // JsonNode->对象
|
|
200
|
+
String value = JacksonUtil.getByPath(node, "data.user.name"); // 路径获取
|
|
201
|
+
String str = JacksonUtil.getString(node, "key"); // 取String
|
|
202
|
+
Integer num = JacksonUtil.getInt(node, "key"); // 取Integer
|
|
203
|
+
|
|
204
|
+
// 节点创建
|
|
205
|
+
ObjectNode objectNode = JacksonUtil.objectNode(); // 创建ObjectNode
|
|
206
|
+
ArrayNode arrayNode = JacksonUtil.arrayNode(); // 创建ArrayNode
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 7. 模糊查询处理 - LeBeanUtil
|
|
212
|
+
|
|
213
|
+
```java
|
|
214
|
+
import net.xnzn.core.common.utils.LeBeanUtil;
|
|
215
|
+
|
|
216
|
+
// 字段模糊查询处理(自动添加 %前后缀)
|
|
217
|
+
param.setName(LeBeanUtil.fieldLikeHandle(param.getName()));
|
|
218
|
+
// 效果: "张" -> "%张%"
|
|
219
|
+
|
|
220
|
+
// 常用于 Service 层查询条件处理
|
|
221
|
+
param.setCanteenName(LeBeanUtil.fieldLikeHandle(param.getCanteenName()));
|
|
222
|
+
param.setStallName(LeBeanUtil.fieldLikeHandle(param.getStallName()));
|
|
223
|
+
param.setDishName(LeBeanUtil.fieldLikeHandle(param.getDishName()));
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 8. 租户上下文 - TenantContextHolder
|
|
229
|
+
|
|
230
|
+
```java
|
|
231
|
+
import net.xnzn.framework.data.tenant.TenantContextHolder;
|
|
232
|
+
|
|
233
|
+
// 获取当前租户ID
|
|
234
|
+
Long tenantId = TenantContextHolder.getTenantId();
|
|
235
|
+
|
|
236
|
+
// 常用于 Redis key 构建
|
|
237
|
+
String key = "cache:" + TenantContextHolder.getTenantId() + ":data";
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 9. 日期时间工具
|
|
243
|
+
|
|
244
|
+
```java
|
|
245
|
+
import java.time.LocalDateTime;
|
|
246
|
+
import java.time.format.DateTimeFormatter;
|
|
247
|
+
|
|
248
|
+
// 当前时间
|
|
249
|
+
LocalDateTime now = LocalDateTime.now();
|
|
250
|
+
|
|
251
|
+
// 格式化
|
|
252
|
+
String format = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
253
|
+
|
|
254
|
+
// 解析
|
|
255
|
+
LocalDateTime time = LocalDateTime.parse(str, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 10. 常用正则表达式
|
|
261
|
+
|
|
262
|
+
```java
|
|
263
|
+
// 手机号
|
|
264
|
+
String phoneReg = "^1[3-9]\\d{9}$";
|
|
265
|
+
|
|
266
|
+
// 邮箱
|
|
267
|
+
String emailReg = "^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$";
|
|
268
|
+
|
|
269
|
+
// 身份证
|
|
270
|
+
String idCardReg = "^[1-9]\\d{5}(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]$";
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 工具类选择速查
|
|
276
|
+
|
|
277
|
+
| 需求 | 推荐工具 | 说明 |
|
|
278
|
+
|------|---------|------|
|
|
279
|
+
| 对象转换 | `BeanUtil.copyProperties()` | **leniu 必用** |
|
|
280
|
+
| 集合判空 | `CollUtil.isEmpty()` | Hutool |
|
|
281
|
+
| 对象判空 | `ObjectUtil.isNull()` | Hutool |
|
|
282
|
+
| 字符串判空 | `StrUtil.isBlank()` | Hutool |
|
|
283
|
+
| JSON序列化 | `JacksonUtil.writeValueAsString()` | leniu |
|
|
284
|
+
| Redis缓存 | `RedisUtil.setString()` | leniu |
|
|
285
|
+
| 模糊查询 | `LeBeanUtil.fieldLikeHandle()` | leniu |
|
|
286
|
+
| 租户ID | `TenantContextHolder.getTenantId()` | leniu |
|
|
287
|
+
| 字符串拼接 | `StrUtil.join()` | Hutool |
|
|
288
|
+
| 日期格式化 | `DateTimeFormatter` | JDK 8 |
|
|
289
|
+
| ID 生成 | `IdUtil.getSnowflakeNextId()` | Hutool |
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 禁止事项
|
|
294
|
+
|
|
295
|
+
```java
|
|
296
|
+
// ❌ 禁止使用 MapstructUtils(leniu 使用 BeanUtil)
|
|
297
|
+
// MapstructUtils.convert(source, Target.class); // 禁止!
|
|
298
|
+
|
|
299
|
+
// ❌ 禁止使用 Map 传递业务数据
|
|
300
|
+
public Map<String, Object> getXxx() { ... } // 禁止!
|
|
301
|
+
|
|
302
|
+
// ❌ 禁止手写 stream 转换(应使用 BeanUtil.copyToList)
|
|
303
|
+
list.stream().map(item -> BeanUtil.copyProperties(item, XxxVO.class)).collect(Collectors.toList());
|
|
304
|
+
// ✅ 推荐
|
|
305
|
+
BeanUtil.copyToList(list, XxxVO.class);
|
|
306
|
+
|
|
307
|
+
// ❌ 禁止使用完整类型引用
|
|
308
|
+
public net.xnzn.core.common.util.LeResponse<XxxVo> getXxx() // 禁止!
|
|
309
|
+
// ✅ 正确:先 import,再使用短类名
|
|
310
|
+
import net.xnzn.core.common.util.LeResponse;
|
|
311
|
+
public LeResponse<XxxVo> getXxx()
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## 常见错误对比
|
|
317
|
+
|
|
318
|
+
### ❌ 错误写法
|
|
319
|
+
|
|
320
|
+
```java
|
|
321
|
+
// 错误 1: 使用 RuoYi 的 MapstructUtils
|
|
322
|
+
MapstructUtils.convert(source, Target.class); // ❌ leniu 用 BeanUtil
|
|
323
|
+
|
|
324
|
+
// 错误 2: 使用 RuoYi 的 StringUtils
|
|
325
|
+
StringUtils.isBlank(str); // ❌ leniu 用 StrUtil
|
|
326
|
+
|
|
327
|
+
// 错误 3: 使用 RuoYi 的 RedisUtils
|
|
328
|
+
RedisUtils.setCacheObject(key, value); // ❌ leniu 用 RedisUtil
|
|
329
|
+
|
|
330
|
+
// 错误 4: 使用 RuoYi 的 JsonUtils
|
|
331
|
+
JsonUtils.toJsonString(obj); // ❌ leniu 用 JacksonUtil
|
|
332
|
+
|
|
333
|
+
// 错误 5: 使用 RuoYi 的 TenantHelper
|
|
334
|
+
TenantHelper.getTenantId(); // ❌ leniu 用 TenantContextHolder
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### ✅ 正确写法
|
|
338
|
+
|
|
339
|
+
```java
|
|
340
|
+
// 正确 1: 使用 leniu 的 BeanUtil
|
|
341
|
+
BeanUtil.copyProperties(source, Target.class); // ✅
|
|
342
|
+
|
|
343
|
+
// 正确 2: 使用 Hutool 的 StrUtil
|
|
344
|
+
StrUtil.isBlank(str); // ✅
|
|
345
|
+
|
|
346
|
+
// 正确 3: 使用 leniu 的 RedisUtil
|
|
347
|
+
RedisUtil.setString(key, value); // ✅
|
|
348
|
+
|
|
349
|
+
// 正确 4: 使用 leniu 的 JacksonUtil
|
|
350
|
+
JacksonUtil.writeValueAsString(obj); // ✅
|
|
351
|
+
|
|
352
|
+
// 正确 5: 使用 leniu 的 TenantContextHolder
|
|
353
|
+
TenantContextHolder.getTenantId(); // ✅
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## 工具类选择决策
|
|
359
|
+
|
|
360
|
+
| 场景 | RuoYi-Vue-Plus | leniu-tengyun-core |
|
|
361
|
+
|------|----------------|-------------------|
|
|
362
|
+
| 对象转换 | `MapstructUtils.convert()` | `BeanUtil.copyProperties()` |
|
|
363
|
+
| 集合判空 | `CollUtil.isEmpty()` | `CollUtil.isEmpty()` |
|
|
364
|
+
| 对象判空 | `ObjectUtil.isNull()` | `ObjectUtil.isNull()` |
|
|
365
|
+
| 字符串判空 | `StringUtils.isBlank()` | `StrUtil.isBlank()` |
|
|
366
|
+
| JSON序列化 | `JsonUtils.toJsonString()` | `JacksonUtil.writeValueAsString()` |
|
|
367
|
+
| Redis缓存 | `RedisUtils.setCacheObject()` | `RedisUtil.setString()` |
|
|
368
|
+
| 模糊查询 | `"%" + keyword + "%"` | `LeBeanUtil.fieldLikeHandle()` |
|
|
369
|
+
| 租户ID | `LoginHelper.getTenantId()` | `TenantContextHolder.getTenantId()` |
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## 相关技能
|
|
374
|
+
|
|
375
|
+
| 需要了解 | 激活 Skill |
|
|
376
|
+
|---------|-----------|
|
|
377
|
+
| Entity/VO/DTO 设计 | `leniu-java-entity` |
|
|
378
|
+
| MyBatis 使用 | `leniu-java-mybatis` |
|
|
379
|
+
| 异常处理 | `leniu-error-handler` |
|
|
380
|
+
| 数据库设计 | `leniu-database-ops` |
|