ai-engineering-init 1.7.0 → 1.10.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 (139) hide show
  1. package/.claude/agents/bug-analyzer.md +103 -0
  2. package/.claude/agents/code-reviewer.md +115 -5
  3. package/.claude/agents/image-reader.md +154 -0
  4. package/.claude/agents/loki-runner.md +80 -0
  5. package/.claude/agents/mysql-runner.md +81 -0
  6. package/.claude/agents/requirements-analyzer.md +162 -0
  7. package/.claude/agents/task-fetcher.md +75 -0
  8. package/.claude/commands/dev.md +29 -0
  9. package/.claude/commands/next.md +31 -1
  10. package/.claude/commands/progress.md +23 -1
  11. package/.claude/hooks/skill-forced-eval.js +46 -62
  12. package/.claude/settings.json +10 -1
  13. package/.claude/skills/api-development/SKILL.md +179 -130
  14. package/.claude/skills/architecture-design/SKILL.md +102 -212
  15. package/.claude/skills/backend-annotations/SKILL.md +166 -220
  16. package/.claude/skills/bug-detective/SKILL.md +225 -186
  17. package/.claude/skills/code-patterns/SKILL.md +127 -244
  18. package/.claude/skills/collaborating-with-codex/SKILL.md +96 -113
  19. package/.claude/skills/crud-development/SKILL.md +226 -307
  20. package/.claude/skills/data-permission/SKILL.md +131 -202
  21. package/.claude/skills/database-ops/SKILL.md +158 -355
  22. package/.claude/skills/error-handler/SKILL.md +224 -285
  23. package/.claude/skills/file-oss-management/SKILL.md +174 -169
  24. package/.claude/skills/git-workflow/SKILL.md +123 -341
  25. package/.claude/skills/json-serialization/SKILL.md +121 -137
  26. package/.claude/skills/performance-doctor/SKILL.md +83 -89
  27. package/.claude/skills/redis-cache/SKILL.md +134 -185
  28. package/.claude/skills/scheduled-jobs/SKILL.md +187 -224
  29. package/.claude/skills/security-guard/SKILL.md +168 -276
  30. package/.claude/skills/sms-mail/SKILL.md +266 -228
  31. package/.claude/skills/social-login/SKILL.md +257 -195
  32. package/.claude/skills/tenant-management/SKILL.md +172 -188
  33. package/.claude/skills/utils-toolkit/SKILL.md +214 -222
  34. package/.claude/skills/websocket-sse/SKILL.md +251 -172
  35. package/.claude/skills/workflow-engine/SKILL.md +178 -250
  36. package/.codex/skills/api-development/SKILL.md +179 -130
  37. package/.codex/skills/architecture-design/SKILL.md +102 -212
  38. package/.codex/skills/backend-annotations/SKILL.md +166 -220
  39. package/.codex/skills/bug-detective/SKILL.md +225 -186
  40. package/.codex/skills/code-patterns/SKILL.md +127 -244
  41. package/.codex/skills/collaborating-with-codex/SKILL.md +96 -113
  42. package/.codex/skills/crud-development/SKILL.md +226 -307
  43. package/.codex/skills/data-permission/SKILL.md +131 -202
  44. package/.codex/skills/database-ops/SKILL.md +158 -355
  45. package/.codex/skills/dev/SKILL.md +476 -131
  46. package/.codex/skills/error-handler/SKILL.md +224 -285
  47. package/.codex/skills/file-oss-management/SKILL.md +174 -169
  48. package/.codex/skills/git-workflow/SKILL.md +123 -341
  49. package/.codex/skills/json-serialization/SKILL.md +121 -137
  50. package/.codex/skills/next/SKILL.md +186 -42
  51. package/.codex/skills/performance-doctor/SKILL.md +83 -89
  52. package/.codex/skills/progress/SKILL.md +147 -76
  53. package/.codex/skills/redis-cache/SKILL.md +134 -185
  54. package/.codex/skills/scheduled-jobs/SKILL.md +187 -224
  55. package/.codex/skills/security-guard/SKILL.md +168 -276
  56. package/.codex/skills/sms-mail/SKILL.md +266 -228
  57. package/.codex/skills/social-login/SKILL.md +257 -195
  58. package/.codex/skills/tenant-management/SKILL.md +172 -188
  59. package/.codex/skills/utils-toolkit/SKILL.md +214 -222
  60. package/.codex/skills/websocket-sse/SKILL.md +251 -172
  61. package/.codex/skills/workflow-engine/SKILL.md +178 -250
  62. package/.cursor/agents/bug-analyzer.md +102 -0
  63. package/.cursor/agents/code-reviewer.md +80 -97
  64. package/.cursor/agents/image-reader.md +154 -0
  65. package/.cursor/agents/loki-runner.md +80 -0
  66. package/.cursor/agents/mysql-runner.md +81 -0
  67. package/.cursor/agents/project-manager.md +1 -1
  68. package/.cursor/agents/requirements-analyzer.md +141 -0
  69. package/.cursor/agents/task-fetcher.md +75 -0
  70. package/.cursor/hooks/cursor-skill-eval.js +66 -6
  71. package/.cursor/skills/api-development/SKILL.md +179 -130
  72. package/.cursor/skills/architecture-design/SKILL.md +102 -212
  73. package/.cursor/skills/backend-annotations/SKILL.md +166 -220
  74. package/.cursor/skills/bug-detective/SKILL.md +225 -186
  75. package/.cursor/skills/code-patterns/SKILL.md +127 -244
  76. package/.cursor/skills/collaborating-with-codex/SKILL.md +96 -113
  77. package/.cursor/skills/crud-development/SKILL.md +226 -307
  78. package/.cursor/skills/data-permission/SKILL.md +131 -202
  79. package/.cursor/skills/database-ops/SKILL.md +158 -355
  80. package/.cursor/skills/error-handler/SKILL.md +224 -285
  81. package/.cursor/skills/file-oss-management/SKILL.md +174 -169
  82. package/.cursor/skills/git-workflow/SKILL.md +123 -341
  83. package/.cursor/skills/json-serialization/SKILL.md +121 -137
  84. package/.cursor/skills/performance-doctor/SKILL.md +83 -89
  85. package/.cursor/skills/redis-cache/SKILL.md +134 -185
  86. package/.cursor/skills/scheduled-jobs/SKILL.md +187 -224
  87. package/.cursor/skills/security-guard/SKILL.md +168 -276
  88. package/.cursor/skills/sms-mail/SKILL.md +266 -228
  89. package/.cursor/skills/social-login/SKILL.md +257 -195
  90. package/.cursor/skills/tenant-management/SKILL.md +172 -188
  91. package/.cursor/skills/utils-toolkit/SKILL.md +214 -222
  92. package/.cursor/skills/websocket-sse/SKILL.md +251 -172
  93. package/.cursor/skills/workflow-engine/SKILL.md +178 -250
  94. package/AGENTS.md +117 -540
  95. package/CLAUDE.md +105 -117
  96. package/README.md +37 -6
  97. package/bin/index.js +5 -1
  98. package/package.json +1 -1
  99. package/src/skills/api-development/SKILL.md +179 -130
  100. package/src/skills/architecture-design/SKILL.md +102 -212
  101. package/src/skills/backend-annotations/SKILL.md +166 -220
  102. package/src/skills/bug-detective/SKILL.md +225 -186
  103. package/src/skills/code-patterns/SKILL.md +127 -244
  104. package/src/skills/collaborating-with-codex/SKILL.md +96 -113
  105. package/src/skills/crud-development/SKILL.md +226 -307
  106. package/src/skills/data-permission/SKILL.md +131 -202
  107. package/src/skills/database-ops/SKILL.md +158 -355
  108. package/src/skills/error-handler/SKILL.md +224 -285
  109. package/src/skills/file-oss-management/SKILL.md +174 -169
  110. package/src/skills/git-workflow/SKILL.md +123 -341
  111. package/src/skills/json-serialization/SKILL.md +121 -137
  112. package/src/skills/performance-doctor/SKILL.md +83 -89
  113. package/src/skills/redis-cache/SKILL.md +134 -185
  114. package/src/skills/scheduled-jobs/SKILL.md +187 -224
  115. package/src/skills/security-guard/SKILL.md +168 -276
  116. package/src/skills/sms-mail/SKILL.md +266 -228
  117. package/src/skills/social-login/SKILL.md +257 -195
  118. package/src/skills/tenant-management/SKILL.md +172 -188
  119. package/src/skills/utils-toolkit/SKILL.md +214 -222
  120. package/src/skills/websocket-sse/SKILL.md +251 -172
  121. package/src/skills/workflow-engine/SKILL.md +178 -250
  122. package/.claude/skills/skill-creator/LICENSE.txt +0 -202
  123. package/.claude/skills/skill-creator/SKILL.md +0 -479
  124. package/.claude/skills/skill-creator/agents/analyzer.md +0 -274
  125. package/.claude/skills/skill-creator/agents/comparator.md +0 -202
  126. package/.claude/skills/skill-creator/agents/grader.md +0 -223
  127. package/.claude/skills/skill-creator/assets/eval_review.html +0 -146
  128. package/.claude/skills/skill-creator/eval-viewer/generate_review.py +0 -471
  129. package/.claude/skills/skill-creator/eval-viewer/viewer.html +0 -1325
  130. package/.claude/skills/skill-creator/references/schemas.md +0 -430
  131. package/.claude/skills/skill-creator/scripts/__init__.py +0 -0
  132. package/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
  133. package/.claude/skills/skill-creator/scripts/generate_report.py +0 -326
  134. package/.claude/skills/skill-creator/scripts/improve_description.py +0 -248
  135. package/.claude/skills/skill-creator/scripts/package_skill.py +0 -136
  136. package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -103
  137. package/.claude/skills/skill-creator/scripts/run_eval.py +0 -310
  138. package/.claude/skills/skill-creator/scripts/run_loop.py +0 -332
  139. package/.claude/skills/skill-creator/scripts/utils.py +0 -47
@@ -1,371 +1,310 @@
1
1
  ---
2
2
  name: error-handler
3
3
  description: |
4
- 后端异常处理规范。包含 ServiceException 用法、全局异常处理器、参数校验、日志规范、错误码设计。
5
-
6
- 触发场景:
7
- - 抛出业务异常(ServiceException)
8
- - 全局异常处理器配置
9
- - 参数校验异常处理
10
- - 日志记录规范
11
- - 错误码设计与国际化
12
- - 事务异常处理
13
-
14
- 触发词:异常、ServiceException、throw、错误处理、全局异常、@Validated、参数校验、日志、log、错误码、事务、@Transactional、try-catch、异常捕获
15
-
16
- 注意:
17
- - 如果是安全相关(认证授权、数据脱敏),请使用 security-guard。
18
- - 如果是数据权限(@DataPermission),请使用 data-permission。
4
+ 通用异常处理指南。涵盖自定义业务异常、全局异常处理器、参数校验等。
5
+ 触发场景:异常设计、错误处理、参数校验、全局异常捕获。
6
+ 触发词:异常处理、错误处理、参数校验、validation、异常捕获。
7
+ 注意:如果项目有专属技能(如 `leniu-error-handler`),优先使用专属版本。
19
8
  ---
20
9
 
21
- # 后端异常处理指南
10
+ # 异常处理指南
22
11
 
23
- > 本项目是纯后端项目,本文档专注于 Java 后端异常处理规范。
12
+ > 通用模板。如果项目有专属技能(如 `leniu-error-handler`),优先使用。
24
13
 
25
- ---
14
+ ## 核心规范
26
15
 
27
- ## 快速索引
16
+ ### 异常分层设计
28
17
 
29
- | 场景 | 推荐方式 |
30
- |------|---------|
31
- | 业务异常 | `throw new ServiceException("msg")` |
32
- | 带参数异常 | `throw new ServiceException("用户 {} 不存在", userId)` |
33
- | 带错误码 | `throw new ServiceException("msg", 200101)` |
34
- | 参数校验 | `@Validated(AddGroup.class)` |
35
- | 日志记录 | `log.error("msg: {}", e.getMessage(), e)` |
18
+ ```
19
+ RuntimeException
20
+ └── BusinessException # 业务异常基类
21
+ ├── NotFoundException # 资源不存在 (404)
22
+ ├── ForbiddenException # 无权限 (403)
23
+ ├── BadRequestException # 参数错误 (400)
24
+ └── ConflictException # 数据冲突 (409)
25
+ ```
36
26
 
37
- ---
27
+ ### 异常处理原则
28
+
29
+ 1. **业务异常用自定义异常类**,不要直接抛 `RuntimeException`
30
+ 2. **全局统一捕获**,通过 `@RestControllerAdvice` 处理
31
+ 3. **区分异常层级**:Controller 层不 try-catch(交给全局处理器),Service 层只捕获需要转换的异常
32
+ 4. **异常信息面向用户**:不暴露堆栈、SQL 等技术细节
33
+ 5. **日志记录完整**:异常日志包含完整上下文和堆栈
38
34
 
39
- ## 1. 业务异常 - ServiceException
35
+ ## 代码示例
40
36
 
41
- ### 基本用法
37
+ ### 1. 自定义业务异常
42
38
 
43
39
  ```java
44
- import org.dromara.common.core.exception.ServiceException;
40
+ package [你的包名].exception;
45
41
 
46
- // ✅ 基本用法:抛出业务异常
47
- throw new ServiceException("用户不存在");
42
+ import lombok.Getter;
48
43
 
49
- // ✅ 带占位符(支持 {} 占位符,使用 Hutool StrFormatter)
50
- throw new ServiceException("用户 {} 不存在", userId);
51
- throw new ServiceException("订单 {} 状态 {} 无法支付", orderId, status);
44
+ @Getter
45
+ public class BusinessException extends RuntimeException {
52
46
 
53
- // 带错误码(第二个参数是 Integer code
54
- throw new ServiceException("用户不存在", 200101);
47
+ private final int code;
55
48
 
56
- // 条件抛出(手动检查)
57
- if (ObjectUtil.isNull(user)) {
58
- throw new ServiceException("用户不存在");
59
- }
49
+ public BusinessException(String message) {
50
+ super(message);
51
+ this.code = 500;
52
+ }
60
53
 
61
- // 参数校验
62
- if (StringUtils.isBlank(bo.getName())) {
63
- throw new ServiceException("名称不能为空");
54
+ public BusinessException(int code, String message) {
55
+ super(message);
56
+ this.code = code;
57
+ }
58
+
59
+ public BusinessException(int code, String message, Throwable cause) {
60
+ super(message, cause);
61
+ this.code = code;
62
+ }
64
63
  }
65
64
  ```
66
65
 
67
- ### ServiceException 完整 API
68
-
69
- > **🔴 重要**:ServiceException **没有静态工厂方法**(如 `of()`)和条件检查方法(如 `throwIf()`, `notNull()`),必须使用 `new` 关键字创建异常对象。
70
-
71
- | 构造函数 | 说明 | 示例 |
72
- |---------|------|------|
73
- | `new ServiceException(String message)` | 只有错误消息 | `new ServiceException("操作失败")` |
74
- | `new ServiceException(String message, Object... args)` | 带占位符参数 | `new ServiceException("用户{}不存在", userId)` |
75
- | `new ServiceException(String message, Integer code)` | 带错误码 | `new ServiceException("用户不存在", 200101)` |
76
-
77
- **链式调用方法**:
78
- - `setMessage(String message)`: 设置错误消息
79
- - `setDetailMessage(String detailMessage)`: 设置详细错误(用于内部调试)
80
-
81
- **源码位置**: `ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java`
66
+ ```java
67
+ package [你的包名].exception;
82
68
 
83
- ---
69
+ public class NotFoundException extends BusinessException {
84
70
 
85
- ## 2. 全局异常处理器
71
+ public NotFoundException(String message) {
72
+ super(404, message);
73
+ }
86
74
 
87
- 框架已提供全局异常处理器,自动捕获并处理各类异常。
75
+ public static NotFoundException of(String resource, Object id) {
76
+ return new NotFoundException(resource + " 不存在: " + id);
77
+ }
78
+ }
79
+ ```
88
80
 
89
- **位置**: `ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java`
81
+ ### 2. 全局异常处理器
90
82
 
91
- ### 异常处理映射
83
+ ```java
84
+ package [你的包名].handler;
92
85
 
93
- > **注意**:所有异常的 HTTP 响应状态码均为 200,错误码通过 `R.code` 字段返回。
86
+ import [你的包名].exception.BusinessException;
87
+ import [你的包名].exception.NotFoundException;
88
+ import jakarta.validation.ConstraintViolation;
89
+ import jakarta.validation.ConstraintViolationException;
90
+ import lombok.extern.slf4j.Slf4j;
91
+ import org.springframework.http.HttpStatus;
92
+ import org.springframework.http.ResponseEntity;
93
+ import org.springframework.validation.FieldError;
94
+ import org.springframework.web.bind.MethodArgumentNotValidException;
95
+ import org.springframework.web.bind.MissingServletRequestParameterException;
96
+ import org.springframework.web.bind.annotation.ExceptionHandler;
97
+ import org.springframework.web.bind.annotation.RestControllerAdvice;
94
98
 
95
- | 异常类型 | 处理方式 | R.code |
96
- |---------|---------|--------|
97
- | `ServiceException` | 返回业务错误信息 | 自定义 code 或 500 |
98
- | `BindException` | 返回参数绑定错误 | 500 |
99
- | `ConstraintViolationException` | 返回参数校验错误 | 500 |
100
- | `MethodArgumentNotValidException` | 返回参数校验错误 | 500 |
101
- | `HandlerMethodValidationException` | 返回 @Validated 校验错误 | 500 |
102
- | `HttpRequestMethodNotSupportedException` | 请求方式不支持 | 405 |
103
- | `NoHandlerFoundException` | 路由不存在 | 404 |
104
- | `JsonParseException` | JSON 解析失败 | 400 |
105
- | `HttpMessageNotReadableException` | 请求参数格式错误 | 400 |
106
- | `ExpressionException` | SpEL 表达式解析失败 | 500 |
107
- | `RuntimeException` / `Exception` | 系统错误 | 500 |
99
+ import java.util.Map;
100
+ import java.util.stream.Collectors;
108
101
 
109
- ---
102
+ @Slf4j
103
+ @RestControllerAdvice
104
+ public class GlobalExceptionHandler {
105
+
106
+ /**
107
+ * 业务异常
108
+ */
109
+ @ExceptionHandler(BusinessException.class)
110
+ public ResponseEntity<Result<Void>> handleBusinessException(BusinessException e) {
111
+ log.warn("业务异常: {}", e.getMessage());
112
+ return ResponseEntity
113
+ .status(HttpStatus.BAD_REQUEST)
114
+ .body(Result.fail(e.getCode(), e.getMessage()));
115
+ }
110
116
 
111
- ## 3. 参数校验
117
+ /**
118
+ * 资源不存在
119
+ */
120
+ @ExceptionHandler(NotFoundException.class)
121
+ public ResponseEntity<Result<Void>> handleNotFoundException(NotFoundException e) {
122
+ log.warn("资源不存在: {}", e.getMessage());
123
+ return ResponseEntity
124
+ .status(HttpStatus.NOT_FOUND)
125
+ .body(Result.fail(404, e.getMessage()));
126
+ }
112
127
 
113
- ### 使用 @Validated 自动校验
128
+ /**
129
+ * @RequestBody 参数校验失败
130
+ */
131
+ @ExceptionHandler(MethodArgumentNotValidException.class)
132
+ public ResponseEntity<Result<Map<String, String>>> handleValidationException(
133
+ MethodArgumentNotValidException e) {
134
+ Map<String, String> errors = e.getBindingResult().getFieldErrors().stream()
135
+ .collect(Collectors.toMap(
136
+ FieldError::getField,
137
+ fe -> fe.getDefaultMessage() != null ? fe.getDefaultMessage() : "校验失败",
138
+ (v1, v2) -> v1
139
+ ));
140
+ log.warn("参数校验失败: {}", errors);
141
+ return ResponseEntity
142
+ .status(HttpStatus.BAD_REQUEST)
143
+ .body(Result.fail(400, "参数校验失败"));
144
+ }
114
145
 
115
- ```java
116
- import org.dromara.common.core.validate.AddGroup;
117
- import org.dromara.common.core.validate.EditGroup;
146
+ /**
147
+ * @RequestParam / @PathVariable 校验失败
148
+ */
149
+ @ExceptionHandler(ConstraintViolationException.class)
150
+ public ResponseEntity<Result<Void>> handleConstraintViolation(ConstraintViolationException e) {
151
+ String message = e.getConstraintViolations().stream()
152
+ .map(ConstraintViolation::getMessage)
153
+ .collect(Collectors.joining("; "));
154
+ log.warn("约束校验失败: {}", message);
155
+ return ResponseEntity
156
+ .status(HttpStatus.BAD_REQUEST)
157
+ .body(Result.fail(400, message));
158
+ }
118
159
 
119
- // Controller 层校验
120
- @PostMapping
121
- public R<Long> add(@Validated(AddGroup.class) @RequestBody XxxBo bo) {
122
- // 参数校验失败会自动抛出异常
123
- // 全局异常处理器会自动捕获并返回错误信息
124
- return R.ok(xxxService.insert(bo));
125
- }
160
+ /**
161
+ * 缺少请求参数
162
+ */
163
+ @ExceptionHandler(MissingServletRequestParameterException.class)
164
+ public ResponseEntity<Result<Void>> handleMissingParam(MissingServletRequestParameterException e) {
165
+ log.warn("缺少请求参数: {}", e.getParameterName());
166
+ return ResponseEntity
167
+ .status(HttpStatus.BAD_REQUEST)
168
+ .body(Result.fail(400, "缺少参数: " + e.getParameterName()));
169
+ }
126
170
 
127
- @PutMapping
128
- public R<Void> edit(@Validated(EditGroup.class) @RequestBody XxxBo bo) {
129
- return toAjax(xxxService.update(bo));
171
+ /**
172
+ * 兜底:未知异常
173
+ */
174
+ @ExceptionHandler(Exception.class)
175
+ public ResponseEntity<Result<Void>> handleException(Exception e) {
176
+ log.error("系统异常", e);
177
+ return ResponseEntity
178
+ .status(HttpStatus.INTERNAL_SERVER_ERROR)
179
+ .body(Result.fail(500, "系统繁忙,请稍后重试"));
180
+ }
130
181
  }
131
182
  ```
132
183
 
133
- ### BO 类校验注解
184
+ ### 3. 参数校验(jakarta.validation)
134
185
 
135
186
  ```java
136
- public class XxxBo extends BaseEntity {
187
+ package [你的包名].dto;
137
188
 
138
- @NotNull(message = "ID不能为空", groups = { EditGroup.class })
139
- private Long id;
189
+ import jakarta.validation.constraints.*;
190
+ import lombok.Data;
140
191
 
141
- @NotBlank(message = "名称不能为空", groups = { AddGroup.class, EditGroup.class })
142
- @Size(max = 100, message = "名称长度不能超过100个字符")
143
- private String name;
192
+ @Data
193
+ public class UserCreateDTO {
144
194
 
195
+ @NotBlank(message = "用户名不能为空")
196
+ @Size(min = 2, max = 32, message = "用户名长度 2-32 位")
197
+ private String username;
198
+
199
+ @NotBlank(message = "邮箱不能为空")
145
200
  @Email(message = "邮箱格式不正确")
146
201
  private String email;
147
202
 
203
+ @NotNull(message = "年龄不能为空")
204
+ @Min(value = 1, message = "年龄最小为 1")
205
+ @Max(value = 150, message = "年龄最大为 150")
206
+ private Integer age;
207
+
148
208
  @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
149
209
  private String phone;
150
-
151
- @Min(value = 0, message = "数量不能小于0")
152
- @Max(value = 9999, message = "数量不能大于9999")
153
- private Integer count;
154
210
  }
155
211
  ```
156
212
 
157
- ### 手动校验(Service 层)
158
-
213
+ **Controller 中使用**:
159
214
  ```java
160
- import org.dromara.common.core.utils.ValidatorUtils;
161
-
162
- // 手动触发校验
163
- ValidatorUtils.validate(bo, AddGroup.class);
164
- ValidatorUtils.validate(bo, EditGroup.class);
215
+ @PostMapping
216
+ public ResponseEntity<Result<Long>> create(@Valid @RequestBody UserCreateDTO dto) {
217
+ return ResponseEntity.ok(Result.ok(userService.create(dto)));
218
+ }
165
219
  ```
166
220
 
167
- ---
168
-
169
- ## 4. 日志规范
170
-
171
- ### 日志级别
172
-
173
- | 级别 | 使用场景 | 示例 |
174
- |------|---------|------|
175
- | ERROR | 系统错误、业务异常 | 数据库连接失败、第三方接口超时 |
176
- | WARN | 警告信息、潜在问题 | 缓存未命中、重试操作 |
177
- | INFO | 重要业务流程、操作记录 | 用户登录、订单创建 |
178
- | DEBUG | 开发调试信息 | 方法入参、中间变量 |
179
- | TRACE | 详细追踪信息 | 循环内部数据 |
180
-
181
- ### 日志最佳实践
221
+ ### 4. 分组校验
182
222
 
183
223
  ```java
184
- import lombok.extern.slf4j.Slf4j;
185
-
186
- @Slf4j
187
- @Service
188
- public class XxxServiceImpl implements IXxxService {
224
+ public interface CreateGroup {}
225
+ public interface UpdateGroup {}
189
226
 
190
- // ✅ 好的:使用占位符(性能更好)
191
- log.info("处理订单: {}, 状态: {}", orderId, status);
227
+ @Data
228
+ public class UserDTO {
192
229
 
193
- // 不好:字符串拼接(每次都会拼接,即使日志级别不输出)
194
- log.info("处理订单: " + orderId + ", 状态: " + status);
195
-
196
- // ✅ 好的:异常日志带堆栈(第三个参数传异常对象)
197
- log.error("处理失败: {}", e.getMessage(), e);
198
-
199
- // ❌ 不好:只记录消息,丢失堆栈
200
- log.error("处理失败: {}", e.getMessage());
230
+ @Null(groups = CreateGroup.class, message = "创建时不能指定 ID")
231
+ @NotNull(groups = UpdateGroup.class, message = "更新时必须指定 ID")
232
+ private Long id;
201
233
 
202
- // 好的:判断日志级别(避免不必要的序列化开销)
203
- if (log.isDebugEnabled()) {
204
- log.debug("详细数据: {}", JsonUtils.toJsonString(data));
205
- }
234
+ @NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
235
+ private String username;
236
+ }
206
237
 
207
- // 好的:敏感信息脱敏
208
- log.info("用户登录,手机: {}", DesensitizedUtil.mobilePhone(phone));
238
+ // Controller 使用
239
+ @PostMapping
240
+ public Result<Long> create(@Validated(CreateGroup.class) @RequestBody UserDTO dto) { ... }
209
241
 
210
- // ❌ 不好:记录敏感信息
211
- log.info("用户登录,手机: {}", phone);
212
- }
242
+ @PutMapping("/{id}")
243
+ public Result<Void> update(@Validated(UpdateGroup.class) @RequestBody UserDTO dto) { ... }
213
244
  ```
214
245
 
215
- ### Service 层日志示例
246
+ ### 5. Service 层异常使用
216
247
 
217
248
  ```java
218
- @Slf4j
219
- @RequiredArgsConstructor
220
249
  @Service
221
- public class SysUserServiceImpl implements ISysUserService {
222
-
223
- private final SysUserMapper baseMapper;
250
+ public class UserServiceImpl implements IUserService {
224
251
 
225
252
  @Override
226
- @Transactional(rollbackFor = Exception.class)
227
- public Long insertUser(SysUserBo bo) {
228
- log.info("开始新增用户,用户名: {}", bo.getUserName());
229
-
230
- // 1. 业务校验
231
- SysUser existUser = baseMapper.selectUserByUserName(bo.getUserName());
232
- if (ObjectUtil.isNotNull(existUser)) {
233
- throw new ServiceException("用户名 {} 已存在", bo.getUserName());
253
+ public UserVO getById(Long id) {
254
+ User user = userMapper.selectById(id);
255
+ if (user == null) {
256
+ throw NotFoundException.of("用户", id);
234
257
  }
235
-
236
- // 2. 执行插入
237
- SysUser user = MapstructUtils.convert(bo, SysUser.class);
238
- baseMapper.insert(user);
239
-
240
- log.info("新增用户成功,用户ID: {}, 用户名: {}", user.getUserId(), user.getUserName());
241
- return user.getUserId();
258
+ // ... 转换为 VO
259
+ return userVO;
242
260
  }
243
261
 
244
262
  @Override
245
- public SysUserVo selectUserById(Long userId) {
246
- SysUser user = baseMapper.selectById(userId);
247
- if (ObjectUtil.isNull(user)) {
248
- throw new ServiceException("用户 {} 不存在", userId);
263
+ public void updateEmail(Long id, String email) {
264
+ // 检查邮箱是否已被使用
265
+ User existing = userMapper.selectByEmail(email);
266
+ if (existing != null && !existing.getId().equals(id)) {
267
+ throw new BusinessException(409, "邮箱已被其他用户使用");
249
268
  }
250
- return MapstructUtils.convert(user, SysUserVo.class);
269
+ // ... 更新逻辑
251
270
  }
252
271
  }
253
272
  ```
254
273
 
255
- ---
256
-
257
- ## 5. 错误码设计
258
-
259
- ### 错误码规范
274
+ ### 6. 日志规范
260
275
 
261
276
  ```java
262
- // 格式: 模块(2位) + 类型(2位) + 序号(2位)
263
- // 模块: 10-系统, 20-用户, 30-订单, 40-商品
264
- // 类型: 01-参数错误, 02-业务错误, 03-权限错误, 04-系统错误
265
-
266
- public class ErrorCode {
267
- // 通用错误
268
- public static final int SUCCESS = 200;
269
- public static final int ERROR = 500;
270
- public static final int UNAUTHORIZED = 401;
271
- public static final int FORBIDDEN = 403;
272
-
273
- // 用户模块 20xxxx
274
- public static final int USER_NOT_FOUND = 200201; // 用户不存在
275
- public static final int USER_PASSWORD_ERROR = 200202; // 密码错误
276
- public static final int USER_DISABLED = 200203; // 用户已禁用
277
-
278
- // 订单模块 30xxxx
279
- public static final int ORDER_NOT_FOUND = 300201; // 订单不存在
280
- public static final int ORDER_STATUS_ERROR = 300202; // 订单状态错误
281
- }
282
-
283
- // 使用示例
284
- throw new ServiceException("用户不存在", ErrorCode.USER_NOT_FOUND);
285
- ```
286
-
287
- ### 错误消息国际化
288
-
289
- ```java
290
- import org.dromara.common.core.utils.MessageUtils;
291
-
292
- // 使用 MessageUtils.message() 获取国际化消息
293
- throw new ServiceException(MessageUtils.message("user.not.exists"));
294
-
295
- // 带参数的国际化消息
296
- throw new ServiceException(MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount));
297
- ```
298
-
299
- ---
300
-
301
- ## 6. 用户友好提示
302
-
303
- ### 错误提示规范
304
-
305
- ```java
306
- // ✅ 好的:用户友好提示
307
- throw new ServiceException("订单已发货,无法取消");
308
- throw new ServiceException("库存不足,请减少购买数量");
309
- throw new ServiceException("验证码已过期,请重新获取");
310
- throw new ServiceException("该用户名已被注册,请换一个试试");
311
-
312
- // ❌ 不好:技术术语
313
- throw new ServiceException("order.status.invalid");
314
- throw new ServiceException("NullPointerException at line 123");
315
- throw new ServiceException("数据库连接失败");
316
- throw new ServiceException("Duplicate entry for key 'uk_username'");
317
- ```
277
+ @Slf4j
278
+ @Service
279
+ public class OrderServiceImpl {
318
280
 
319
- ---
281
+ // 使用占位符(性能更好)
282
+ log.info("创建订单: orderNo={}, amount={}", dto.getOrderNo(), dto.getAmount());
320
283
 
321
- ## 7. 事务异常处理
284
+ // 异常日志带堆栈(第三个参数传异常对象)
285
+ log.error("处理失败: {}", e.getMessage(), e);
322
286
 
323
- ```java
324
- @Transactional(rollbackFor = Exception.class) // 所有异常都回滚
325
- public void batchOperation() {
326
- // 业务逻辑
287
+ // 事务方法:所有异常都回滚
288
+ @Transactional(rollbackFor = Exception.class)
289
+ public void createOrder(OrderCreateDTO dto) {
290
+ log.info("开始创建订单, orderNo={}", dto.getOrderNo());
291
+ // ... 业务逻辑
292
+ log.info("订单创建成功, id={}", order.getId());
293
+ }
327
294
  }
328
-
329
- // ✅ 好的:指定回滚异常类型
330
- @Transactional(rollbackFor = Exception.class)
331
-
332
- // ❌ 不好:使用默认(只回滚 RuntimeException)
333
- @Transactional
334
295
  ```
335
296
 
336
- ---
337
-
338
- ## 错误处理检查清单
339
-
340
- - [ ] 业务异常使用 `new ServiceException()`(不是 `ServiceException.of()`)
341
- - [ ] 条件检查使用 `if + ObjectUtil.isNull()` 判断
342
- - [ ] 参数校验使用 `@Validated(XxxGroup.class)`
343
- - [ ] 事务方法添加 `@Transactional(rollbackFor = Exception.class)`
344
- - [ ] 日志记录异常堆栈:`log.error("msg: {}", e.getMessage(), e)`
345
- - [ ] 日志使用占位符 `{}`,不使用字符串拼接
346
- - [ ] 敏感信息脱敏后再记录日志
347
- - [ ] 重要操作记录 INFO 日志
348
- - [ ] 错误提示使用用户友好语言
349
-
350
- ---
297
+ ## 常见错误
351
298
 
352
- ## 快速对照表
353
-
354
- | 错误写法 | 正确写法 |
355
- |-----------|-----------|
356
- | `throw ServiceException.of("msg")` | `throw new ServiceException("msg")` |
357
- | `ServiceException.throwIf(cond, "msg")` | `if (cond) { throw new ServiceException("msg"); }` |
358
- | `ServiceException.notNull(obj, "msg")` | `if (ObjectUtil.isNull(obj)) { throw new ServiceException("msg"); }` |
359
- | `log.error("失败: " + e.getMessage())` | `log.error("失败: {}", e.getMessage(), e)` |
360
- | `@Transactional` | `@Transactional(rollbackFor = Exception.class)` |
361
- | `throw new ServiceException("DB error")` | `throw new ServiceException("数据保存失败,请重试")` |
362
-
363
- ---
364
-
365
- ## 相关技能
366
-
367
- | 需要了解 | 激活 Skill |
368
- |---------|-----------|
369
- | Java 异常规范 | `java-exception` |
370
- | Service 层规范 | `java-service` |
371
- | Controller 层规范 | `java-controller` |
299
+ | 错误 | 正确做法 |
300
+ |------|---------|
301
+ | `RuntimeException("xxx")` | 使用自定义业务异常类 |
302
+ | Controller 里 try-catch 所有异常 | 交给 `@RestControllerAdvice` 统一处理 |
303
+ | 异常信息暴露 SQL / 堆栈 | 对用户返回友好提示,日志记录完整信息 |
304
+ | `javax.validation` | JDK 17+ 使用 `jakarta.validation` |
305
+ | 吞掉异常:`catch (Exception e) {}` | 至少记录日志 `log.error("...", e)` |
306
+ | 所有异常都返回 200 状态码 | 根据异常类型返回对应 HTTP 状态码 |
307
+ | `e.getMessage()` 直接返回给用户 | 第三方异常信息可能包含敏感信息,需要包装 |
308
+ | 校验逻辑写在 Controller | 用 `@Valid` + DTO 注解声明式校验 |
309
+ | 日志用字符串拼接 `"失败:" + msg` | 用占位符 `log.error("失败: {}", msg, e)` |
310
+ | `@Transactional` 不指定回滚 | 加 `rollbackFor = Exception.class` |