ai-engineering-init 1.6.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 (187) hide show
  1. package/.claude/agents/code-reviewer.md +3 -130
  2. package/.claude/hooks/skill-forced-eval.js +46 -60
  3. package/.claude/hooks/stop.js +24 -1
  4. package/.claude/settings.json +10 -1
  5. package/.claude/skills/api-development/SKILL.md +179 -130
  6. package/.claude/skills/architecture-design/SKILL.md +102 -212
  7. package/.claude/skills/backend-annotations/SKILL.md +166 -220
  8. package/.claude/skills/bug-detective/SKILL.md +225 -186
  9. package/.claude/skills/code-patterns/SKILL.md +127 -244
  10. package/.claude/skills/codex-code-review/SKILL.md +327 -0
  11. package/.claude/skills/collaborating-with-codex/SKILL.md +96 -113
  12. package/.claude/skills/crud-development/SKILL.md +226 -307
  13. package/.claude/skills/data-permission/SKILL.md +131 -202
  14. package/.claude/skills/database-ops/SKILL.md +158 -355
  15. package/.claude/skills/error-handler/SKILL.md +224 -285
  16. package/.claude/skills/file-oss-management/SKILL.md +174 -169
  17. package/.claude/skills/git-workflow/SKILL.md +123 -341
  18. package/.claude/skills/json-serialization/SKILL.md +121 -137
  19. package/.claude/skills/leniu-report-customization/SKILL.md +82 -2
  20. package/.claude/skills/leniu-report-standard-customization/SKILL.md +65 -2
  21. package/.claude/skills/loki-log-query/SKILL.md +400 -0
  22. package/.claude/skills/mysql-debug/SKILL.md +58 -22
  23. package/.claude/skills/performance-doctor/SKILL.md +83 -89
  24. package/.claude/skills/redis-cache/SKILL.md +134 -185
  25. package/.claude/skills/scheduled-jobs/SKILL.md +187 -224
  26. package/.claude/skills/security-guard/SKILL.md +168 -276
  27. package/.claude/skills/sms-mail/SKILL.md +266 -228
  28. package/.claude/skills/social-login/SKILL.md +257 -195
  29. package/.claude/skills/sync-back-merge/SKILL.md +66 -0
  30. package/.claude/skills/tenant-management/SKILL.md +172 -188
  31. package/.claude/skills/utils-toolkit/SKILL.md +214 -222
  32. package/.claude/skills/websocket-sse/SKILL.md +251 -172
  33. package/.claude/skills/workflow-engine/SKILL.md +178 -250
  34. package/.claude/skills/yunxiao-task-management/SKILL.md +489 -0
  35. package/.codex/skills/api-development/SKILL.md +179 -130
  36. package/.codex/skills/architecture-design/SKILL.md +102 -212
  37. package/.codex/skills/backend-annotations/SKILL.md +166 -220
  38. package/.codex/skills/bug-detective/SKILL.md +225 -186
  39. package/.codex/skills/code-patterns/SKILL.md +127 -244
  40. package/.codex/skills/collaborating-with-codex/SKILL.md +96 -113
  41. package/.codex/skills/crud-development/SKILL.md +226 -307
  42. package/.codex/skills/data-permission/SKILL.md +131 -202
  43. package/.codex/skills/database-ops/SKILL.md +158 -355
  44. package/.codex/skills/error-handler/SKILL.md +224 -285
  45. package/.codex/skills/file-oss-management/SKILL.md +174 -169
  46. package/.codex/skills/git-workflow/SKILL.md +123 -341
  47. package/.codex/skills/json-serialization/SKILL.md +121 -137
  48. package/.codex/skills/leniu-report-customization/SKILL.md +82 -2
  49. package/.codex/skills/leniu-report-standard-customization/SKILL.md +65 -2
  50. package/.codex/skills/loki-log-query/SKILL.md +400 -0
  51. package/.codex/skills/loki-log-query/environments.json +45 -0
  52. package/.codex/skills/mysql-debug/SKILL.md +58 -22
  53. package/.codex/skills/performance-doctor/SKILL.md +83 -89
  54. package/.codex/skills/redis-cache/SKILL.md +134 -185
  55. package/.codex/skills/scheduled-jobs/SKILL.md +187 -224
  56. package/.codex/skills/security-guard/SKILL.md +168 -276
  57. package/.codex/skills/skill-creator/LICENSE.txt +202 -0
  58. package/.codex/skills/skill-creator/SKILL.md +479 -0
  59. package/.codex/skills/skill-creator/agents/analyzer.md +274 -0
  60. package/.codex/skills/skill-creator/agents/comparator.md +202 -0
  61. package/.codex/skills/skill-creator/agents/grader.md +223 -0
  62. package/.codex/skills/skill-creator/assets/eval_review.html +146 -0
  63. package/.codex/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  64. package/.codex/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  65. package/.codex/skills/skill-creator/references/schemas.md +430 -0
  66. package/.codex/skills/skill-creator/scripts/__init__.py +0 -0
  67. package/.codex/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  68. package/.codex/skills/skill-creator/scripts/generate_report.py +326 -0
  69. package/.codex/skills/skill-creator/scripts/improve_description.py +248 -0
  70. package/.codex/skills/skill-creator/scripts/package_skill.py +136 -0
  71. package/.codex/skills/skill-creator/scripts/quick_validate.py +103 -0
  72. package/.codex/skills/skill-creator/scripts/run_eval.py +310 -0
  73. package/.codex/skills/skill-creator/scripts/run_loop.py +332 -0
  74. package/.codex/skills/skill-creator/scripts/utils.py +47 -0
  75. package/.codex/skills/sms-mail/SKILL.md +266 -228
  76. package/.codex/skills/social-login/SKILL.md +257 -195
  77. package/.codex/skills/sync-back-merge/SKILL.md +66 -0
  78. package/.codex/skills/tenant-management/SKILL.md +172 -188
  79. package/.codex/skills/utils-toolkit/SKILL.md +214 -222
  80. package/.codex/skills/websocket-sse/SKILL.md +251 -172
  81. package/.codex/skills/workflow-engine/SKILL.md +178 -250
  82. package/.codex/skills/yunxiao-task-management/SKILL.md +489 -0
  83. package/.cursor/hooks/cursor-skill-eval.js +66 -6
  84. package/.cursor/hooks/stop.js +23 -1
  85. package/.cursor/skills/api-development/SKILL.md +179 -130
  86. package/.cursor/skills/architecture-design/SKILL.md +102 -212
  87. package/.cursor/skills/backend-annotations/SKILL.md +166 -220
  88. package/.cursor/skills/bug-detective/SKILL.md +225 -186
  89. package/.cursor/skills/code-patterns/SKILL.md +127 -244
  90. package/.cursor/skills/collaborating-with-codex/SKILL.md +96 -113
  91. package/.cursor/skills/crud-development/SKILL.md +226 -307
  92. package/.cursor/skills/data-permission/SKILL.md +131 -202
  93. package/.cursor/skills/database-ops/SKILL.md +158 -355
  94. package/.cursor/skills/error-handler/SKILL.md +224 -285
  95. package/.cursor/skills/file-oss-management/SKILL.md +174 -169
  96. package/.cursor/skills/git-workflow/SKILL.md +123 -341
  97. package/.cursor/skills/json-serialization/SKILL.md +121 -137
  98. package/.cursor/skills/leniu-report-customization/SKILL.md +82 -2
  99. package/.cursor/skills/leniu-report-standard-customization/SKILL.md +65 -2
  100. package/.cursor/skills/loki-log-query/SKILL.md +400 -0
  101. package/.cursor/skills/loki-log-query/environments.json +45 -0
  102. package/.cursor/skills/mysql-debug/SKILL.md +58 -22
  103. package/.cursor/skills/performance-doctor/SKILL.md +83 -89
  104. package/.cursor/skills/redis-cache/SKILL.md +134 -185
  105. package/.cursor/skills/scheduled-jobs/SKILL.md +187 -224
  106. package/.cursor/skills/security-guard/SKILL.md +168 -276
  107. package/.cursor/skills/skill-creator/LICENSE.txt +202 -0
  108. package/.cursor/skills/skill-creator/SKILL.md +479 -0
  109. package/.cursor/skills/skill-creator/agents/analyzer.md +274 -0
  110. package/.cursor/skills/skill-creator/agents/comparator.md +202 -0
  111. package/.cursor/skills/skill-creator/agents/grader.md +223 -0
  112. package/.cursor/skills/skill-creator/assets/eval_review.html +146 -0
  113. package/.cursor/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  114. package/.cursor/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  115. package/.cursor/skills/skill-creator/references/schemas.md +430 -0
  116. package/.cursor/skills/skill-creator/scripts/__init__.py +0 -0
  117. package/.cursor/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  118. package/.cursor/skills/skill-creator/scripts/generate_report.py +326 -0
  119. package/.cursor/skills/skill-creator/scripts/improve_description.py +248 -0
  120. package/.cursor/skills/skill-creator/scripts/package_skill.py +136 -0
  121. package/.cursor/skills/skill-creator/scripts/quick_validate.py +103 -0
  122. package/.cursor/skills/skill-creator/scripts/run_eval.py +310 -0
  123. package/.cursor/skills/skill-creator/scripts/run_loop.py +332 -0
  124. package/.cursor/skills/skill-creator/scripts/utils.py +47 -0
  125. package/.cursor/skills/sms-mail/SKILL.md +266 -228
  126. package/.cursor/skills/social-login/SKILL.md +257 -195
  127. package/.cursor/skills/sync-back-merge/SKILL.md +66 -0
  128. package/.cursor/skills/tenant-management/SKILL.md +172 -188
  129. package/.cursor/skills/utils-toolkit/SKILL.md +214 -222
  130. package/.cursor/skills/websocket-sse/SKILL.md +251 -172
  131. package/.cursor/skills/workflow-engine/SKILL.md +178 -250
  132. package/.cursor/skills/yunxiao-task-management/SKILL.md +489 -0
  133. package/AGENTS.md +49 -540
  134. package/CLAUDE.md +73 -119
  135. package/README.md +37 -6
  136. package/bin/index.js +611 -25
  137. package/package.json +1 -1
  138. package/src/platform-map.json +4 -0
  139. package/src/skills/api-development/SKILL.md +179 -130
  140. package/src/skills/architecture-design/SKILL.md +102 -212
  141. package/src/skills/backend-annotations/SKILL.md +166 -220
  142. package/src/skills/bug-detective/SKILL.md +225 -186
  143. package/src/skills/code-patterns/SKILL.md +127 -244
  144. package/src/skills/codex-code-review/SKILL.md +261 -69
  145. package/src/skills/collaborating-with-codex/SKILL.md +96 -113
  146. package/src/skills/crud-development/SKILL.md +226 -307
  147. package/src/skills/data-permission/SKILL.md +131 -202
  148. package/src/skills/database-ops/SKILL.md +158 -355
  149. package/src/skills/error-handler/SKILL.md +224 -285
  150. package/src/skills/file-oss-management/SKILL.md +174 -169
  151. package/src/skills/git-workflow/SKILL.md +123 -341
  152. package/src/skills/json-serialization/SKILL.md +121 -137
  153. package/src/skills/leniu-report-customization/SKILL.md +82 -2
  154. package/src/skills/leniu-report-standard-customization/SKILL.md +65 -2
  155. package/src/skills/loki-log-query/SKILL.md +400 -0
  156. package/src/skills/loki-log-query/environments.json +45 -0
  157. package/src/skills/mysql-debug/SKILL.md +58 -22
  158. package/src/skills/performance-doctor/SKILL.md +83 -89
  159. package/src/skills/redis-cache/SKILL.md +134 -185
  160. package/src/skills/scheduled-jobs/SKILL.md +187 -224
  161. package/src/skills/security-guard/SKILL.md +168 -276
  162. package/src/skills/skill-creator/LICENSE.txt +202 -0
  163. package/src/skills/skill-creator/SKILL.md +479 -0
  164. package/src/skills/skill-creator/agents/analyzer.md +274 -0
  165. package/src/skills/skill-creator/agents/comparator.md +202 -0
  166. package/src/skills/skill-creator/agents/grader.md +223 -0
  167. package/src/skills/skill-creator/assets/eval_review.html +146 -0
  168. package/src/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  169. package/src/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  170. package/src/skills/skill-creator/references/schemas.md +430 -0
  171. package/src/skills/skill-creator/scripts/__init__.py +0 -0
  172. package/src/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  173. package/src/skills/skill-creator/scripts/generate_report.py +326 -0
  174. package/src/skills/skill-creator/scripts/improve_description.py +248 -0
  175. package/src/skills/skill-creator/scripts/package_skill.py +136 -0
  176. package/src/skills/skill-creator/scripts/quick_validate.py +103 -0
  177. package/src/skills/skill-creator/scripts/run_eval.py +310 -0
  178. package/src/skills/skill-creator/scripts/run_loop.py +332 -0
  179. package/src/skills/skill-creator/scripts/utils.py +47 -0
  180. package/src/skills/sms-mail/SKILL.md +266 -228
  181. package/src/skills/social-login/SKILL.md +257 -195
  182. package/src/skills/sync-back-merge/SKILL.md +66 -0
  183. package/src/skills/tenant-management/SKILL.md +172 -188
  184. package/src/skills/utils-toolkit/SKILL.md +214 -222
  185. package/src/skills/websocket-sse/SKILL.md +251 -172
  186. package/src/skills/workflow-engine/SKILL.md +178 -250
  187. package/src/skills/yunxiao-task-management/SKILL.md +489 -0
@@ -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` |