ai-engineering-init 1.7.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 (118) hide show
  1. package/.claude/hooks/skill-forced-eval.js +46 -62
  2. package/.claude/settings.json +10 -1
  3. package/.claude/skills/api-development/SKILL.md +179 -130
  4. package/.claude/skills/architecture-design/SKILL.md +102 -212
  5. package/.claude/skills/backend-annotations/SKILL.md +166 -220
  6. package/.claude/skills/bug-detective/SKILL.md +225 -186
  7. package/.claude/skills/code-patterns/SKILL.md +127 -244
  8. package/.claude/skills/collaborating-with-codex/SKILL.md +96 -113
  9. package/.claude/skills/crud-development/SKILL.md +226 -307
  10. package/.claude/skills/data-permission/SKILL.md +131 -202
  11. package/.claude/skills/database-ops/SKILL.md +158 -355
  12. package/.claude/skills/error-handler/SKILL.md +224 -285
  13. package/.claude/skills/file-oss-management/SKILL.md +174 -169
  14. package/.claude/skills/git-workflow/SKILL.md +123 -341
  15. package/.claude/skills/json-serialization/SKILL.md +121 -137
  16. package/.claude/skills/performance-doctor/SKILL.md +83 -89
  17. package/.claude/skills/redis-cache/SKILL.md +134 -185
  18. package/.claude/skills/scheduled-jobs/SKILL.md +187 -224
  19. package/.claude/skills/security-guard/SKILL.md +168 -276
  20. package/.claude/skills/sms-mail/SKILL.md +266 -228
  21. package/.claude/skills/social-login/SKILL.md +257 -195
  22. package/.claude/skills/tenant-management/SKILL.md +172 -188
  23. package/.claude/skills/utils-toolkit/SKILL.md +214 -222
  24. package/.claude/skills/websocket-sse/SKILL.md +251 -172
  25. package/.claude/skills/workflow-engine/SKILL.md +178 -250
  26. package/.codex/skills/api-development/SKILL.md +179 -130
  27. package/.codex/skills/architecture-design/SKILL.md +102 -212
  28. package/.codex/skills/backend-annotations/SKILL.md +166 -220
  29. package/.codex/skills/bug-detective/SKILL.md +225 -186
  30. package/.codex/skills/code-patterns/SKILL.md +127 -244
  31. package/.codex/skills/collaborating-with-codex/SKILL.md +96 -113
  32. package/.codex/skills/crud-development/SKILL.md +226 -307
  33. package/.codex/skills/data-permission/SKILL.md +131 -202
  34. package/.codex/skills/database-ops/SKILL.md +158 -355
  35. package/.codex/skills/error-handler/SKILL.md +224 -285
  36. package/.codex/skills/file-oss-management/SKILL.md +174 -169
  37. package/.codex/skills/git-workflow/SKILL.md +123 -341
  38. package/.codex/skills/json-serialization/SKILL.md +121 -137
  39. package/.codex/skills/performance-doctor/SKILL.md +83 -89
  40. package/.codex/skills/redis-cache/SKILL.md +134 -185
  41. package/.codex/skills/scheduled-jobs/SKILL.md +187 -224
  42. package/.codex/skills/security-guard/SKILL.md +168 -276
  43. package/.codex/skills/sms-mail/SKILL.md +266 -228
  44. package/.codex/skills/social-login/SKILL.md +257 -195
  45. package/.codex/skills/tenant-management/SKILL.md +172 -188
  46. package/.codex/skills/utils-toolkit/SKILL.md +214 -222
  47. package/.codex/skills/websocket-sse/SKILL.md +251 -172
  48. package/.codex/skills/workflow-engine/SKILL.md +178 -250
  49. package/.cursor/hooks/cursor-skill-eval.js +66 -6
  50. package/.cursor/skills/api-development/SKILL.md +179 -130
  51. package/.cursor/skills/architecture-design/SKILL.md +102 -212
  52. package/.cursor/skills/backend-annotations/SKILL.md +166 -220
  53. package/.cursor/skills/bug-detective/SKILL.md +225 -186
  54. package/.cursor/skills/code-patterns/SKILL.md +127 -244
  55. package/.cursor/skills/collaborating-with-codex/SKILL.md +96 -113
  56. package/.cursor/skills/crud-development/SKILL.md +226 -307
  57. package/.cursor/skills/data-permission/SKILL.md +131 -202
  58. package/.cursor/skills/database-ops/SKILL.md +158 -355
  59. package/.cursor/skills/error-handler/SKILL.md +224 -285
  60. package/.cursor/skills/file-oss-management/SKILL.md +174 -169
  61. package/.cursor/skills/git-workflow/SKILL.md +123 -341
  62. package/.cursor/skills/json-serialization/SKILL.md +121 -137
  63. package/.cursor/skills/performance-doctor/SKILL.md +83 -89
  64. package/.cursor/skills/redis-cache/SKILL.md +134 -185
  65. package/.cursor/skills/scheduled-jobs/SKILL.md +187 -224
  66. package/.cursor/skills/security-guard/SKILL.md +168 -276
  67. package/.cursor/skills/sms-mail/SKILL.md +266 -228
  68. package/.cursor/skills/social-login/SKILL.md +257 -195
  69. package/.cursor/skills/tenant-management/SKILL.md +172 -188
  70. package/.cursor/skills/utils-toolkit/SKILL.md +214 -222
  71. package/.cursor/skills/websocket-sse/SKILL.md +251 -172
  72. package/.cursor/skills/workflow-engine/SKILL.md +178 -250
  73. package/AGENTS.md +49 -540
  74. package/CLAUDE.md +73 -119
  75. package/README.md +37 -6
  76. package/bin/index.js +5 -1
  77. package/package.json +1 -1
  78. package/src/skills/api-development/SKILL.md +179 -130
  79. package/src/skills/architecture-design/SKILL.md +102 -212
  80. package/src/skills/backend-annotations/SKILL.md +166 -220
  81. package/src/skills/bug-detective/SKILL.md +225 -186
  82. package/src/skills/code-patterns/SKILL.md +127 -244
  83. package/src/skills/collaborating-with-codex/SKILL.md +96 -113
  84. package/src/skills/crud-development/SKILL.md +226 -307
  85. package/src/skills/data-permission/SKILL.md +131 -202
  86. package/src/skills/database-ops/SKILL.md +158 -355
  87. package/src/skills/error-handler/SKILL.md +224 -285
  88. package/src/skills/file-oss-management/SKILL.md +174 -169
  89. package/src/skills/git-workflow/SKILL.md +123 -341
  90. package/src/skills/json-serialization/SKILL.md +121 -137
  91. package/src/skills/performance-doctor/SKILL.md +83 -89
  92. package/src/skills/redis-cache/SKILL.md +134 -185
  93. package/src/skills/scheduled-jobs/SKILL.md +187 -224
  94. package/src/skills/security-guard/SKILL.md +168 -276
  95. package/src/skills/sms-mail/SKILL.md +266 -228
  96. package/src/skills/social-login/SKILL.md +257 -195
  97. package/src/skills/tenant-management/SKILL.md +172 -188
  98. package/src/skills/utils-toolkit/SKILL.md +214 -222
  99. package/src/skills/websocket-sse/SKILL.md +251 -172
  100. package/src/skills/workflow-engine/SKILL.md +178 -250
  101. package/.claude/skills/skill-creator/LICENSE.txt +0 -202
  102. package/.claude/skills/skill-creator/SKILL.md +0 -479
  103. package/.claude/skills/skill-creator/agents/analyzer.md +0 -274
  104. package/.claude/skills/skill-creator/agents/comparator.md +0 -202
  105. package/.claude/skills/skill-creator/agents/grader.md +0 -223
  106. package/.claude/skills/skill-creator/assets/eval_review.html +0 -146
  107. package/.claude/skills/skill-creator/eval-viewer/generate_review.py +0 -471
  108. package/.claude/skills/skill-creator/eval-viewer/viewer.html +0 -1325
  109. package/.claude/skills/skill-creator/references/schemas.md +0 -430
  110. package/.claude/skills/skill-creator/scripts/__init__.py +0 -0
  111. package/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
  112. package/.claude/skills/skill-creator/scripts/generate_report.py +0 -326
  113. package/.claude/skills/skill-creator/scripts/improve_description.py +0 -248
  114. package/.claude/skills/skill-creator/scripts/package_skill.py +0 -136
  115. package/.claude/skills/skill-creator/scripts/quick_validate.py +0 -103
  116. package/.claude/skills/skill-creator/scripts/run_eval.py +0 -310
  117. package/.claude/skills/skill-creator/scripts/run_loop.py +0 -332
  118. 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
+ ```