@llryiop/avatar-boot-cli 1.0.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 (77) hide show
  1. package/README.md +309 -0
  2. package/bin/cli.js +3 -0
  3. package/docs/plans/2026-03-12-avatar-boot-cli-design.md +73 -0
  4. package/docs/plans/2026-03-12-avatar-boot-cli-plan.md +681 -0
  5. package/package.json +28 -0
  6. package/src/index.js +78 -0
  7. package/src/prompts.js +78 -0
  8. package/src/template.js +37 -0
  9. package/src/transform.js +172 -0
  10. package/src/utils.js +34 -0
  11. package/templates/.claude/rules/architecture-redlines.md +146 -0
  12. package/templates/.claude/rules/code-review-standards.md +137 -0
  13. package/templates/.claude/rules/coding-standards.md +56 -0
  14. package/templates/.claude/rules/git-commit.md +59 -0
  15. package/templates/.claude/rules/layered-architecture.md +201 -0
  16. package/templates/.claude/rules/mybatis-plus.md +263 -0
  17. package/templates/.claude/rules/tech-stack.md +41 -0
  18. package/templates/.claude/rules/version.md +467 -0
  19. package/templates/.claude/settings.local.json +18 -0
  20. package/templates/.claude/skills/ai-tool-guide/SKILL.md +314 -0
  21. package/templates/.claude/skills/api-design/SKILL.md +200 -0
  22. package/templates/.claude/skills/api-doc-generator/SKILL.md +380 -0
  23. package/templates/.claude/skills/api-service-module-creator/SKILL.md +1114 -0
  24. package/templates/.claude/skills/avatar-boot-starter-feign/SKILL.md +243 -0
  25. package/templates/.claude/skills/avatar-boot-starter-job/SKILL.md +437 -0
  26. package/templates/.claude/skills/avatar-boot-starter-kafka/SKILL.md +580 -0
  27. package/templates/.claude/skills/avatar-boot-starter-mysql/SKILL.md +572 -0
  28. package/templates/.claude/skills/avatar-boot-starter-nacos/SKILL.md +901 -0
  29. package/templates/.claude/skills/avatar-boot-starter-oss/SKILL.md +594 -0
  30. package/templates/.claude/skills/avatar-boot-starter-redis/SKILL.md +586 -0
  31. package/templates/.claude/skills/avatar-boot-starter-rocketmq/SKILL.md +662 -0
  32. package/templates/.claude/skills/avatar-boot-starter-web/SKILL.md +1007 -0
  33. package/templates/.claude/skills/changelog-generator/SKILL.md +114 -0
  34. package/templates/.claude/skills/code-review/SKILL.md +239 -0
  35. package/templates/.claude/skills/crud-generator/SKILL.md +824 -0
  36. package/templates/.claude/skills/database-design/SKILL.md +377 -0
  37. package/templates/.claude/skills/deployment-config/SKILL.md +277 -0
  38. package/templates/.claude/skills/incident-analysis/SKILL.md +241 -0
  39. package/templates/.claude/skills/integration-test-generator/SKILL.md +496 -0
  40. package/templates/.claude/skills/prompt-engineering/SKILL.md +249 -0
  41. package/templates/.claude/skills/requirement-management/SKILL.md +244 -0
  42. package/templates/.claude/skills/security-audit/SKILL.md +330 -0
  43. package/templates/.claude/skills/test-case-design/SKILL.md +257 -0
  44. package/templates/.claude/skills/testing-workflow/SKILL.md +68 -0
  45. package/templates/.claude/skills/troubleshooting/SKILL.md +240 -0
  46. package/templates/CLAUDE.md +173 -0
  47. package/templates/README.md +303 -0
  48. package/templates/avatar-scaffold-api/pom.xml +41 -0
  49. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/api/LoginFeignClient.java +40 -0
  50. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/constant/LoginConstant.java +21 -0
  51. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/dto/request/LoginRequest.java +17 -0
  52. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/dto/request/RefreshTokenRequest.java +14 -0
  53. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/dto/response/LoginResponse.java +31 -0
  54. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/dto/response/TokenInfoResponse.java +25 -0
  55. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/enums/LoginTypeEnum.java +23 -0
  56. package/templates/avatar-scaffold-api/src/main/java/com/iflytek/avatar/login/exception/LoginException.java +23 -0
  57. package/templates/avatar-scaffold-service/k8s-app/Dockerfile +14 -0
  58. package/templates/avatar-scaffold-service/k8s-app/Dockerfile-arm64 +14 -0
  59. package/templates/avatar-scaffold-service/packaging/assembly.xml +16 -0
  60. package/templates/avatar-scaffold-service/pom.xml +150 -0
  61. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/Application.java +21 -0
  62. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/config/LoginConfig.java +20 -0
  63. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/controller/LoginController.java +37 -0
  64. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/converter/LoginConverter.java +54 -0
  65. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/feign/DemoFeign.java +21 -0
  66. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/repository/entity/UserLoginEntity.java +33 -0
  67. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/repository/entity/UserTokenEntity.java +39 -0
  68. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/repository/mapper/UserLoginMapper.java +20 -0
  69. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/service/LoginService.java +22 -0
  70. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/service/impl/LoginServiceImpl.java +43 -0
  71. package/templates/avatar-scaffold-service/src/main/java/com/iflytek/avatar/login/utils/LoginUtils.java +31 -0
  72. package/templates/avatar-scaffold-service/src/main/resources/application-dev.yaml +29 -0
  73. package/templates/avatar-scaffold-service/src/main/resources/application-local.yaml +61 -0
  74. package/templates/avatar-scaffold-service/src/main/resources/application-prod.yaml +28 -0
  75. package/templates/avatar-scaffold-service/src/main/resources/application-test.yaml +28 -0
  76. package/templates/avatar-scaffold-service/src/main/resources/application.yaml +12 -0
  77. package/templates/pom.xml +98 -0
@@ -0,0 +1,1007 @@
1
+ ---
2
+ name: avatar-boot-starter-web
3
+ description: 当使用 avatar-boot-starter-web 模块时使用此技能 - 为 Spring Boot 3.5.3 应用提供全面的 Web 开发基础设施,包括请求上下文管理、结构化日志、全局异常处理、指标集成。
4
+ ---
5
+
6
+ # Avatar Boot Starter Web 技能
7
+
8
+ ## 何时使用此技能
9
+
10
+ 当用户出现以下情况时调用此技能:
11
+
12
+ - 提到 "avatar-boot-starter-web"、"web starter" 或 "web 模块"
13
+ - 询问请求上下文、TraceId 或请求追踪
14
+ - 想要配置或排查 Avatar Boot 应用中的日志问题
15
+ - 询问全局异常处理或错误响应
16
+ - 需要为 Web 应用配置 CORS
17
+ - 想要检测或监控慢请求
18
+ - 询问客户端 IP 提取或代理处理
19
+ - 提到 `@LogInfo` 注解或方法级日志
20
+ - 想要集成 Micrometer 指标/监控
21
+ - 正在使用 Avatar Boot 创建新的 Web 应用
22
+ - 询问结构化日志或 JSON 日志格式
23
+ - 需要在业务逻辑中访问请求上下文
24
+ - 排查 TraceId 传播问题
25
+ - 询问异步场景下的 TraceID 传播(1.0.0-SNAPSHOT+)
26
+ - 使用 @Async、CompletableFuture 或自定义线程池(1.0.0-SNAPSHOT+)
27
+ - 想要自定义 TraceID 生成策略(1.0.0-SNAPSHOT+)
28
+ - 需要集成 Micrometer Tracing 或分布式追踪系统(1.0.0-SNAPSHOT+)
29
+
30
+ ## 模块概述
31
+
32
+ `avatar-boot-starter-web` 模块是一个 Spring Boot starter,为 Avatar Boot 应用提供生产就绪的 Web 开发基础设施。它遵循 Spring Boot 自动配置约定,无需任何配置即可开始使用。
33
+
34
+ **核心能力:**
35
+ - 自动初始化请求上下文并生成 TraceId
36
+ - 结构化日志并自动注入 TraceId
37
+ - 全局异常处理并返回标准化响应
38
+ - 使用 Micrometer 集成指标
39
+ - CORS 支持(默认禁用)
40
+ - 慢请求检测和监控
41
+ - 代理感知的客户端 IP 提取
42
+
43
+ ## 核心功能
44
+
45
+ ### 1. 请求上下文管理
46
+
47
+ **功能说明:**
48
+ - 为每个 HTTP 请求自动初始化 `AvatarContext`
49
+ - 从 `X-Trace-Id` 请求头生成或传播 TraceId
50
+ - 提取客户端 IP(代理感知,支持 X-Forwarded-For)
51
+ - 追踪请求耗时并检测慢请求
52
+ - 自动清理 ThreadLocal 以防止内存泄漏
53
+
54
+ **核心组件:**
55
+ - `RequestContextInterceptor` - 拦截所有请求(可配置路径)
56
+ - `AvatarContext` - 保存请求元数据(traceId、uri、method、clientIp、startTime)
57
+ - `ContextManager` - 基于 ThreadLocal 的上下文访问器
58
+
59
+ **使用示例:**
60
+ 引入pom依赖:
61
+ ```xml
62
+ <dependency>
63
+ <groupId>com.iflytek.avatar.boot</groupId>
64
+ <artifactId>avatar-boot-starter-web</artifactId>
65
+ <version>${avatar.boot.version}</version>
66
+ </dependency>
67
+ ```
68
+
69
+ ```java
70
+ import com.iflytek.avatar.boot.context.AvatarContext;
71
+ import com.iflytek.avatar.boot.context.ContextManager;
72
+
73
+ @RestController
74
+ public class UserController {
75
+
76
+ @GetMapping("/user/info")
77
+ public Result<UserInfo> getUserInfo() {
78
+ // 访问当前请求上下文
79
+ AvatarContext context = ContextManager.get();
80
+ String traceId = context.getTraceId();
81
+ String clientIp = context.getClientIp();
82
+
83
+ log.info("Processing request for user from IP: {}", clientIp);
84
+ return Result.success(userInfo);
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### 2. 结构化日志
90
+
91
+ **功能说明:**
92
+ - 自动将 TraceId 注入所有日志消息
93
+ - 使用 Logstash Logback Encoder 提供 JSON 日志格式
94
+ - 支持多种日志输出(控制台、文件、JSON 文件)
95
+ - 使用异步 appender 实现高性能
96
+ - 自动为慢请求添加 `[SLOW]` 前缀标记
97
+
98
+ **日志格式:**
99
+
100
+ 控制台/文件格式:
101
+ ```
102
+ 2026-03-03 17:04:15.123 [http-nio-8080-exec-1] [abc123def456] INFO c.i.a.b.controller.UserController - Request started: GET /user/info from 192.168.1.100
103
+ ```
104
+
105
+ JSON 格式:
106
+ ```json
107
+ {
108
+ "timestamp": "2026-03-03T17:04:15.123+08:00",
109
+ "level": "INFO",
110
+ "thread": "http-nio-8080-exec-1",
111
+ "logger": "com.iflytek.avatar.boot.controller.UserController",
112
+ "message": "Request started: GET /user/info from 192.168.1.100",
113
+ "traceId": "abc123def456",
114
+ "app": "avatar-app"
115
+ }
116
+ ```
117
+
118
+ **日志文件:**
119
+ - `logs/${spring.application.name}.log` - 所有日志
120
+ - `logs/${spring.application.name}-error.log` - 仅错误日志
121
+ - `logs/${spring.application.name}-json.log` - JSON 格式日志
122
+
123
+ **环境特定行为:**
124
+ - **dev/local**:控制台 + 文件输出
125
+ - **test**:控制台 + 文件 + JSON 输出
126
+ - **prod**:仅文件 + JSON 输出(无控制台)
127
+
128
+ ### 3. @LogInfo 注解
129
+
130
+ **功能说明:**
131
+ - 通过 AOP 提供方法级日志
132
+ - 记录请求参数和响应结果
133
+ - 追踪方法执行时间
134
+ - 集成指标记录
135
+ - 自动记录异常及堆栈信息
136
+
137
+ **注解参数:**
138
+ ```java
139
+ @LogInfo(
140
+ request = true, // 记录请求参数(默认:false)
141
+ response = true, // 记录响应结果(默认:false)
142
+ metrics = true, // 记录指标(默认:false)
143
+ costTime = 3000 // 慢方法阈值(毫秒)(默认:3000)
144
+ )
145
+ ```
146
+
147
+ **使用示例:**
148
+ ```java
149
+ @Service
150
+ public class UserService {
151
+
152
+ @LogInfo(request = true, response = true, metrics = true, costTime = 2000)
153
+ public UserInfo getUserById(Long userId) {
154
+ // 业务逻辑
155
+ return userInfo;
156
+ }
157
+ }
158
+ ```
159
+
160
+ **日志输出:**
161
+ ```
162
+ [UserService.getUserById] Status: SUCCESS, Time: 156ms
163
+ → Request: [123]
164
+ → Response: {"id":123,"name":"John Doe"}
165
+ ```
166
+
167
+ **慢方法警告:**
168
+ ```
169
+ [UserService.getUserById] Status: SUCCESS, Time: 2345ms (⚠️ threshold: 2000ms)
170
+ → Request: [123]
171
+ → Response: {"id":123,"name":"John Doe"}
172
+ ```
173
+
174
+ ### 4. 全局异常处理
175
+
176
+ **功能说明:**
177
+ - 捕获所有异常并返回标准化的 `Result<?>` 响应
178
+ - 处理业务异常、参数校验错误和系统错误
179
+ - 记录异常及请求上下文(URI、方法)
180
+ - 返回适当的 HTTP 状态码
181
+
182
+ **支持的异常类型:**
183
+ - `BusinessException` - 业务逻辑错误(HTTP 200 带错误码)
184
+ - `MethodArgumentNotValidException` - @RequestBody 校验(HTTP 400)
185
+ - `BindException` - @ModelAttribute 校验(HTTP 400)
186
+ - `ConstraintViolationException` - @RequestParam 校验(HTTP 400)
187
+ - `RuntimeException` - 运行时错误(HTTP 500)
188
+ - `Exception` - 所有其他异常(HTTP 500)
189
+
190
+ **使用示例:**
191
+ ```java
192
+ import com.iflytek.avatar.boot.exception.BusinessException;
193
+ import com.iflytek.avatar.boot.entity.response.ResultCode;
194
+
195
+ @Service
196
+ public class UserService {
197
+
198
+ public void updateUser(Long userId) {
199
+ if (userId == null) {
200
+ throw new BusinessException(ResultCode.PARAM_ERROR, "用户ID不能为空");
201
+ }
202
+ // 业务逻辑
203
+ }
204
+ }
205
+ ```
206
+
207
+ **响应格式:**
208
+ ```json
209
+ {
210
+ "code": 400,
211
+ "message": "用户ID不能为空",
212
+ "data": null,
213
+ "success": false
214
+ }
215
+ ```
216
+
217
+ ### 5. 指标集成
218
+
219
+ **功能说明:**
220
+ - 记录方法调用次数和执行时间
221
+ - 使用 Micrometer 进行指标收集
222
+ - 缓存指标实例以避免重复注册
223
+ - 支持成功/失败状态追踪
224
+
225
+ **记录的指标:**
226
+ - `method.invocation.count` - 计数器,标签:class、method、status
227
+ - `method.execution.time` - 计时器,标签:class、method、status
228
+
229
+ **使用方式:**
230
+ 使用 `@LogInfo(metrics = true)` 时会自动记录指标。
231
+
232
+ **手动记录:**
233
+ ```java
234
+ @Autowired
235
+ private MetricsRecorder metricsRecorder;
236
+
237
+ public void someMethod() {
238
+ long startTime = System.currentTimeMillis();
239
+ try {
240
+ // 业务逻辑
241
+ long costTime = System.currentTimeMillis() - startTime;
242
+ metricsRecorder.recordMethodMetrics("MyClass", "someMethod", costTime, null);
243
+ } catch (Exception e) {
244
+ long costTime = System.currentTimeMillis() - startTime;
245
+ metricsRecorder.recordMethodMetrics("MyClass", "someMethod", costTime, e);
246
+ throw e;
247
+ }
248
+ }
249
+ ```
250
+
251
+ ### 6. CORS 支持
252
+
253
+ **功能说明:**
254
+ - 提供可配置的跨域资源共享
255
+ - 出于安全考虑默认禁用
256
+ - 支持自定义源、方法、请求头和凭证
257
+
258
+ **配置示例:**
259
+ ```yaml
260
+ avatar:
261
+ web:
262
+ cors-enabled: true
263
+ cors:
264
+ allowed-origins:
265
+ - "https://example.com"
266
+ - "https://app.example.com"
267
+ allowed-methods:
268
+ - GET
269
+ - POST
270
+ - PUT
271
+ - DELETE
272
+ allowed-headers:
273
+ - "*"
274
+ exposed-headers:
275
+ - X-Trace-Id
276
+ allow-credentials: true
277
+ max-age: 3600
278
+ ```
279
+
280
+ ### 7. TraceID 异步支持与 Micrometer 集成
281
+
282
+ **版本要求:** Avatar Boot 1.0.0-SNAPSHOT+
283
+
284
+ **功能说明:**
285
+ - 使用 TransmittableThreadLocal 自动传播 TraceID 到异步线程
286
+ - 支持 @Async、CompletableFuture、自定义线程池
287
+ - 可插拔的 TraceID 生成器(UUID、Snowflake、自定义)
288
+ - 可选的 Micrometer Tracing 集成(支持 Zipkin、Jaeger)
289
+ - 向后兼容,无需修改现有代码
290
+
291
+ **核心改进:**
292
+
293
+ #### 7.1 异步场景自动支持
294
+
295
+ **问题:** 之前使用普通 ThreadLocal,无法在异步线程中传播 TraceID
296
+
297
+ **解决方案:**
298
+ - 使用阿里巴巴的 TransmittableThreadLocal (TTL)
299
+ - 自动配置 TaskDecorator 装饰异步任务
300
+ - 支持 @Async、CompletableFuture、自定义线程池
301
+
302
+ **使用示例:**
303
+
304
+ ```java
305
+ @Service
306
+ public class UserService {
307
+
308
+ @Async
309
+ public CompletableFuture<User> getUserAsync(String userId) {
310
+ // TraceID 自动传播到异步线程
311
+ String traceId = ContextManager.get().getTraceId();
312
+ log.info("Async thread TraceID: {}", traceId);
313
+
314
+ User user = userRepository.findById(userId);
315
+ return CompletableFuture.completedFuture(user);
316
+ }
317
+ }
318
+ ```
319
+
320
+ **日志输出:**
321
+ ```
322
+ [main] [abc123] Request started: GET /users/1
323
+ [avatar-async-1] [abc123] Async thread TraceID: abc123
324
+ [main] [abc123] Request completed: GET /users/1 - 150ms
325
+ ```
326
+
327
+ #### 7.2 可插拔的 TraceID 生成器
328
+
329
+ **默认实现:** 使用 UUID(去除连字符)
330
+
331
+ **自定义实现:**
332
+
333
+ ```java
334
+ @Component
335
+ public class SnowflakeTraceIdGenerator implements TraceIdGenerator {
336
+
337
+ private final Snowflake snowflake = IdUtil.getSnowflake(1, 1);
338
+
339
+ @Override
340
+ public String generate() {
341
+ return String.valueOf(snowflake.nextId());
342
+ }
343
+ }
344
+ ```
345
+
346
+ #### 7.3 Micrometer Tracing 集成(可选)
347
+
348
+ **启用方式:**
349
+
350
+ ```yaml
351
+ avatar:
352
+ web:
353
+ tracing:
354
+ micrometer-enabled: true
355
+
356
+ management:
357
+ tracing:
358
+ enabled: true
359
+ sampling:
360
+ probability: 1.0
361
+ ```
362
+
363
+ **收益:**
364
+ - 与 Spring Cloud 生态无缝集成
365
+ - 支持 Zipkin、Jaeger 等分布式追踪系统
366
+ - 自动传播 W3C Trace Context 和 B3 格式
367
+
368
+ **异步配置:**
369
+
370
+ ```yaml
371
+ avatar:
372
+ web:
373
+ async:
374
+ enabled: true # 是否启用异步支持(默认:true)
375
+ core-pool-size: 10 # 核心线程数
376
+ max-pool-size: 50 # 最大线程数
377
+ queue-capacity: 200 # 队列容量
378
+ thread-name-prefix: avatar-async-
379
+ ```
380
+
381
+ **追踪配置:**
382
+
383
+ ```yaml
384
+ avatar:
385
+ web:
386
+ tracing:
387
+ micrometer-enabled: false # 是否启用 Micrometer 集成(默认:false)
388
+ generator-type: uuid # TraceID 生成策略(uuid, snowflake, custom)
389
+ ```
390
+
391
+ **性能影响:**
392
+ - 内存开销:每个异步任务额外复制一份上下文(约 1KB)
393
+ - CPU 开销:上下文复制和恢复(< 1ms)
394
+ - 总体影响:< 5%(压测验证)
395
+
396
+ **依赖版本:**
397
+ - TransmittableThreadLocal: 2.14.5
398
+ - Micrometer Tracing: 1.4.3(可选)
399
+
400
+ ## 配置指南
401
+
402
+ ### 基础配置(最小化)
403
+
404
+ ```yaml
405
+ spring:
406
+ application:
407
+ name: my-app
408
+
409
+ # 所有功能默认启用,无需额外配置
410
+ ```
411
+
412
+ ### 高级配置(所有选项)
413
+
414
+ ```yaml
415
+ avatar:
416
+ web:
417
+ # 功能开关
418
+ interceptor-enabled: true # 启用请求拦截器(默认:true)
419
+ exception-handler-enabled: true # 启用全局异常处理器(默认:true)
420
+ cors-enabled: false # 启用 CORS(默认:false)
421
+
422
+ # 拦截器配置
423
+ interceptor:
424
+ trace-id-header: X-Trace-Id # TraceId 请求头名称(默认:X-Trace-Id)
425
+ include-paths: # 拦截路径(默认:/**)
426
+ - /**
427
+ exclude-paths: # 排除路径
428
+ - /error
429
+ - /actuator/**
430
+ - /favicon.ico
431
+ - /webjars/**
432
+ - /swagger-ui/**
433
+ - /v3/api-docs/**
434
+
435
+ # 日志配置
436
+ log:
437
+ request-enabled: true # 记录请求开始(默认:true)
438
+ response-enabled: true # 记录请求完成(默认:true)
439
+ slow-request-threshold: 3000 # 慢请求阈值(毫秒)(默认:3000)
440
+
441
+ # 异步配置(1.0.0-SNAPSHOT+)
442
+ async:
443
+ enabled: true # 启用异步支持(默认:true)
444
+ core-pool-size: 10 # 核心线程数(默认:10)
445
+ max-pool-size: 50 # 最大线程数(默认:50)
446
+ queue-capacity: 200 # 队列容量(默认:200)
447
+ thread-name-prefix: avatar-async- # 线程名前缀(默认:avatar-async-)
448
+
449
+ # 追踪配置(1.0.0-SNAPSHOT+)
450
+ tracing:
451
+ micrometer-enabled: false # 启用 Micrometer 集成(默认:false)
452
+ generator-type: uuid # TraceID 生成策略(uuid, snowflake, custom)
453
+
454
+ # CORS 配置(仅当 cors-enabled: true 时生效)
455
+ cors:
456
+ allowed-origins:
457
+ - "*"
458
+ allowed-methods:
459
+ - GET
460
+ - POST
461
+ - PUT
462
+ - DELETE
463
+ - OPTIONS
464
+ allowed-headers:
465
+ - "*"
466
+ exposed-headers:
467
+ - X-Trace-Id
468
+ allow-credentials: true
469
+ max-age: 3600
470
+
471
+ # 日志配置
472
+ logging:
473
+ level:
474
+ root: INFO
475
+ com.iflytek.avatar.boot: DEBUG
476
+ file:
477
+ path: logs
478
+ ```
479
+
480
+ ### 环境特定配置
481
+
482
+ **开发环境(dev):**
483
+ ```yaml
484
+ spring:
485
+ config:
486
+ activate:
487
+ on-profile: dev
488
+
489
+ avatar:
490
+ web:
491
+ log:
492
+ slow-request-threshold: 5000 # 开发环境放宽阈值
493
+
494
+ logging:
495
+ level:
496
+ root: INFO
497
+ com.iflytek.avatar.boot: DEBUG
498
+ ```
499
+
500
+ **生产环境(prod):**
501
+ ```yaml
502
+ spring:
503
+ config:
504
+ activate:
505
+ on-profile: prod
506
+
507
+ avatar:
508
+ web:
509
+ cors-enabled: false # 生产环境禁用 CORS
510
+ log:
511
+ slow-request-threshold: 2000 # 生产环境更严格的阈值
512
+
513
+ logging:
514
+ level:
515
+ root: INFO
516
+ com.iflytek.avatar.boot: INFO
517
+ file:
518
+ path: /var/log/app
519
+ ```
520
+
521
+ ## 常见使用场景
522
+
523
+ ### 场景 1:API 网关集成
524
+
525
+ 在 API 网关(Kong、Nginx)后构建服务时:
526
+
527
+ ```yaml
528
+ avatar:
529
+ web:
530
+ interceptor:
531
+ trace-id-header: X-Request-Id # 匹配网关的请求头名称
532
+ exclude-paths:
533
+ - /health
534
+ - /metrics
535
+ ```
536
+
537
+ ### 场景 2:微服务分布式追踪
538
+
539
+ ```java
540
+ @Service
541
+ public class OrderService {
542
+
543
+ @Autowired
544
+ private RestTemplate restTemplate;
545
+
546
+ public void createOrder(OrderRequest request) {
547
+ // TraceId 自动可用
548
+ AvatarContext context = ContextManager.get();
549
+ String traceId = context.getTraceId();
550
+
551
+ // 将 TraceId 传播到下游服务
552
+ HttpHeaders headers = new HttpHeaders();
553
+ headers.set("X-Trace-Id", traceId);
554
+
555
+ HttpEntity<OrderRequest> entity = new HttpEntity<>(request, headers);
556
+ restTemplate.postForObject("http://inventory-service/api/reserve", entity, Void.class);
557
+ }
558
+ }
559
+ ```
560
+
561
+ ### 场景 3:性能监控
562
+
563
+ ```java
564
+ @RestController
565
+ @RequestMapping("/api/reports")
566
+ public class ReportController {
567
+
568
+ @LogInfo(request = true, response = false, metrics = true, costTime = 5000)
569
+ @GetMapping("/sales")
570
+ public Result<SalesReport> generateSalesReport(@RequestParam String startDate,
571
+ @RequestParam String endDate) {
572
+ // 重计算
573
+ SalesReport report = reportService.generateReport(startDate, endDate);
574
+ return Result.success(report);
575
+ }
576
+ }
577
+ ```
578
+
579
+ ### 场景 4:自定义异常处理
580
+
581
+ ```java
582
+ @Component
583
+ public class CustomExceptionHandler extends GlobalExceptionHandler {
584
+
585
+ @ExceptionHandler(AuthenticationException.class)
586
+ @ResponseStatus(HttpStatus.UNAUTHORIZED)
587
+ public Result<?> handleAuthenticationException(AuthenticationException e) {
588
+ log.error("Authentication failed: {}", e.getMessage());
589
+ return Result.fail(401, "认证失败,请重新登录");
590
+ }
591
+ }
592
+ ```
593
+
594
+ ### 场景 5:@Async 异步方法(1.0.0-SNAPSHOT+)
595
+
596
+ **自动支持 TraceID 传播**:
597
+
598
+ ```java
599
+ @Service
600
+ public class NotificationService {
601
+
602
+ @Async
603
+ public void sendNotification(String userId, String message) {
604
+ // TraceID 自动传播到异步线程
605
+ String traceId = ContextManager.get().getTraceId();
606
+ log.info("Sending notification with TraceID: {}", traceId);
607
+ // ... 发送通知
608
+ }
609
+ }
610
+ ```
611
+
612
+ ### 场景 6:CompletableFuture 并行处理(1.0.0-SNAPSHOT+)
613
+
614
+ **自动支持 TraceID 传播**:
615
+
616
+ ```java
617
+ @Service
618
+ public class OrderService {
619
+
620
+ @Autowired
621
+ private Executor asyncExecutor;
622
+
623
+ public Order createOrder(OrderRequest request) {
624
+ String traceId = ContextManager.get().getTraceId();
625
+
626
+ CompletableFuture<Void> inventoryCheck = CompletableFuture.runAsync(() -> {
627
+ // TraceID 自动传播
628
+ log.info("Checking inventory with TraceID: {}", traceId);
629
+ inventoryService.checkStock(request.getProductId());
630
+ }, asyncExecutor);
631
+
632
+ CompletableFuture<Void> paymentProcess = CompletableFuture.runAsync(() -> {
633
+ // TraceID 自动传播
634
+ log.info("Processing payment with TraceID: {}", traceId);
635
+ paymentService.process(request.getPayment());
636
+ }, asyncExecutor);
637
+
638
+ CompletableFuture.allOf(inventoryCheck, paymentProcess).join();
639
+
640
+ return orderRepository.save(new Order(request));
641
+ }
642
+ }
643
+ ```
644
+
645
+ ### 场景 7:自定义线程池(1.0.0-SNAPSHOT+)
646
+
647
+ **需要使用 MdcTaskDecorator**:
648
+
649
+ ```java
650
+ @Configuration
651
+ public class CustomThreadPoolConfig {
652
+
653
+ @Autowired
654
+ private MdcTaskDecorator mdcTaskDecorator;
655
+
656
+ @Bean
657
+ public ThreadPoolTaskExecutor customExecutor() {
658
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
659
+ executor.setCorePoolSize(5);
660
+ executor.setMaxPoolSize(10);
661
+ executor.setQueueCapacity(100);
662
+ executor.setThreadNamePrefix("custom-");
663
+
664
+ // 应用 MDC 任务装饰器以传播 TraceID
665
+ executor.setTaskDecorator(mdcTaskDecorator);
666
+
667
+ executor.initialize();
668
+ return executor;
669
+ }
670
+ }
671
+ ```
672
+
673
+ ## 故障排查
674
+
675
+ ### 问题 1:日志中未显示 TraceId
676
+
677
+ **症状:**
678
+ - 日志中没有显示 TraceId(方括号内)
679
+ - JSON 日志中缺少 traceId 字段
680
+
681
+ **可能原因:**
682
+ 1. 请求拦截器被禁用
683
+ 2. 请求路径被排除
684
+ 3. Logback 配置不正确
685
+
686
+ **解决方案:**
687
+ ```yaml
688
+ # 1. 确保拦截器已启用
689
+ avatar:
690
+ web:
691
+ interceptor-enabled: true
692
+
693
+ # 2. 检查排除路径
694
+ avatar:
695
+ web:
696
+ interceptor:
697
+ exclude-paths:
698
+ - /actuator/** # 确保你的路径没有被排除
699
+
700
+ # 3. 验证 logback-spring.xml 包含 TraceId 模式
701
+ # 模式应包含:[%X{traceId}]
702
+ ```
703
+
704
+ ### 问题 2:CORS 错误
705
+
706
+ **症状:**
707
+ - 浏览器显示 CORS 策略错误
708
+ - 预检 OPTIONS 请求失败
709
+
710
+ **解决方案:**
711
+ ```yaml
712
+ # 显式启用 CORS
713
+ avatar:
714
+ web:
715
+ cors-enabled: true
716
+ cors:
717
+ allowed-origins:
718
+ - "https://your-frontend-domain.com" # 使用凭证时不要用 "*"
719
+ allow-credentials: true
720
+ ```
721
+
722
+ **重要提示:** 如果 `allow-credentials: true`,则不能使用 `allowed-origins: ["*"]`。必须指定确切的源。
723
+
724
+ ### 问题 3:慢请求误报
725
+
726
+ **症状:**
727
+ - 许多请求被标记为 [SLOW],但实际上并不慢
728
+ - 阈值对你的使用场景来说太低
729
+
730
+ **解决方案:**
731
+ ```yaml
732
+ # 根据环境调整阈值
733
+ avatar:
734
+ web:
735
+ log:
736
+ slow-request-threshold: 5000 # 增加到 5 秒
737
+
738
+ # 或禁用慢请求日志
739
+ avatar:
740
+ web:
741
+ log:
742
+ response-enabled: false
743
+ ```
744
+
745
+ ### 问题 4:ThreadLocal 内存泄漏
746
+
747
+ **症状:**
748
+ - 内存使用随时间增长
749
+ - 生产环境出现 OutOfMemoryError
750
+
751
+ **诊断:**
752
+ 拦截器会在 `afterCompletion()` 中自动清理 ThreadLocal。如果出现内存泄漏:
753
+
754
+ 1. 检查是否手动设置上下文但未清理:
755
+ ```java
756
+ // ❌ 错误 - 手动设置但未清理
757
+ ContextManager.set(context);
758
+ // ... 业务逻辑
759
+ // 缺失:ContextManager.clear();
760
+
761
+ // ✅ 正确 - 让拦截器处理
762
+ AvatarContext context = ContextManager.get();
763
+ ```
764
+
765
+ 2. 验证拦截器已注册:
766
+ ```yaml
767
+ avatar:
768
+ web:
769
+ interceptor-enabled: true # 必须为 true
770
+ ```
771
+
772
+ ### 问题 5:客户端 IP 显示为代理 IP
773
+
774
+ **症状:**
775
+ - `context.getClientIp()` 返回代理/负载均衡器 IP
776
+ - 未捕获真实客户端 IP
777
+
778
+ **解决方案:**
779
+ 确保你的代理/负载均衡器设置了正确的请求头:
780
+
781
+ ```nginx
782
+ # Nginx 配置
783
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
784
+ proxy_set_header X-Real-IP $remote_addr;
785
+ ```
786
+
787
+ 拦截器按以下顺序检查请求头:
788
+ 1. `X-Forwarded-For`(如果有多个则取第一个 IP)
789
+ 2. `X-Real-IP`
790
+ 3. `Proxy-Client-IP`
791
+ 4. `WL-Proxy-Client-IP`
792
+ 5. `request.getRemoteAddr()`
793
+
794
+ ### 问题 6:@LogInfo 不工作
795
+
796
+ **症状:**
797
+ - 使用 @LogInfo 注解的方法没有日志
798
+ - 指标未记录
799
+
800
+ **可能原因:**
801
+ 1. AOP 未启用
802
+ 2. 方法未通过 Spring 代理调用
803
+ 3. LogInfoAspect 未注册
804
+
805
+ **解决方案:**
806
+ ```java
807
+ // ❌ 错误 - 直接调用方法会绕过 AOP
808
+ @Service
809
+ public class UserService {
810
+ @LogInfo
811
+ public void method1() { }
812
+
813
+ public void method2() {
814
+ this.method1(); // 直接调用,AOP 不会拦截
815
+ }
816
+ }
817
+
818
+ // ✅ 正确 - 通过 Spring 代理调用
819
+ @Service
820
+ @RequiredArgsConstructor
821
+ public class UserService {
822
+ private final UserService self; // 注入自身
823
+
824
+ @LogInfo
825
+ public void method1() { }
826
+
827
+ public void method2() {
828
+ self.method1(); // 代理调用,AOP 生效
829
+ }
830
+ }
831
+ ```
832
+
833
+ ### 问题 7:异步线程中 TraceID 为 null(1.0.0-SNAPSHOT+)
834
+
835
+ **症状:**
836
+ - 异步方法中 `ContextManager.get().getTraceId()` 返回 null
837
+ - 异步线程的日志中没有 TraceID
838
+
839
+ **可能原因:**
840
+ 1. 使用了自定义线程池但未应用 MdcTaskDecorator
841
+ 2. 异步支持被禁用
842
+ 3. 使用了不受管理的线程(如 new Thread())
843
+
844
+ **解决方案:**
845
+
846
+ **方案 1:使用配置的异步执行器**
847
+ ```java
848
+ @Service
849
+ public class MyService {
850
+ @Autowired
851
+ private Executor asyncExecutor; // 使用 Avatar Boot 配置的执行器
852
+
853
+ public void doWork() {
854
+ CompletableFuture.runAsync(() -> {
855
+ // TraceID 自动传播
856
+ String traceId = ContextManager.get().getTraceId();
857
+ }, asyncExecutor);
858
+ }
859
+ }
860
+ ```
861
+
862
+ **方案 2:自定义线程池应用 MdcTaskDecorator**
863
+ ```java
864
+ @Configuration
865
+ public class ThreadPoolConfig {
866
+ @Autowired
867
+ private MdcTaskDecorator mdcTaskDecorator;
868
+
869
+ @Bean
870
+ public ThreadPoolTaskExecutor customExecutor() {
871
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
872
+ executor.setTaskDecorator(mdcTaskDecorator); // 关键:应用装饰器
873
+ executor.initialize();
874
+ return executor;
875
+ }
876
+ }
877
+ ```
878
+
879
+ **方案 3:检查异步支持是否启用**
880
+ ```yaml
881
+ avatar:
882
+ web:
883
+ async:
884
+ enabled: true # 确保为 true
885
+ ```
886
+
887
+ ### 问题 8:如何验证异步 TraceID 传播是否生效(1.0.0-SNAPSHOT+)
888
+
889
+ **验证方法:**
890
+
891
+ ```java
892
+ @RestController
893
+ public class TestController {
894
+
895
+ @Autowired
896
+ private Executor asyncExecutor;
897
+
898
+ @GetMapping("/test-async")
899
+ public String testAsync() {
900
+ String mainTraceId = ContextManager.get().getTraceId();
901
+ log.info("Main thread TraceID: {}", mainTraceId);
902
+
903
+ CompletableFuture.runAsync(() -> {
904
+ String asyncTraceId = ContextManager.get().getTraceId();
905
+ log.info("Async thread TraceID: {}", asyncTraceId);
906
+ }, asyncExecutor).join();
907
+
908
+ return "Check logs";
909
+ }
910
+ }
911
+ ```
912
+
913
+ **预期日志输出:**
914
+ ```
915
+ [http-nio-8080-exec-1] [abc123] Main thread TraceID: abc123
916
+ [avatar-async-1] [abc123] Async thread TraceID: abc123
917
+ ```
918
+
919
+ 如果异步线程的 TraceID 为 null 或不一致,说明传播未生效。
920
+
921
+ ## 组件参考
922
+
923
+ ### 核心类
924
+
925
+ | 类 | 包 | 用途 |
926
+ |-------|---------|---------|
927
+ | `WebAutoConfiguration` | `com.iflytek.avatar.boot.config` | 主自动配置类 |
928
+ | `WebProperties` | `com.iflytek.avatar.boot.config` | 配置属性 |
929
+ | `WebMvcConfiguration` | `com.iflytek.avatar.boot.config` | MVC 配置(拦截器、CORS) |
930
+ | `MetricsConfiguration` | `com.iflytek.avatar.boot.config` | 指标记录器配置 |
931
+ | `RequestContextInterceptor` | `com.iflytek.avatar.boot.interceptor` | 请求上下文初始化 |
932
+ | `GlobalExceptionHandler` | `com.iflytek.avatar.boot.exception` | 全局异常处理 |
933
+ | `LogInfo` | `com.iflytek.avatar.boot.anno` | 方法级日志注解 |
934
+ | `LogInfoAspect` | `com.iflytek.avatar.boot.anno` | @LogInfo 的 AOP 切面 |
935
+ | `MetricsRecorder` | `com.iflytek.avatar.boot.metrics` | 指标记录工具 |
936
+ | `AvatarContext` | `com.iflytek.avatar.boot.context` | 请求上下文持有者(在 core 中) |
937
+ | `ContextManager` | `com.iflytek.avatar.boot.context` | ThreadLocal 上下文管理器(在 core 中) |
938
+
939
+ ### 配置属性
940
+
941
+ | 属性 | 类型 | 默认值 | 说明 |
942
+ |----------|------|---------|-------------|
943
+ | `avatar.web.interceptor-enabled` | boolean | true | 启用请求拦截器 |
944
+ | `avatar.web.exception-handler-enabled` | boolean | true | 启用全局异常处理器 |
945
+ | `avatar.web.cors-enabled` | boolean | false | 启用 CORS |
946
+ | `avatar.web.interceptor.trace-id-header` | String | X-Trace-Id | TraceId 请求头名称 |
947
+ | `avatar.web.interceptor.include-paths` | List<String> | ["/**"] | 拦截路径 |
948
+ | `avatar.web.interceptor.exclude-paths` | List<String> | ["/error", "/actuator/**", ...] | 排除路径 |
949
+ | `avatar.web.log.request-enabled` | boolean | true | 记录请求开始 |
950
+ | `avatar.web.log.response-enabled` | boolean | true | 记录请求完成 |
951
+ | `avatar.web.log.slow-request-threshold` | long | 3000 | 慢请求阈值(毫秒) |
952
+ | `avatar.web.async.enabled` | boolean | true | 启用异步支持(1.0.0-SNAPSHOT+) |
953
+ | `avatar.web.async.core-pool-size` | int | 10 | 异步执行器核心线程数(1.0.0-SNAPSHOT+) |
954
+ | `avatar.web.async.max-pool-size` | int | 50 | 异步执行器最大线程数(1.0.0-SNAPSHOT+) |
955
+ | `avatar.web.async.queue-capacity` | int | 200 | 异步执行器队列容量(1.0.0-SNAPSHOT+) |
956
+ | `avatar.web.async.thread-name-prefix` | String | avatar-async- | 异步线程名前缀(1.0.0-SNAPSHOT+) |
957
+ | `avatar.web.tracing.micrometer-enabled` | boolean | false | 启用 Micrometer 集成(1.0.0-SNAPSHOT+) |
958
+ | `avatar.web.tracing.generator-type` | String | uuid | TraceID 生成策略(1.0.0-SNAPSHOT+) |
959
+ | `avatar.web.cors.allowed-origins` | List<String> | ["*"] | 允许的源 |
960
+ | `avatar.web.cors.allowed-methods` | List<String> | ["GET", "POST", ...] | 允许的 HTTP 方法 |
961
+ | `avatar.web.cors.allowed-headers` | List<String> | ["*"] | 允许的请求头 |
962
+ | `avatar.web.cors.exposed-headers` | List<String> | ["X-Trace-Id"] | 暴露的响应头 |
963
+ | `avatar.web.cors.allow-credentials` | boolean | true | 允许凭证 |
964
+ | `avatar.web.cors.max-age` | long | 3600 | 预检缓存时间(秒) |
965
+
966
+ ## 依赖
967
+
968
+ 此模块依赖于:
969
+
970
+ - `avatar-boot-core` - 核心功能(AvatarContext、Result 等)
971
+ - `spring-boot-starter-web` - Spring MVC
972
+ - `spring-boot-starter-validation` - 参数校验
973
+ - `spring-boot-starter-aop` - @LogInfo 的 AOP 支持
974
+ - `micrometer-core` - 指标(可选)
975
+ - `logstash-logback-encoder` - JSON 日志格式化
976
+ - `hutool-all` - 工具库(UUID、字符串处理)
977
+
978
+ ## 最佳实践
979
+
980
+ 1. **始终使用 Result<T> 作为 API 响应** - 确保响应格式一致
981
+ 2. **生产环境不要禁用拦截器** - TraceId 对故障排查至关重要
982
+ 3. **谨慎使用 @LogInfo** - 仅在关键业务方法上使用,避免日志膨胀
983
+ 4. **根据环境配置慢请求阈值** - 开发环境可以放宽,生产环境应严格
984
+ 5. **生产环境永远不要使用 CORS "*" 配合凭证** - 安全风险
985
+ 6. **始终将 TraceId 传播到下游服务** - 启用分布式追踪
986
+ 7. **使用环境特定的日志级别** - 开发环境用 DEBUG,生产环境用 INFO
987
+ 8. **在生产环境监控指标** - 为高失败率或慢方法设置告警
988
+ 9. **不要捕获并吞掉异常** - 让 GlobalExceptionHandler 处理它们
989
+ 10. **在 finally 块中清理资源** - 尽管拦截器会处理上下文清理
990
+
991
+ ## 版本要求
992
+
993
+ - Java 21+
994
+ - Spring Boot 3.5.3+
995
+ - Maven 3.8.6+
996
+
997
+ ## 相关技能
998
+
999
+ - `avatar-boot-sdk-development` - 用于整体 Avatar Boot 架构和依赖管理
1000
+ - `java-spring-boot` - 用于通用 Spring Boot 开发模式
1001
+ - `systematic-debugging` - 用于排查此模块的问题
1002
+
1003
+ ## 其他资源
1004
+
1005
+ - 模块 README:`avatar-boot-starter-web/README.md`
1006
+ - 配置示例:`avatar-boot-starter-web/src/main/resources/application-example.yml`
1007
+ - Logback 配置:`avatar-boot-starter-web/src/main/resources/logback-spring.xml`