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,224 +1,176 @@
1
1
  ---
2
2
  name: scheduled-jobs
3
3
  description: |
4
- 定时任务开发指南。涵盖 @Scheduled、SnailJob 两种方案,支持分布式任务调度、失败重试、工作流编排。
5
-
4
+ 通用定时任务开发指南。涵盖 @Scheduled、Quartz、XXL-Job 等方案的概念对比与实现模式。
6
5
  触发场景:
7
6
  - 每日数据汇总、定期清理等周期性任务
8
- - 分布式复杂业务、失败重试、可视化管理(SnailJob)
9
- - 任务分片、Map/MapReduce 分布式计算
10
- - 广播任务(所有节点执行)
11
-
12
- 触发词:定时任务、SnailJob、任务调度、重试机制、工作流、@JobExecutor、@Scheduled、分布式任务、广播任务、分片任务、MapReduce
13
-
14
- 核心特性:
15
- - @Scheduled:简单周期任务、框架内置
16
- - SnailJob:分布式集群、可视化管理、失败重试、工作流编排
7
+ - 分布式任务调度、失败重试
8
+ - 任务分片、广播任务
9
+ - 可视化任务管理和监控
10
+ 触发词:定时任务、任务调度、@Scheduled、Quartz、XXL-Job、Cron、重试、分片任务、广播任务、分布式任务
11
+ 注意:如果项目有专属技能(如 `leniu-java-task`),优先使用专属版本。
17
12
  ---
18
13
 
19
14
  # 定时任务开发指南
20
15
 
21
- > 模块位置:`ruoyi-modules/ruoyi-job`
16
+ > 通用模板。如果项目有专属技能(如 `leniu-java-task`),优先使用。
22
17
 
23
- ## 方案选择
18
+ ## 设计原则
24
19
 
25
- | 场景 | 推荐 | 理由 |
26
- |------|------|------|
27
- | 简单周期任务(日报、清理) | `@Scheduled` | 内置、无依赖 |
28
- | 分布式/需重试/需监控 | **SnailJob** | 可视化管理、完整重试 |
29
- | 广播(所有节点执行) | **SnailJob** | 支持广播模式 |
30
- | 海量数据分片 | **SnailJob** | 静态分片/Map/MapReduce |
20
+ 1. **幂等性**:任务重复执行不会产生副作用。考虑任务被重试、多次调度的情况。
21
+ 2. **可观测**:任务执行结果必须可追踪(日志、监控、告警)。
22
+ 3. **故障隔离**:单个任务失败不应影响其他任务的调度。
23
+ 4. **超时控制**:设置合理的执行超时时间,避免任务无限阻塞。
31
24
 
32
25
  ---
33
26
 
34
- ## SnailJob 配置
35
-
36
- ```yaml
37
- # application-dev.yml
38
- snail-job:
39
- enabled: ${SNAIL_JOB_ENABLED:false}
40
- group: ${app.id}
41
- token: ${SNAIL_JOB_TOKEN:SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT}
42
- server:
43
- host: ${SNAIL_JOB_HOST:127.0.0.1}
44
- port: ${SNAIL_JOB_PORT:17888}
45
- namespace: ${spring.profiles.active}
46
- port: 2${server.port}
47
- ```
27
+ ## 方案对比
48
28
 
49
- ```java
50
- // ruoyi-common/ruoyi-common-job/.../config/SnailJobConfig.java
51
- @AutoConfiguration
52
- @ConditionalOnProperty(prefix = "snail-job", name = "enabled", havingValue = "true")
53
- @EnableScheduling
54
- @EnableSnailJob
55
- public class SnailJobConfig {}
29
+ | 维度 | @Scheduled | Quartz | XXL-Job | 其他(SnailJob 等) |
30
+ |------|-----------|--------|---------|-------------------|
31
+ | 学习成本 | 极低 | 中等 | 低 | 低-中 |
32
+ | 分布式支持 | | 支持(数据库锁) | 原生支持 | 原生支持 |
33
+ | 可视化管理 | 无 | 无(需自建) | Web 控制台 | Web 控制台 |
34
+ | 失败重试 | 无 | 支持 | 支持 | 支持 |
35
+ | 任务分片 | 不支持 | 不支持 | 支持 | 支持 |
36
+ | 广播模式 | 不支持 | 不支持 | 支持 | 支持 |
37
+ | 工作流编排 | 不支持 | 不支持 | 子任务依赖 | 部分支持 |
38
+ | 依赖 | Spring 内置 | quartz jar | 独立服务 | 独立服务 |
39
+ | 适用场景 | 简单周期任务 | 中等复杂度 | 生产级分布式 | 生产级分布式 |
40
+
41
+ ### 选型决策树
42
+
43
+ ```
44
+ 需要分布式调度?
45
+ ├── 否 → @Scheduled(简单定时)
46
+ └── 是 → 需要可视化管理?
47
+ ├── 否 → Quartz + 数据库(中等规模)
48
+ └── 是 → XXL-Job / SnailJob / PowerJob(生产推荐)
56
49
  ```
57
50
 
58
51
  ---
59
52
 
60
- ## SnailJob 任务类型
53
+ ## 实现模式
61
54
 
62
- ### 基础任务(注解方式,推荐)
63
-
64
- > 源码:`ruoyi-job/.../snailjob/TestAnnoJobExecutor.java`
55
+ ### 方案一:@Scheduled(Spring 内置)
65
56
 
66
57
  ```java
67
- import com.aizuda.snailjob.client.job.core.annotation.JobExecutor;
68
- import com.aizuda.snailjob.client.job.core.dto.JobArgs;
69
- import com.aizuda.snailjob.model.dto.ExecuteResult;
70
- import com.aizuda.snailjob.common.log.SnailJobLog;
71
- import com.aizuda.snailjob.common.core.util.JsonUtil;
72
-
73
58
  @Component
74
- @JobExecutor(name = "testJobExecutor")
75
- public class TestAnnoJobExecutor {
59
+ @EnableScheduling
60
+ public class SimpleScheduledTasks {
76
61
 
77
- public ExecuteResult jobExecute(JobArgs jobArgs) {
78
- SnailJobLog.REMOTE.info("任务执行,参数: {}", JsonUtil.toJsonString(jobArgs));
79
- String jobParams = jobArgs.getJobParams();
80
- // 业务逻辑...
81
- return ExecuteResult.success("执行成功");
62
+ // 固定频率:每30秒执行一次
63
+ @Scheduled(fixedRate = 30000)
64
+ public void heartbeat() {
65
+ log.info("心跳检测");
82
66
  }
83
- }
84
- ```
85
-
86
- ### 广播任务
87
67
 
88
- > 源码:`ruoyi-job/.../snailjob/TestBroadcastJob.java`
89
-
90
- 所有节点都执行,适用于清理本地缓存等场景。
68
+ // 固定延迟:上次执行完成后等10秒再执行
69
+ @Scheduled(fixedDelay = 10000)
70
+ public void cleanTempFiles() {
71
+ log.info("清理临时文件");
72
+ }
91
73
 
92
- ```java
93
- @Component
94
- @JobExecutor(name = "testBroadcastJob")
95
- public class TestBroadcastJob {
74
+ // Cron 表达式:每天凌晨2点执行
75
+ @Scheduled(cron = "0 0 2 * * ?")
76
+ public void dailyReport() {
77
+ log.info("生成日报");
78
+ }
96
79
 
97
- public ExecuteResult jobExecute(JobArgs jobArgs) {
98
- SnailJobLog.REMOTE.info("广播任务执行");
99
- // 每个节点都会执行此方法
100
- return ExecuteResult.success("广播任务执行成功");
80
+ // 使用配置文件中的 Cron
81
+ @Scheduled(cron = "${task.cleanup.cron:0 0 3 * * ?}")
82
+ public void configuredTask() {
83
+ log.info("可配置的定时任务");
101
84
  }
102
85
  }
103
86
  ```
104
87
 
105
- ### 静态分片任务
106
-
107
- > 源码:`ruoyi-job/.../snailjob/TestStaticShardingJob.java`
108
-
109
- 按固定规则分片,每个节点处理不同数据范围。
88
+ **注意事项**:
89
+ - 默认单线程执行,需配置线程池避免任务阻塞
90
+ - 多实例部署时每个实例都会执行,需分布式锁控制
110
91
 
111
92
  ```java
112
- @Component
113
- @JobExecutor(name = "testStaticShardingJob")
114
- public class TestStaticShardingJob {
115
-
116
- // jobParams 格式:起始ID,结束ID(如:1,100000)
117
- public ExecuteResult jobExecute(JobArgs jobArgs) {
118
- String[] split = jobArgs.getJobParams().split(",");
119
- Long fromId = Long.parseLong(split[0]);
120
- Long toId = Long.parseLong(split[1]);
121
- SnailJobLog.REMOTE.info("处理 ID 范围: {} - {}", fromId, toId);
122
- // 处理该范围的数据
123
- return ExecuteResult.success("分片任务完成");
93
+ @Configuration
94
+ public class SchedulingConfig implements SchedulingConfigurer {
95
+ @Override
96
+ public void configureTasks(ScheduledTaskRegistrar registrar) {
97
+ registrar.setScheduler(Executors.newScheduledThreadPool(5));
124
98
  }
125
99
  }
126
100
  ```
127
101
 
128
- **控制台分片配置**:分片0=`1,100000` / 分片1=`100001,200000` / 分片2=`200001,300000`
129
-
130
- ### Map 任务(动态分片)
131
-
132
- > 源码:`ruoyi-job/.../snailjob/TestMapJobAnnotation.java`
133
-
134
- 运行时动态拆分数据,并行执行。
102
+ ### 方案二:Quartz
135
103
 
136
104
  ```java
137
- import com.aizuda.snailjob.client.job.core.annotation.MapExecutor;
138
- import com.aizuda.snailjob.client.job.core.dto.MapArgs;
139
- import com.aizuda.snailjob.client.job.core.MapHandler;
105
+ // 1. 定义 Job
106
+ public class OrderCleanupJob implements Job {
107
+ @Override
108
+ public void execute(JobExecutionContext context) throws JobExecutionException {
109
+ JobDataMap dataMap = context.getMergedJobDataMap();
110
+ int days = dataMap.getInt("retentionDays");
111
+ // 业务逻辑...
112
+ }
113
+ }
140
114
 
141
- @Component
142
- @JobExecutor(name = "testMapJobAnnotation")
143
- public class TestMapJobAnnotation {
144
-
145
- @MapExecutor // 无 taskName = 入口方法
146
- public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
147
- List<List<Integer>> partition = IntStream.rangeClosed(1, 200)
148
- .boxed()
149
- .collect(Collectors.groupingBy(i -> (i - 1) / 50))
150
- .values().stream().toList();
151
- return mapHandler.doMap(partition, "doCalc");
115
+ // 2. 配置调度
116
+ @Configuration
117
+ public class QuartzConfig {
118
+
119
+ @Bean
120
+ public JobDetail orderCleanupJobDetail() {
121
+ return JobBuilder.newJob(OrderCleanupJob.class)
122
+ .withIdentity("orderCleanup", "maintenance")
123
+ .usingJobData("retentionDays", 30)
124
+ .storeDurably()
125
+ .build();
152
126
  }
153
127
 
154
- @MapExecutor(taskName = "doCalc") // 子任务
155
- public ExecuteResult doCalc(MapArgs mapArgs) {
156
- List<Integer> sourceList = (List<Integer>) mapArgs.getMapResult();
157
- int total = sourceList.stream().mapToInt(i -> i).sum();
158
- return ExecuteResult.success(total);
128
+ @Bean
129
+ public Trigger orderCleanupTrigger() {
130
+ return TriggerBuilder.newTrigger()
131
+ .forJob(orderCleanupJobDetail())
132
+ .withIdentity("orderCleanupTrigger", "maintenance")
133
+ .withSchedule(CronScheduleBuilder.cronSchedule("0 0 2 * * ?"))
134
+ .build();
159
135
  }
160
136
  }
161
137
  ```
162
138
 
163
- ### MapReduce 任务(分片 + 汇总)
164
-
165
- > 源码:`ruoyi-job/.../snailjob/TestMapReduceAnnotation1.java`
166
-
167
- 在 Map 基础上增加 Reduce 汇总。
139
+ ### 方案三:分布式任务调度平台(XXL-Job 等)
168
140
 
169
141
  ```java
170
- import com.aizuda.snailjob.client.job.core.annotation.ReduceExecutor;
171
- import com.aizuda.snailjob.client.job.core.dto.ReduceArgs;
172
-
142
+ // 通用模式:注解标记任务处理器
173
143
  @Component
174
- @JobExecutor(name = "testMapReduceAnnotation1")
175
- public class TestMapReduceAnnotation1 {
176
-
177
- @MapExecutor
178
- public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) {
179
- // 拆分数据(同 Map 任务)
180
- return mapHandler.doMap(partition, "doCalc");
181
- }
144
+ public class OrderCleanupJobHandler {
182
145
 
183
- @MapExecutor(taskName = "doCalc")
184
- public ExecuteResult doCalc(MapArgs mapArgs) {
185
- List<Integer> sourceList = (List<Integer>) mapArgs.getMapResult();
186
- return ExecuteResult.success(sourceList.stream().mapToInt(i -> i).sum());
187
- }
146
+ @Autowired
147
+ private OrderService orderService;
188
148
 
189
- @ReduceExecutor
190
- public ExecuteResult reduceExecute(ReduceArgs reduceArgs) {
191
- List<?> mapResults = reduceArgs.getMapResult();
192
- int total = mapResults.stream()
193
- .mapToInt(i -> Integer.parseInt((String) i)).sum();
194
- SnailJobLog.REMOTE.info("Reduce 汇总,最终总和: {}", total);
195
- return ExecuteResult.success(total);
149
+ // 具体注解取决于所选平台
150
+ // XXL-Job: @XxlJob("orderCleanupHandler")
151
+ // SnailJob: @JobExecutor(name = "orderCleanupJob")
152
+ public void execute(String params) {
153
+ log.info("开始清理过期订单");
154
+ try {
155
+ int days = StringUtils.isBlank(params) ? 30 : Integer.parseInt(params);
156
+ int count = orderService.cleanupExpiredOrders(days);
157
+ log.info("清理完成,删除 {} 条", count);
158
+ } catch (Exception e) {
159
+ log.error("清理失败: {}", e.getMessage());
160
+ throw e; // 抛出异常触发平台重试
161
+ }
196
162
  }
197
163
  }
198
164
  ```
199
165
 
200
- ---
201
-
202
- ## 执行模式对比
166
+ ### 执行模式
203
167
 
204
168
  | 模式 | 特点 | 适用场景 |
205
169
  |------|------|---------|
206
- | **集群** | 多节点竞争,只有一个执行 | 订单处理、数据汇总 |
207
- | **广播** | 所有节点都执行 | 清理缓存、刷新配置 |
208
- | **静态分片** | 按固定规则分片 | 已知数据范围的批处理 |
209
- | **Map** | 动态分片 | 运行时确定分片 |
210
- | **MapReduce** | 动态分片 + 结果汇总 | 分布式计算(求和、统计) |
211
-
212
- ---
213
-
214
- ## 日志工具
215
-
216
- ```java
217
- import com.aizuda.snailjob.common.log.SnailJobLog;
218
-
219
- SnailJobLog.LOCAL.info("本地日志: {}", msg); // 输出到控制台/日志文件
220
- SnailJobLog.REMOTE.info("远程日志: {}", msg); // 上报到 SnailJob 控制台
221
- ```
170
+ | **集群/随机** | 多节点竞争,只有一个执行 | 订单处理、数据汇总 |
171
+ | **广播** | 所有节点都执行 | 清理本地缓存、刷新配置 |
172
+ | **分片** | 按规则分片,每节点处理部分数据 | 大数据量批处理 |
173
+ | **MapReduce** | 动态分片 + 结果汇总 | 分布式计算、统计 |
222
174
 
223
175
  ---
224
176
 
@@ -228,87 +180,98 @@ SnailJobLog.REMOTE.info("远程日志: {}", msg); // 上报到 SnailJob 控制
228
180
 
229
181
  ```java
230
182
  @Component
231
- @JobExecutor(name = "orderCleanupJob")
232
- public class OrderCleanupJob {
183
+ public class [你的任务类] {
184
+
185
+ private static final Logger log = LoggerFactory.getLogger([你的任务类].class);
233
186
 
234
187
  @Autowired
235
- private IOrderService orderService;
188
+ private [你的业务Service] bizService;
189
+
190
+ public void execute(String params) {
191
+ long startTime = System.currentTimeMillis();
192
+ log.info("[任务开始] 参数: {}", params);
236
193
 
237
- public ExecuteResult jobExecute(JobArgs jobArgs) {
238
- SnailJobLog.REMOTE.info("开始清理过期订单");
239
194
  try {
240
- String params = jobArgs.getJobParams();
241
- int days = StringUtils.isBlank(params) ? 30 : Integer.parseInt(params);
242
- int count = orderService.cleanupExpiredOrders(days);
243
- SnailJobLog.REMOTE.info("清理完成,删除 {} 条", count);
244
- return ExecuteResult.success("清理 " + count + " 条");
195
+ // 1. 解析参数
196
+ int days = parseParams(params);
197
+
198
+ // 2. 幂等检查
199
+ if (bizService.isAlreadyProcessed(today())) {
200
+ log.info("[任务跳过] 今日已执行");
201
+ return;
202
+ }
203
+
204
+ // 3. 执行业务逻辑
205
+ int count = bizService.process(days);
206
+
207
+ // 4. 记录结果
208
+ long cost = System.currentTimeMillis() - startTime;
209
+ log.info("[任务完成] 处理 {} 条,耗时 {} ms", count, cost);
210
+
245
211
  } catch (Exception e) {
246
- SnailJobLog.REMOTE.error("清理失败: {}", e.getMessage());
212
+ log.error("[任务失败] {}", e.getMessage(), e);
247
213
  throw e; // 抛出异常触发重试
248
214
  }
249
215
  }
250
216
  }
251
217
  ```
252
218
 
253
- ### 幂等性保证
254
-
255
- ```java
256
- public ExecuteResult jobExecute(JobArgs jobArgs) {
257
- String orderId = jobArgs.getJobParams();
258
- if (paymentService.isSynced(orderId)) {
259
- return ExecuteResult.success("已同步,跳过");
260
- }
261
- paymentService.sync(orderId);
262
- return ExecuteResult.success("同步成功");
263
- }
264
- ```
219
+ ### 重试策略对比
265
220
 
266
- ### 错误处理
221
+ | 策略 | 说明 | 适用场景 |
222
+ |------|------|---------|
223
+ | 固定间隔 | 每次间隔相同(如 30s) | 网络抖动、临时故障 |
224
+ | 指数退避 | 间隔逐倍增加(1s, 2s, 4s, 8s...) | 服务恢复中 |
225
+ | Cron 重试 | 按 Cron 表达式重试 | 定点重试 |
226
+ | 最大次数 | 限制最大重试次数 | 防止无限重试 |
267
227
 
268
- ```java
269
- // 抛出异常 -> SnailJob 自动重试
270
- throw e;
228
+ ### Cron 表达式速查
271
229
 
272
- // 返回 failure -> 不会触发重试(慎用)
273
- return ExecuteResult.failure("失败");
274
- ```
230
+ | 表达式 | 含义 |
231
+ |--------|------|
232
+ | `0 0 2 * * ?` | 每天凌晨 2:00 |
233
+ | `0 0/30 * * * ?` | 每 30 分钟 |
234
+ | `0 0 9-18 * * MON-FRI` | 工作日 9-18 点每小时 |
235
+ | `0 0 0 1 * ?` | 每月 1 号零点 |
236
+ | `0 0 0 L * ?` | 每月最后一天零点 |
275
237
 
276
238
  ---
277
239
 
278
- ## 控制台配置
279
-
280
- 1. **任务管理** -> **新增任务**
281
- 2. 任务名称与 `@JobExecutor(name)` 一致
282
- 3. 任务类型:集群/广播/静态分片/Map/MapReduce
283
- 4. 触发类型:CRON / 固定频率
284
-
285
- ### 重试策略
286
-
287
- | 策略 | 说明 | 场景 |
288
- |------|------|------|
289
- | 固定间隔 | 每次间隔相同 | 网络抖动 |
290
- | 指数退避 | 间隔逐倍增加 | 服务恢复中 |
291
- | CRON | 按表达式重试 | 定点重试 |
240
+ ## 常见错误
292
241
 
293
- ---
294
-
295
- ## 常见问题
242
+ ```java
243
+ // 1. @Scheduled 任务中抛出异常但不处理
244
+ @Scheduled(fixedRate = 60000)
245
+ public void task() {
246
+ service.process(); // 异常后任务停止调度!
247
+ }
248
+ // 应捕获异常
249
+ @Scheduled(fixedRate = 60000)
250
+ public void task() {
251
+ try {
252
+ service.process();
253
+ } catch (Exception e) {
254
+ log.error("任务执行失败", e);
255
+ }
256
+ }
296
257
 
297
- | 问题 | 排查步骤 |
298
- |------|---------|
299
- | 任务不执行 | 1.检查 `snail-job.enabled: true` 2.`@JobExecutor(name)` 与控制台一致 3.SnailJob 服务是否启动 |
300
- | @Scheduled vs SnailJob | 简单/<100个任务 -> @Scheduled;需重试/监控/分布式 -> SnailJob |
301
- | 查看任务日志 | 本地 -> 应用日志;远程 -> SnailJob 控制台 -> 任务实例 -> 查看日志 |
258
+ // 2. @Scheduled 默认单线程,长任务阻塞其他任务
259
+ // 应配置线程池(见上文 SchedulingConfig)
302
260
 
303
- ---
261
+ // 3. 多实例部署未做互斥
262
+ // @Scheduled 每个实例都执行 -> 数据重复处理
263
+ // 应使用分布式锁(Redis / 数据库)或分布式调度平台
304
264
 
305
- ## 核心文件位置
265
+ // 4. 任务非幂等
266
+ public void syncData() {
267
+ insertAll(fetchData()); // 重复执行会插入重复数据
268
+ }
269
+ // 应先检查是否已处理
306
270
 
307
- | 文件 | 路径 |
308
- |------|------|
309
- | 注解方式 | `ruoyi-modules/ruoyi-job/.../snailjob/TestAnnoJobExecutor.java` |
310
- | 类方式 | `ruoyi-modules/ruoyi-job/.../snailjob/TestClassJobExecutor.java` |
311
- | 广播任务 | `ruoyi-modules/ruoyi-job/.../snailjob/TestBroadcastJob.java` |
312
- | 静态分片 | `ruoyi-modules/ruoyi-job/.../snailjob/TestStaticShardingJob.java` |
313
- | Map 任务 | `ruoyi-modules/ruoyi-job/.../snailjob/TestMapJobAnnotation.java` |
314
- | MapReduce | `ruoyi-modules/ruoyi-job/.../snailjob/TestMapReduceAnnotation1.java` |
271
+ // 5. 任务内开启大事务
272
+ @Transactional
273
+ public void processAllOrders() {
274
+ // 处理百万条数据在一个事务中 -> 内存溢出、锁超时
275
+ }
276
+ // 应分批处理,每批独立事务
277
+ ```