@llryiop/avatar-boot-cli 1.0.1 → 1.0.2

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 (66) hide show
  1. package/docs/exam-question-generate-api.md +163 -0
  2. package/package.json +1 -1
  3. package/src/prompts.js +3 -3
  4. package/src/transform.js +1 -1
  5. package/templates/.claude/skills/avatar-boot-starter-feign/README.md +243 -0
  6. package/templates/.claude/skills/avatar-boot-starter-feign/SKILL.md +47 -219
  7. package/templates/.claude/skills/avatar-boot-starter-feign/references//345/212/237/350/203/275/350/257/246/350/247/243.md +65 -0
  8. package/templates/.claude/skills/avatar-boot-starter-feign/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +75 -0
  9. package/templates/.claude/skills/avatar-boot-starter-feign/references//351/205/215/347/275/256/345/217/202/350/200/203.md +70 -0
  10. package/templates/.claude/skills/avatar-boot-starter-job/README.md +437 -0
  11. package/templates/.claude/skills/avatar-boot-starter-job/SKILL.md +35 -414
  12. package/templates/.claude/skills/avatar-boot-starter-job/references//345/270/270/350/247/201/351/227/256/351/242/230.md +55 -0
  13. package/templates/.claude/skills/avatar-boot-starter-job/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +124 -0
  14. package/templates/.claude/skills/avatar-boot-starter-job/references//347/233/221/346/216/247/346/214/207/346/240/207.md +72 -0
  15. package/templates/.claude/skills/avatar-boot-starter-kafka/README.md +580 -0
  16. package/templates/.claude/skills/avatar-boot-starter-kafka/SKILL.md +36 -560
  17. package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/234/200/344/275/263/345/256/236/350/267/265.md +43 -0
  18. package/templates/.claude/skills/avatar-boot-starter-kafka/references//346/240/270/345/277/203/345/212/237/350/203/275.md +117 -0
  19. package/templates/.claude/skills/avatar-boot-starter-kafka/references//351/205/215/347/275/256/345/217/202/350/200/203.md +54 -0
  20. package/templates/.claude/skills/avatar-boot-starter-mysql/README.md +572 -0
  21. package/templates/.claude/skills/avatar-boot-starter-mysql/SKILL.md +40 -550
  22. package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/256/236/344/275/223/344/270/216/345/212/237/350/203/275.md +96 -0
  23. package/templates/.claude/skills/avatar-boot-starter-mysql/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/346/225/260/346/215/256/346/272/220.md +91 -0
  24. package/templates/.claude/skills/avatar-boot-starter-mysql/references//351/253/230/347/272/247/347/211/271/346/200/247/344/270/216/351/205/215/347/275/256.md +59 -0
  25. package/templates/.claude/skills/avatar-boot-starter-nacos/README.md +901 -0
  26. package/templates/.claude/skills/avatar-boot-starter-nacos/SKILL.md +40 -879
  27. package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/212/237/350/203/275/344/275/277/347/224/250.md +134 -0
  28. package/templates/.claude/skills/avatar-boot-starter-nacos/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +96 -0
  29. package/templates/.claude/skills/avatar-boot-starter-nacos/references//346/225/205/351/232/234/346/216/222/346/237/245.md +64 -0
  30. package/templates/.claude/skills/avatar-boot-starter-oss/README.md +594 -0
  31. package/templates/.claude/skills/avatar-boot-starter-oss/SKILL.md +52 -570
  32. package/templates/.claude/skills/avatar-boot-starter-oss/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +77 -0
  33. package/templates/.claude/skills/avatar-boot-starter-oss/references//346/240/270/345/277/203/345/212/237/350/203/275.md +94 -0
  34. package/templates/.claude/skills/avatar-boot-starter-oss/references//350/247/204/350/214/203/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +61 -0
  35. package/templates/.claude/skills/avatar-boot-starter-redis/README.md +586 -0
  36. package/templates/.claude/skills/avatar-boot-starter-redis/SKILL.md +42 -566
  37. package/templates/.claude/skills/avatar-boot-starter-redis/references//345/277/253/351/200/237/346/216/245/345/205/245/344/270/216/351/205/215/347/275/256.md +78 -0
  38. package/templates/.claude/skills/avatar-boot-starter-redis/references//346/225/260/346/215/256/346/223/215/344/275/234.md +111 -0
  39. package/templates/.claude/skills/avatar-boot-starter-redis/references//351/253/230/347/272/247/345/212/237/350/203/275.md +90 -0
  40. package/templates/.claude/skills/avatar-boot-starter-rocketmq/README.md +662 -0
  41. package/templates/.claude/skills/avatar-boot-starter-rocketmq/SKILL.md +48 -640
  42. package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//346/240/270/345/277/203/345/212/237/350/203/275.md +101 -0
  43. package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/205/215/347/275/256/344/270/216/346/263/250/346/204/217/344/272/213/351/241/271.md +44 -0
  44. package/templates/.claude/skills/avatar-boot-starter-rocketmq/references//351/253/230/347/272/247/347/211/271/346/200/247.md +71 -0
  45. package/templates/.claude/skills/avatar-boot-starter-web/README.md +1007 -0
  46. package/templates/.claude/skills/avatar-boot-starter-web/SKILL.md +150 -1003
  47. package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-LogInfo/346/263/250/350/247/243.md +75 -0
  48. package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/345/205/250/345/261/200/345/274/202/345/270/270/345/244/204/347/220/206.md +90 -0
  49. package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/214/207/346/240/207/347/233/221/346/216/247.md +74 -0
  50. package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/346/227/245/345/277/227/344/275/223/347/263/273.md +73 -0
  51. package/templates/.claude/skills/avatar-boot-starter-web/references//345/212/237/350/203/275-/350/257/267/346/261/202/344/270/212/344/270/213/346/226/207.md +77 -0
  52. package/templates/.claude/skills/avatar-boot-starter-web/references//345/277/253/351/200/237/346/216/245/345/205/245/346/214/207/345/215/227.md +52 -0
  53. package/templates/.claude/skills/avatar-boot-starter-web/references//346/263/250/346/204/217/344/272/213/351/241/271.md +68 -0
  54. package/templates/.claude/skills/avatar-boot-starter-web/references//350/207/252/345/256/232/344/271/211/346/211/251/345/261/225/346/214/207/345/215/227.md +107 -0
  55. package/templates/.claude/skills/avatar-boot-starter-web/references//351/205/215/347/275/256/345/217/202/350/200/203.md +107 -0
  56. package/templates/.claude/skills/crud-generator/SKILL.md +133 -64
  57. package/templates/.claude/skills/database-design/README.md +207 -0
  58. package/templates/.claude/skills/database-design/SKILL.md +469 -82
  59. package/templates/.claude/skills/database-design/references//345/221/275/345/220/215/350/247/204/350/214/203.md +232 -0
  60. package/templates/.claude/skills/database-design/references//345/255/227/346/256/265/347/261/273/345/236/213/350/247/204/350/214/203.md +400 -0
  61. package/templates/.claude/skills/database-design/references//347/264/242/345/274/225/350/247/204/350/214/203.md +506 -0
  62. package/templates/avatar-scaffold-api/pom.xml +0 -5
  63. package/templates/avatar-scaffold-service/pom.xml +25 -87
  64. package/templates/avatar-scaffold-service/src/main/resources/application-dev.yaml +3 -5
  65. package/templates/avatar-scaffold-service/src/main/resources/application-local.yaml +2 -2
  66. package/templates/pom.xml +9 -18
@@ -0,0 +1,662 @@
1
+ ---
2
+ name: avatar-boot-starter-rocketmq
3
+ description: 当涉及 RocketMQ、消息队列、延迟消息、事务消息、顺序消息 相关功能时使用此技能 - 为 Spring Boot 3.5.3 应用提供基于 rocketmq-spring-boot-starter 2.3.1 的消息队列能力,包含生产者/消费者自动配置、延迟消息、事务消息、顺序消费。
4
+ ---
5
+
6
+ Avatar Boot 的 RocketMQ 消息队列集成模块,基于 rocketmq-spring-boot-starter 2.3.1 提供开箱即用的消息生产和消费能力。
7
+
8
+ ## 功能特性
9
+
10
+ - ✅ **自动配置** - 基于 Spring Boot 自动配置机制,自动装配 RocketMQTemplate
11
+ - ✅ **注解驱动** - 使用 @RocketMQMessageListener 注解快速定义消费者
12
+ - ✅ **延迟消息** - 支持 18 个等级的延迟消息投递
13
+ - ✅ **事务消息** - 支持分布式事务消息,保证本地事务与消息发送的一致性
14
+ - ✅ **顺序消息** - 支持分区有序和全局有序消息消费
15
+ - ✅ **Tag 过滤** - 支持基于 Tag 的消息过滤,灵活路由消息
16
+ - ✅ **重试机制** - 内置消费重试策略,支持自定义重试次数
17
+
18
+ ## 快速开始
19
+
20
+ ### 1. 添加依赖
21
+
22
+ 在项目的 `pom.xml` 中添加依赖:
23
+
24
+ ```xml
25
+ <dependency>
26
+ <groupId>com.iflytek.avatar.boot</groupId>
27
+ <artifactId>avatar-boot-starter-rocketmq</artifactId>
28
+ </dependency>
29
+ ```
30
+
31
+ > 版本由 Avatar Boot BOM 统一管理,无需指定 version。内置 rocketmq-spring-boot-starter 2.3.1。
32
+
33
+ ### 2. 配置文件
34
+
35
+ 在 `application.yml` 中添加 RocketMQ 配置:
36
+
37
+ ```yaml
38
+ rocketmq:
39
+ name-server: localhost:9876 # NameServer 地址
40
+ producer:
41
+ group: ${spring.application.name}-producer # 生产者组名
42
+ send-message-timeout: 3000 # 发送超时时间(毫秒)
43
+ retry-times-when-send-failed: 2 # 同步发送失败重试次数
44
+ retry-times-when-send-async-failed: 2 # 异步发送失败重试次数
45
+ max-message-size: 4194304 # 最大消息大小(4MB)
46
+ compress-message-body-threshold: 4096 # 消息压缩阈值(4KB)
47
+ ```
48
+
49
+ ### 3. 发送第一条消息
50
+
51
+ ```java
52
+ package com.example.producer;
53
+
54
+ import lombok.RequiredArgsConstructor;
55
+ import lombok.extern.slf4j.Slf4j;
56
+ import org.apache.rocketmq.spring.core.RocketMQTemplate;
57
+ import org.springframework.stereotype.Service;
58
+
59
+ @Slf4j
60
+ @Service
61
+ @RequiredArgsConstructor
62
+ public class SimpleProducer {
63
+
64
+ private final RocketMQTemplate rocketMQTemplate;
65
+
66
+ public void sendMessage(String topic, String message) {
67
+ rocketMQTemplate.convertAndSend(topic, message);
68
+ log.info("消息发送成功: topic={}, message={}", topic, message);
69
+ }
70
+ }
71
+ ```
72
+
73
+ ### 4. 消费第一条消息
74
+
75
+ ```java
76
+ package com.example.consumer;
77
+
78
+ import lombok.extern.slf4j.Slf4j;
79
+ import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
80
+ import org.apache.rocketmq.spring.core.RocketMQListener;
81
+ import org.springframework.stereotype.Component;
82
+
83
+ @Slf4j
84
+ @Component
85
+ @RocketMQMessageListener(
86
+ topic = "test-topic",
87
+ consumerGroup = "test-consumer-group"
88
+ )
89
+ public class SimpleConsumer implements RocketMQListener<String> {
90
+
91
+ @Override
92
+ public void onMessage(String message) {
93
+ log.info("收到消息: {}", message);
94
+ // 处理业务逻辑
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## 生产者详解
100
+
101
+ ### 同步发送
102
+
103
+ ```java
104
+ @Service
105
+ @RequiredArgsConstructor
106
+ public class OrderProducer {
107
+
108
+ private final RocketMQTemplate rocketMQTemplate;
109
+
110
+ /**
111
+ * 同步发送(等待 Broker 确认)
112
+ */
113
+ public void syncSend(OrderEvent event) {
114
+ SendResult result = rocketMQTemplate.syncSend("order-topic", event);
115
+ log.info("同步发送结果: msgId={}, status={}", result.getMsgId(), result.getSendStatus());
116
+ }
117
+
118
+ /**
119
+ * 同步发送(带 Tag)
120
+ */
121
+ public void syncSendWithTag(OrderEvent event, String tag) {
122
+ // destination 格式: topic:tag
123
+ SendResult result = rocketMQTemplate.syncSend("order-topic:" + tag, event);
124
+ log.info("发送结果: msgId={}, tag={}", result.getMsgId(), tag);
125
+ }
126
+
127
+ /**
128
+ * 同步发送(带超时和延迟级别)
129
+ */
130
+ public void syncSendWithTimeout(OrderEvent event) {
131
+ SendResult result = rocketMQTemplate.syncSend("order-topic",
132
+ MessageBuilder.withPayload(event).build(),
133
+ 3000); // 超时 3 秒
134
+ log.info("发送结果: {}", result);
135
+ }
136
+ }
137
+ ```
138
+
139
+ ### 异步发送
140
+
141
+ ```java
142
+ @Service
143
+ @RequiredArgsConstructor
144
+ public class AsyncProducer {
145
+
146
+ private final RocketMQTemplate rocketMQTemplate;
147
+
148
+ /**
149
+ * 异步发送(不阻塞当前线程)
150
+ */
151
+ public void asyncSend(OrderEvent event) {
152
+ rocketMQTemplate.asyncSend("order-topic", event, new SendCallback() {
153
+ @Override
154
+ public void onSuccess(SendResult sendResult) {
155
+ log.info("异步发送成功: msgId={}", sendResult.getMsgId());
156
+ }
157
+
158
+ @Override
159
+ public void onException(Throwable e) {
160
+ log.error("异步发送失败: event={}", event, e);
161
+ // 补偿处理:入库重试、告警
162
+ }
163
+ });
164
+ }
165
+
166
+ /**
167
+ * 单向发送(不需要确认,最快但不保证送达)
168
+ */
169
+ public void onewaySend(LogEvent event) {
170
+ rocketMQTemplate.sendOneWay("log-topic", event);
171
+ }
172
+ }
173
+ ```
174
+
175
+ ### 批量发送
176
+
177
+ ```java
178
+ @Service
179
+ @RequiredArgsConstructor
180
+ public class BatchProducer {
181
+
182
+ private final RocketMQTemplate rocketMQTemplate;
183
+
184
+ /**
185
+ * 批量发送消息
186
+ */
187
+ public void batchSend(List<OrderEvent> events) {
188
+ List<Message<OrderEvent>> messages = events.stream()
189
+ .map(event -> MessageBuilder.withPayload(event).build())
190
+ .collect(Collectors.toList());
191
+
192
+ SendResult result = rocketMQTemplate.syncSend("order-topic", messages, 5000);
193
+ log.info("批量发送结果: {}", result);
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## 消费者详解
199
+
200
+ ### 基本消费
201
+
202
+ ```java
203
+ @Slf4j
204
+ @Component
205
+ @RocketMQMessageListener(
206
+ topic = "order-topic",
207
+ consumerGroup = "order-consumer-group",
208
+ consumeMode = ConsumeMode.CONCURRENTLY, // 并发消费(默认)
209
+ messageModel = MessageModel.CLUSTERING, // 集群模式(默认)
210
+ consumeThreadNumber = 20 // 消费线程数
211
+ )
212
+ public class OrderConsumer implements RocketMQListener<OrderEvent> {
213
+
214
+ @Override
215
+ public void onMessage(OrderEvent event) {
216
+ log.info("收到订单事件: orderNo={}", event.getOrderNo());
217
+ // 处理业务逻辑
218
+ // 抛出异常会触发重试
219
+ }
220
+ }
221
+ ```
222
+
223
+ ### 带 Tag 过滤的消费
224
+
225
+ ```java
226
+ @Slf4j
227
+ @Component
228
+ @RocketMQMessageListener(
229
+ topic = "order-topic",
230
+ consumerGroup = "order-create-group",
231
+ selectorType = SelectorType.TAG,
232
+ selectorExpression = "CREATE || UPDATE" // 只消费 CREATE 和 UPDATE 标签的消息
233
+ )
234
+ public class OrderCreateConsumer implements RocketMQListener<OrderEvent> {
235
+
236
+ @Override
237
+ public void onMessage(OrderEvent event) {
238
+ log.info("收到订单创建/更新事件: {}", event);
239
+ }
240
+ }
241
+ ```
242
+
243
+ ### 消费原始 MessageExt
244
+
245
+ ```java
246
+ @Slf4j
247
+ @Component
248
+ @RocketMQMessageListener(
249
+ topic = "order-topic",
250
+ consumerGroup = "order-ext-group"
251
+ )
252
+ public class OrderExtConsumer implements RocketMQListener<MessageExt> {
253
+
254
+ @Override
255
+ public void onMessage(MessageExt messageExt) {
256
+ String msgId = messageExt.getMsgId();
257
+ int reconsumeTimes = messageExt.getReconsumeTimes();
258
+ String body = new String(messageExt.getBody(), StandardCharsets.UTF_8);
259
+
260
+ log.info("收到消息: msgId={}, 重试次数={}, body={}", msgId, reconsumeTimes, body);
261
+
262
+ if (reconsumeTimes >= 3) {
263
+ // 超过重试次数,记录到数据库人工处理
264
+ log.error("消息重试超过 3 次,进入人工处理: msgId={}", msgId);
265
+ return;
266
+ }
267
+
268
+ // 处理业务逻辑
269
+ }
270
+ }
271
+ ```
272
+
273
+ ## 延迟消息
274
+
275
+ RocketMQ 支持 18 个延迟级别:
276
+
277
+ | 级别 | 延迟时间 | 级别 | 延迟时间 |
278
+ |------|---------|------|---------|
279
+ | 1 | 1s | 10 | 6min |
280
+ | 2 | 5s | 11 | 7min |
281
+ | 3 | 10s | 12 | 8min |
282
+ | 4 | 30s | 13 | 9min |
283
+ | 5 | 1min | 14 | 10min |
284
+ | 6 | 2min | 15 | 20min |
285
+ | 7 | 3min | 16 | 30min |
286
+ | 8 | 4min | 17 | 1h |
287
+ | 9 | 5min | 18 | 2h |
288
+
289
+ ### 发送延迟消息
290
+
291
+ ```java
292
+ @Service
293
+ @RequiredArgsConstructor
294
+ public class DelayMessageProducer {
295
+
296
+ private final RocketMQTemplate rocketMQTemplate;
297
+
298
+ /**
299
+ * 发送延迟消息
300
+ * @param delayLevel 延迟级别(1-18)
301
+ */
302
+ public void sendDelayMessage(String topic, Object payload, int delayLevel) {
303
+ Message<?> message = MessageBuilder.withPayload(payload).build();
304
+ // syncSend(destination, message, timeout, delayLevel)
305
+ SendResult result = rocketMQTemplate.syncSend(topic, message, 3000, delayLevel);
306
+ log.info("延迟消息发送成功: msgId={}, delayLevel={}", result.getMsgId(), delayLevel);
307
+ }
308
+
309
+ /**
310
+ * 订单超时取消:30 分钟后检查(延迟级别 16)
311
+ */
312
+ public void sendOrderTimeoutCheck(String orderNo) {
313
+ OrderTimeoutEvent event = new OrderTimeoutEvent(orderNo, LocalDateTime.now());
314
+ Message<?> message = MessageBuilder.withPayload(event).build();
315
+ rocketMQTemplate.syncSend("order-timeout-topic", message, 3000, 16);
316
+ log.info("订单超时检查消息已发送: orderNo={}, 30 分钟后触发", orderNo);
317
+ }
318
+ }
319
+ ```
320
+
321
+ ### 消费延迟消息
322
+
323
+ ```java
324
+ @Slf4j
325
+ @Component
326
+ @RocketMQMessageListener(
327
+ topic = "order-timeout-topic",
328
+ consumerGroup = "order-timeout-group"
329
+ )
330
+ public class OrderTimeoutConsumer implements RocketMQListener<OrderTimeoutEvent> {
331
+
332
+ @Autowired
333
+ private OrderService orderService;
334
+
335
+ @Override
336
+ public void onMessage(OrderTimeoutEvent event) {
337
+ log.info("收到订单超时检查: orderNo={}", event.getOrderNo());
338
+ // 检查订单是否已支付,未支付则取消
339
+ orderService.cancelIfUnpaid(event.getOrderNo());
340
+ }
341
+ }
342
+ ```
343
+
344
+ ## 事务消息
345
+
346
+ ### 事务消息流程
347
+
348
+ 1. 生产者发送半事务消息(Half Message)到 Broker
349
+ 2. Broker 存储成功后回调本地事务执行
350
+ 3. 本地事务执行成功,提交消息;失败,回滚消息
351
+ 4. Broker 定期回查未确认的事务消息状态
352
+
353
+ ### 发送事务消息
354
+
355
+ ```java
356
+ @Service
357
+ @RequiredArgsConstructor
358
+ public class TransactionalProducer {
359
+
360
+ private final RocketMQTemplate rocketMQTemplate;
361
+
362
+ /**
363
+ * 发送事务消息
364
+ */
365
+ public void sendTransactionalMessage(OrderEvent event) {
366
+ Message<OrderEvent> message = MessageBuilder
367
+ .withPayload(event)
368
+ .setHeader("orderNo", event.getOrderNo())
369
+ .build();
370
+
371
+ // 发送事务消息
372
+ TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction(
373
+ "order-tx-topic", message, event);
374
+
375
+ log.info("事务消息发送结果: msgId={}, localTxState={}",
376
+ result.getMsgId(), result.getLocalTransactionState());
377
+ }
378
+ }
379
+ ```
380
+
381
+ ### 实现本地事务监听器
382
+
383
+ ```java
384
+ package com.example.listener;
385
+
386
+ import com.example.event.OrderEvent;
387
+ import com.example.service.OrderService;
388
+ import lombok.RequiredArgsConstructor;
389
+ import lombok.extern.slf4j.Slf4j;
390
+ import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
391
+ import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
392
+ import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
393
+ import org.springframework.messaging.Message;
394
+ import org.springframework.stereotype.Component;
395
+
396
+ @Slf4j
397
+ @Component
398
+ @RocketMQTransactionListener
399
+ @RequiredArgsConstructor
400
+ public class OrderTransactionListener implements RocketMQLocalTransactionListener {
401
+
402
+ private final OrderService orderService;
403
+
404
+ /**
405
+ * 执行本地事务
406
+ * 半事务消息发送成功后回调
407
+ */
408
+ @Override
409
+ public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
410
+ OrderEvent event = (OrderEvent) arg;
411
+ try {
412
+ log.info("执行本地事务: orderNo={}", event.getOrderNo());
413
+ // 执行本地数据库操作
414
+ orderService.createOrderLocal(event);
415
+ return RocketMQLocalTransactionState.COMMIT; // 提交消息
416
+ } catch (Exception e) {
417
+ log.error("本地事务执行失败: orderNo={}", event.getOrderNo(), e);
418
+ return RocketMQLocalTransactionState.ROLLBACK; // 回滚消息
419
+ }
420
+ }
421
+
422
+ /**
423
+ * 事务回查
424
+ * Broker 未收到确认时定期回查
425
+ */
426
+ @Override
427
+ public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
428
+ String orderNo = (String) msg.getHeaders().get("orderNo");
429
+ log.info("事务回查: orderNo={}", orderNo);
430
+
431
+ // 查询本地事务是否已执行
432
+ boolean exists = orderService.existsByOrderNo(orderNo);
433
+ if (exists) {
434
+ return RocketMQLocalTransactionState.COMMIT;
435
+ }
436
+ return RocketMQLocalTransactionState.UNKNOWN; // 继续回查
437
+ }
438
+ }
439
+ ```
440
+
441
+ ## 顺序消息
442
+
443
+ ### 发送顺序消息
444
+
445
+ ```java
446
+ @Service
447
+ @RequiredArgsConstructor
448
+ public class OrderlyProducer {
449
+
450
+ private final RocketMQTemplate rocketMQTemplate;
451
+
452
+ /**
453
+ * 发送顺序消息
454
+ * 相同 hashKey 的消息发送到同一队列,保证顺序
455
+ */
456
+ public void sendOrderly(String topic, Object payload, String hashKey) {
457
+ SendResult result = rocketMQTemplate.syncSendOrderly(topic, payload, hashKey);
458
+ log.info("顺序消息发送成功: msgId={}, queue={}", result.getMsgId(),
459
+ result.getMessageQueue().getQueueId());
460
+ }
461
+
462
+ /**
463
+ * 订单状态变更:同一订单的状态变更消息保证顺序
464
+ */
465
+ public void sendOrderStatusChange(String orderNo, String status) {
466
+ OrderStatusEvent event = new OrderStatusEvent(orderNo, status, LocalDateTime.now());
467
+ // 使用 orderNo 作为 hashKey,保证同一订单消息有序
468
+ rocketMQTemplate.syncSendOrderly("order-status-topic", event, orderNo);
469
+ }
470
+ }
471
+ ```
472
+
473
+ ### 顺序消费
474
+
475
+ ```java
476
+ @Slf4j
477
+ @Component
478
+ @RocketMQMessageListener(
479
+ topic = "order-status-topic",
480
+ consumerGroup = "order-status-group",
481
+ consumeMode = ConsumeMode.ORDERLY // 顺序消费模式
482
+ )
483
+ public class OrderStatusConsumer implements RocketMQListener<OrderStatusEvent> {
484
+
485
+ @Override
486
+ public void onMessage(OrderStatusEvent event) {
487
+ log.info("顺序收到订单状态变更: orderNo={}, status={}",
488
+ event.getOrderNo(), event.getStatus());
489
+ // 按顺序处理订单状态变更
490
+ }
491
+ }
492
+ ```
493
+
494
+ ## Tag 消息过滤
495
+
496
+ ### 发送带 Tag 的消息
497
+
498
+ ```java
499
+ @Service
500
+ @RequiredArgsConstructor
501
+ public class TagProducer {
502
+
503
+ private final RocketMQTemplate rocketMQTemplate;
504
+
505
+ public void sendWithTag(OrderEvent event, OrderAction action) {
506
+ // destination 格式: topic:tag
507
+ String destination = "order-topic:" + action.name();
508
+ rocketMQTemplate.syncSend(destination, event);
509
+ }
510
+ }
511
+
512
+ public enum OrderAction {
513
+ CREATE, UPDATE, CANCEL, COMPLETE
514
+ }
515
+ ```
516
+
517
+ ### 按 Tag 过滤消费
518
+
519
+ ```java
520
+ // 只消费创建和取消的订单消息
521
+ @RocketMQMessageListener(
522
+ topic = "order-topic",
523
+ consumerGroup = "order-alert-group",
524
+ selectorType = SelectorType.TAG,
525
+ selectorExpression = "CREATE || CANCEL"
526
+ )
527
+ public class OrderAlertConsumer implements RocketMQListener<OrderEvent> {
528
+ @Override
529
+ public void onMessage(OrderEvent event) {
530
+ // 仅处理 CREATE 和 CANCEL 标签的消息
531
+ }
532
+ }
533
+ ```
534
+
535
+ ## 重试策略配置
536
+
537
+ ### 消费重试
538
+
539
+ RocketMQ 默认消费失败重试 16 次,重试间隔逐步增大:
540
+
541
+ | 重试次数 | 间隔时间 | 重试次数 | 间隔时间 |
542
+ |---------|---------|---------|---------|
543
+ | 1 | 10s | 9 | 7min |
544
+ | 2 | 30s | 10 | 8min |
545
+ | 3 | 1min | 11 | 9min |
546
+ | 4 | 2min | 12 | 10min |
547
+ | 5 | 3min | 13 | 20min |
548
+ | 6 | 4min | 14 | 30min |
549
+ | 7 | 5min | 15 | 1h |
550
+ | 8 | 6min | 16 | 2h |
551
+
552
+ ### 自定义重试次数
553
+
554
+ ```java
555
+ @RocketMQMessageListener(
556
+ topic = "order-topic",
557
+ consumerGroup = "order-retry-group",
558
+ maxReconsumeTimes = 5 // 最大重试 5 次
559
+ )
560
+ public class RetryConsumer implements RocketMQListener<MessageExt> {
561
+
562
+ @Override
563
+ public void onMessage(MessageExt msg) {
564
+ if (msg.getReconsumeTimes() >= 3) {
565
+ log.warn("消息重试 {} 次,进入补偿流程: msgId={}", msg.getReconsumeTimes(), msg.getMsgId());
566
+ // 入库人工处理
567
+ return;
568
+ }
569
+ // 正常业务处理,抛出异常会自动重试
570
+ }
571
+ }
572
+ ```
573
+
574
+ ## 最佳实践
575
+
576
+ ### 1. 生产者
577
+
578
+ - **设置合理的超时时间**:`send-message-timeout` 建议 3-5 秒
579
+ - **启用重试**:同步和异步发送各设置 2-3 次重试
580
+ - **使用 Tag 分类消息**:便于消费端按需过滤
581
+ - **消息体控制大小**:单条消息不超过 4MB,大数据存 OSS 传引用
582
+
583
+ ### 2. 消费者
584
+
585
+ - **幂等消费**:根据业务唯一标识(如订单号)去重
586
+ - **异常处理**:合理使用重试机制,超过重试次数入库人工处理
587
+ - **消费线程数**:`consumeThreadNumber` 根据业务类型设置,IO 密集型可设大
588
+ - **避免长时间消费**:单条消息处理时间建议不超过 15 秒
589
+
590
+ ### 3. Topic 设计
591
+
592
+ - **按业务域划分 Topic**:如 order-topic、payment-topic、user-topic
593
+ - **使用 Tag 区分操作**:如 CREATE、UPDATE、DELETE
594
+ - **Topic 命名规范**:`{应用名}-{业务域}-topic`
595
+
596
+ ### 4. 事务消息
597
+
598
+ - **本地事务要快**:避免在 executeLocalTransaction 中执行耗时操作
599
+ - **回查逻辑要幂等**:checkLocalTransaction 可能被多次调用
600
+ - **记录事务状态**:本地事务执行后记录状态,便于回查
601
+
602
+ ## 常见问题
603
+
604
+ ### 1. 消息发送超时
605
+
606
+ **原因**:NameServer 或 Broker 不可达
607
+
608
+ **解决**:
609
+ - 检查 `rocketmq.name-server` 配置是否正确
610
+ - 检查网络连通性:`telnet nameserver-host 9876`
611
+ - 增大 `send-message-timeout` 配置
612
+ - 检查 Broker 是否正常运行
613
+
614
+ ### 2. 消费者无法消费消息
615
+
616
+ **原因**:消费者组名冲突或 Topic 不存在
617
+
618
+ **解决**:
619
+ - 检查 `consumerGroup` 是否唯一(不同 Topic 不能使用相同消费者组)
620
+ - 检查 Topic 是否已创建
621
+ - 检查 selectorExpression(Tag 过滤表达式)是否正确
622
+ - 查看消费者日志是否有注册成功的信息
623
+
624
+ ### 3. 消息堆积
625
+
626
+ **原因**:消费速度跟不上生产速度
627
+
628
+ **解决**:
629
+ - 增大 `consumeThreadNumber`(消费线程数)
630
+ - 优化消费逻辑,减少单条消息处理时间
631
+ - 临时扩容消费者实例
632
+ - 检查是否有消费异常导致重试堆积
633
+
634
+ ### 4. 事务消息一直 UNKNOWN
635
+
636
+ **原因**:`checkLocalTransaction` 无法确认事务状态
637
+
638
+ **解决**:
639
+ - 确保本地事务执行后有持久化状态可查
640
+ - 检查回查逻辑是否正确查询了本地事务状态
641
+ - 设置合理的事务回查间隔和次数
642
+
643
+ ### 5. 顺序消息乱序
644
+
645
+ **原因**:消费端并发消费或消费失败重试
646
+
647
+ **解决**:
648
+ - 确保 `consumeMode = ConsumeMode.ORDERLY`
649
+ - 避免消费异常导致的重试打乱顺序
650
+ - 确保使用正确的 hashKey 发送到同一队列
651
+
652
+ ## 依赖版本
653
+
654
+ - rocketmq-spring-boot-starter: 2.3.1
655
+ - Spring Boot: 3.5.3
656
+ - Java: 21
657
+
658
+ ## 参考文档
659
+
660
+ - [RocketMQ Spring 官方文档](https://github.com/apache/rocketmq-spring)
661
+ - [Apache RocketMQ 官方文档](https://rocketmq.apache.org/docs/)
662
+ - [RocketMQ 最佳实践](https://rocketmq.apache.org/docs/bestPractice/01bestpractice/)