jettask 0.2.23__py3-none-any.whl → 0.2.24__py3-none-any.whl
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.
- jettask/__init__.py +2 -0
- jettask/cli.py +12 -8
- jettask/config/lua_scripts.py +37 -0
- jettask/config/nacos_config.py +1 -1
- jettask/core/app.py +313 -340
- jettask/core/container.py +4 -4
- jettask/{persistence → core}/namespace.py +93 -27
- jettask/core/task.py +16 -9
- jettask/core/unified_manager_base.py +136 -26
- jettask/db/__init__.py +67 -0
- jettask/db/base.py +137 -0
- jettask/{utils/db_connector.py → db/connector.py} +130 -26
- jettask/db/models/__init__.py +16 -0
- jettask/db/models/scheduled_task.py +196 -0
- jettask/db/models/task.py +77 -0
- jettask/db/models/task_run.py +85 -0
- jettask/executor/__init__.py +0 -15
- jettask/executor/core.py +76 -31
- jettask/executor/process_entry.py +29 -114
- jettask/executor/task_executor.py +4 -0
- jettask/messaging/event_pool.py +928 -685
- jettask/messaging/scanner.py +30 -0
- jettask/persistence/__init__.py +28 -103
- jettask/persistence/buffer.py +170 -0
- jettask/persistence/consumer.py +330 -249
- jettask/persistence/manager.py +304 -0
- jettask/persistence/persistence.py +391 -0
- jettask/scheduler/__init__.py +15 -3
- jettask/scheduler/{task_crud.py → database.py} +61 -57
- jettask/scheduler/loader.py +2 -2
- jettask/scheduler/{scheduler_coordinator.py → manager.py} +23 -6
- jettask/scheduler/models.py +14 -10
- jettask/scheduler/schedule.py +166 -0
- jettask/scheduler/scheduler.py +12 -11
- jettask/schemas/__init__.py +50 -1
- jettask/schemas/backlog.py +43 -6
- jettask/schemas/namespace.py +70 -19
- jettask/schemas/queue.py +19 -3
- jettask/schemas/responses.py +493 -0
- jettask/task/__init__.py +0 -2
- jettask/task/router.py +3 -0
- jettask/test_connection_monitor.py +1 -1
- jettask/utils/__init__.py +7 -5
- jettask/utils/db_init.py +8 -4
- jettask/utils/namespace_dep.py +167 -0
- jettask/utils/queue_matcher.py +186 -0
- jettask/utils/rate_limit/concurrency_limiter.py +7 -1
- jettask/utils/stream_backlog.py +1 -1
- jettask/webui/__init__.py +0 -1
- jettask/webui/api/__init__.py +4 -4
- jettask/webui/api/alerts.py +806 -71
- jettask/webui/api/example_refactored.py +400 -0
- jettask/webui/api/namespaces.py +390 -45
- jettask/webui/api/overview.py +300 -54
- jettask/webui/api/queues.py +971 -267
- jettask/webui/api/scheduled.py +1249 -56
- jettask/webui/api/settings.py +129 -7
- jettask/webui/api/workers.py +442 -0
- jettask/webui/app.py +46 -2329
- jettask/webui/middleware/__init__.py +6 -0
- jettask/webui/middleware/namespace_middleware.py +135 -0
- jettask/webui/services/__init__.py +146 -0
- jettask/webui/services/heartbeat_service.py +251 -0
- jettask/webui/services/overview_service.py +60 -51
- jettask/webui/services/queue_monitor_service.py +426 -0
- jettask/webui/services/redis_monitor_service.py +87 -0
- jettask/webui/services/settings_service.py +174 -111
- jettask/webui/services/task_monitor_service.py +222 -0
- jettask/webui/services/timeline_pg_service.py +452 -0
- jettask/webui/services/timeline_service.py +189 -0
- jettask/webui/services/worker_monitor_service.py +467 -0
- jettask/webui/utils/__init__.py +11 -0
- jettask/webui/utils/time_utils.py +122 -0
- jettask/worker/lifecycle.py +8 -2
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/METADATA +1 -1
- jettask-0.2.24.dist-info/RECORD +142 -0
- jettask/executor/executor.py +0 -338
- jettask/persistence/backlog_monitor.py +0 -567
- jettask/persistence/base.py +0 -2334
- jettask/persistence/db_manager.py +0 -516
- jettask/persistence/maintenance.py +0 -81
- jettask/persistence/message_consumer.py +0 -259
- jettask/persistence/models.py +0 -49
- jettask/persistence/offline_recovery.py +0 -196
- jettask/persistence/queue_discovery.py +0 -215
- jettask/persistence/task_persistence.py +0 -218
- jettask/persistence/task_updater.py +0 -583
- jettask/scheduler/add_execution_count.sql +0 -11
- jettask/scheduler/add_priority_field.sql +0 -26
- jettask/scheduler/add_scheduler_id.sql +0 -25
- jettask/scheduler/add_scheduler_id_index.sql +0 -10
- jettask/scheduler/make_scheduler_id_required.sql +0 -28
- jettask/scheduler/migrate_interval_seconds.sql +0 -9
- jettask/scheduler/performance_optimization.sql +0 -45
- jettask/scheduler/run_scheduler.py +0 -186
- jettask/scheduler/schema.sql +0 -84
- jettask/task/task_executor.py +0 -318
- jettask/webui/api/analytics.py +0 -323
- jettask/webui/config.py +0 -90
- jettask/webui/models/__init__.py +0 -3
- jettask/webui/models/namespace.py +0 -63
- jettask/webui/namespace_manager/__init__.py +0 -10
- jettask/webui/namespace_manager/multi.py +0 -593
- jettask/webui/namespace_manager/unified.py +0 -193
- jettask/webui/run.py +0 -46
- jettask-0.2.23.dist-info/RECORD +0 -145
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/WHEEL +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.23.dist-info → jettask-0.2.24.dist-info}/top_level.txt +0 -0
jettask/webui/api/scheduled.py
CHANGED
@@ -15,17 +15,87 @@ logger = logging.getLogger(__name__)
|
|
15
15
|
|
16
16
|
# ============ 定时任务管理 ============
|
17
17
|
|
18
|
-
@router.get(
|
18
|
+
@router.get(
|
19
|
+
"/",
|
20
|
+
summary="获取定时任务列表",
|
21
|
+
description="获取定时任务列表,支持分页、搜索和状态筛选",
|
22
|
+
responses={
|
23
|
+
200: {
|
24
|
+
"description": "成功返回定时任务列表",
|
25
|
+
"content": {
|
26
|
+
"application/json": {
|
27
|
+
"example": {
|
28
|
+
"success": True,
|
29
|
+
"data": [
|
30
|
+
{
|
31
|
+
"id": "task-001",
|
32
|
+
"name": "每日数据统计",
|
33
|
+
"namespace": "default",
|
34
|
+
"queue_name": "stat_queue",
|
35
|
+
"schedule_type": "cron",
|
36
|
+
"schedule_config": {"cron": "0 0 * * *"},
|
37
|
+
"is_active": True,
|
38
|
+
"next_run_time": "2025-10-19T00:00:00Z"
|
39
|
+
}
|
40
|
+
],
|
41
|
+
"total": 50,
|
42
|
+
"page": 1,
|
43
|
+
"page_size": 20
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
},
|
48
|
+
500: {"description": "服务器内部错误"}
|
49
|
+
}
|
50
|
+
)
|
19
51
|
async def get_scheduled_tasks(
|
20
52
|
request: Request,
|
21
|
-
page: int = Query(1, ge=1),
|
22
|
-
page_size: int = Query(20, ge=1, le=100),
|
23
|
-
search: Optional[str] = Query(None, description="
|
24
|
-
is_active: Optional[bool] = Query(None, description="
|
53
|
+
page: int = Query(1, ge=1, description="页码,从 1 开始", example=1),
|
54
|
+
page_size: int = Query(20, ge=1, le=100, description="每页数量,范围 1-100", example=20),
|
55
|
+
search: Optional[str] = Query(None, description="搜索关键字,支持按名称、描述搜索", example="数据统计"),
|
56
|
+
is_active: Optional[bool] = Query(None, description="是否只返回激活的任务", example=True)
|
25
57
|
):
|
26
58
|
"""
|
27
|
-
获取定时任务列表
|
28
|
-
|
59
|
+
## 获取定时任务列表
|
60
|
+
|
61
|
+
获取系统中所有定时任务的列表,支持分页、搜索和按状态筛选。
|
62
|
+
|
63
|
+
**返回信息包括**:
|
64
|
+
- 任务 ID 和名称
|
65
|
+
- 命名空间和队列名称
|
66
|
+
- 调度类型和配置
|
67
|
+
- 激活状态
|
68
|
+
- 下次执行时间
|
69
|
+
- 创建和更新时间
|
70
|
+
|
71
|
+
**调度类型**:
|
72
|
+
- `cron`: Cron 表达式调度
|
73
|
+
- `interval`: 固定间隔调度
|
74
|
+
- `date`: 指定时间调度
|
75
|
+
- `every`: 周期性调度
|
76
|
+
|
77
|
+
**使用场景**:
|
78
|
+
- 定时任务管理页面
|
79
|
+
- 任务列表查看
|
80
|
+
- 任务搜索和筛选
|
81
|
+
|
82
|
+
**示例请求**:
|
83
|
+
```bash
|
84
|
+
# 获取所有定时任务
|
85
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/?page=1&page_size=20"
|
86
|
+
|
87
|
+
# 搜索特定任务
|
88
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/?search=数据统计"
|
89
|
+
|
90
|
+
# 只获取激活的任务
|
91
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/?is_active=true"
|
92
|
+
```
|
93
|
+
|
94
|
+
**注意事项**:
|
95
|
+
- 分页从 1 开始
|
96
|
+
- 搜索支持模糊匹配
|
97
|
+
- 返回结果按创建时间倒序排列
|
98
|
+
|
29
99
|
Args:
|
30
100
|
page: 页码(从1开始)
|
31
101
|
page_size: 每页数量
|
@@ -60,10 +130,161 @@ async def get_scheduled_tasks(
|
|
60
130
|
raise HTTPException(status_code=500, detail=str(e))
|
61
131
|
|
62
132
|
|
63
|
-
@router.post(
|
133
|
+
@router.post(
|
134
|
+
"/filter",
|
135
|
+
summary="获取定时任务列表(高级筛选)",
|
136
|
+
description="获取定时任务列表,支持高级筛选条件、时间范围查询和多维度过滤",
|
137
|
+
responses={
|
138
|
+
200: {
|
139
|
+
"description": "成功返回筛选后的定时任务列表",
|
140
|
+
"content": {
|
141
|
+
"application/json": {
|
142
|
+
"example": {
|
143
|
+
"success": True,
|
144
|
+
"data": [
|
145
|
+
{
|
146
|
+
"id": "task-001",
|
147
|
+
"name": "每日数据统计",
|
148
|
+
"namespace": "default",
|
149
|
+
"queue_name": "stat_queue",
|
150
|
+
"schedule_type": "cron",
|
151
|
+
"schedule_config": {"cron": "0 0 * * *"},
|
152
|
+
"is_active": True,
|
153
|
+
"next_run_time": "2025-10-19T00:00:00Z"
|
154
|
+
}
|
155
|
+
],
|
156
|
+
"total": 50,
|
157
|
+
"page": 1,
|
158
|
+
"page_size": 20
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
},
|
163
|
+
400: {
|
164
|
+
"description": "请求参数错误",
|
165
|
+
"content": {
|
166
|
+
"application/json": {
|
167
|
+
"example": {"detail": "时间范围参数无效"}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
},
|
171
|
+
500: {"description": "服务器内部错误"}
|
172
|
+
}
|
173
|
+
)
|
64
174
|
async def get_scheduled_tasks_with_filters(request: Request):
|
65
175
|
"""
|
66
|
-
获取定时任务列表(支持高级筛选)
|
176
|
+
## 获取定时任务列表(支持高级筛选)
|
177
|
+
|
178
|
+
获取定时任务列表,支持多维度筛选条件、时间范围查询和复杂过滤逻辑。
|
179
|
+
|
180
|
+
**请求体参数**:
|
181
|
+
```json
|
182
|
+
{
|
183
|
+
"page": 1,
|
184
|
+
"page_size": 20,
|
185
|
+
"search": "数据统计",
|
186
|
+
"is_active": true,
|
187
|
+
"filters": [
|
188
|
+
{
|
189
|
+
"field": "schedule_type",
|
190
|
+
"operator": "eq",
|
191
|
+
"value": "cron"
|
192
|
+
},
|
193
|
+
{
|
194
|
+
"field": "namespace",
|
195
|
+
"operator": "in",
|
196
|
+
"value": ["default", "production"]
|
197
|
+
}
|
198
|
+
],
|
199
|
+
"time_range": "7d",
|
200
|
+
"start_time": "2025-10-12T00:00:00Z",
|
201
|
+
"end_time": "2025-10-19T23:59:59Z"
|
202
|
+
}
|
203
|
+
```
|
204
|
+
|
205
|
+
**筛选器支持的字段**:
|
206
|
+
- `schedule_type`: 调度类型(cron/interval/date/every)
|
207
|
+
- `namespace`: 命名空间
|
208
|
+
- `queue_name`: 队列名称
|
209
|
+
- `is_active`: 激活状态
|
210
|
+
- `created_at`: 创建时间
|
211
|
+
|
212
|
+
**筛选器支持的操作符**:
|
213
|
+
- `eq`: 等于
|
214
|
+
- `ne`: 不等于
|
215
|
+
- `in`: 包含于列表
|
216
|
+
- `gt`: 大于
|
217
|
+
- `lt`: 小于
|
218
|
+
- `gte`: 大于等于
|
219
|
+
- `lte`: 小于等于
|
220
|
+
|
221
|
+
**时间范围快捷值**:
|
222
|
+
- `1h`: 最近1小时
|
223
|
+
- `24h`: 最近24小时
|
224
|
+
- `7d`: 最近7天
|
225
|
+
- `30d`: 最近30天
|
226
|
+
- `custom`: 自定义时间范围(需配合 start_time 和 end_time)
|
227
|
+
|
228
|
+
**返回信息包括**:
|
229
|
+
- 筛选后的任务列表
|
230
|
+
- 总数和分页信息
|
231
|
+
- 每个任务的完整配置
|
232
|
+
|
233
|
+
**使用场景**:
|
234
|
+
- 高级任务搜索
|
235
|
+
- 多条件组合查询
|
236
|
+
- 时间范围分析
|
237
|
+
- 任务统计报表
|
238
|
+
|
239
|
+
**示例请求**:
|
240
|
+
```bash
|
241
|
+
# 查询最近7天创建的激活任务
|
242
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/filter" \\
|
243
|
+
-H "Content-Type: application/json" \\
|
244
|
+
-d '{
|
245
|
+
"page": 1,
|
246
|
+
"page_size": 20,
|
247
|
+
"is_active": true,
|
248
|
+
"time_range": "7d"
|
249
|
+
}'
|
250
|
+
|
251
|
+
# 多条件组合查询
|
252
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/filter" \\
|
253
|
+
-H "Content-Type: application/json" \\
|
254
|
+
-d '{
|
255
|
+
"page": 1,
|
256
|
+
"page_size": 20,
|
257
|
+
"filters": [
|
258
|
+
{
|
259
|
+
"field": "schedule_type",
|
260
|
+
"operator": "eq",
|
261
|
+
"value": "cron"
|
262
|
+
},
|
263
|
+
{
|
264
|
+
"field": "namespace",
|
265
|
+
"operator": "in",
|
266
|
+
"value": ["default", "production"]
|
267
|
+
}
|
268
|
+
]
|
269
|
+
}'
|
270
|
+
|
271
|
+
# 自定义时间范围查询
|
272
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/filter" \\
|
273
|
+
-H "Content-Type: application/json" \\
|
274
|
+
-d '{
|
275
|
+
"page": 1,
|
276
|
+
"page_size": 20,
|
277
|
+
"time_range": "custom",
|
278
|
+
"start_time": "2025-10-12T00:00:00Z",
|
279
|
+
"end_time": "2025-10-19T23:59:59Z"
|
280
|
+
}'
|
281
|
+
```
|
282
|
+
|
283
|
+
**注意事项**:
|
284
|
+
- 所有筛选条件之间为 AND 关系
|
285
|
+
- 时间范围查询基于任务创建时间
|
286
|
+
- 使用 custom 时间范围时,必须同时提供 start_time 和 end_time
|
287
|
+
- 筛选器数组为空时等同于不使用筛选器
|
67
288
|
"""
|
68
289
|
try:
|
69
290
|
app = request.app
|
@@ -109,13 +330,193 @@ async def get_scheduled_tasks_with_filters(request: Request):
|
|
109
330
|
raise HTTPException(status_code=500, detail=str(e))
|
110
331
|
|
111
332
|
|
112
|
-
@router.post(
|
333
|
+
@router.post(
|
334
|
+
"/",
|
335
|
+
summary="创建定时任务",
|
336
|
+
description="创建一个新的定时任务,支持多种调度类型和配置选项",
|
337
|
+
status_code=201,
|
338
|
+
responses={
|
339
|
+
201: {
|
340
|
+
"description": "定时任务创建成功",
|
341
|
+
"content": {
|
342
|
+
"application/json": {
|
343
|
+
"example": {
|
344
|
+
"success": True,
|
345
|
+
"data": {
|
346
|
+
"id": "task-001",
|
347
|
+
"name": "每日数据统计",
|
348
|
+
"namespace": "default",
|
349
|
+
"queue_name": "stat_queue",
|
350
|
+
"schedule_type": "cron",
|
351
|
+
"schedule_config": {"cron": "0 0 * * *"},
|
352
|
+
"is_active": True,
|
353
|
+
"created_at": "2025-10-19T12:00:00Z"
|
354
|
+
},
|
355
|
+
"message": "定时任务创建成功"
|
356
|
+
}
|
357
|
+
}
|
358
|
+
}
|
359
|
+
},
|
360
|
+
400: {
|
361
|
+
"description": "请求参数错误",
|
362
|
+
"content": {
|
363
|
+
"application/json": {
|
364
|
+
"example": {"detail": "调度配置无效: Cron表达式格式错误"}
|
365
|
+
}
|
366
|
+
}
|
367
|
+
},
|
368
|
+
500: {"description": "服务器内部错误"}
|
369
|
+
}
|
370
|
+
)
|
113
371
|
async def create_scheduled_task(request: Request, task_request: ScheduledTaskRequest):
|
114
372
|
"""
|
115
|
-
创建定时任务
|
116
|
-
|
117
|
-
|
118
|
-
|
373
|
+
## 创建定时任务
|
374
|
+
|
375
|
+
创建一个新的定时任务,用于自动化执行周期性或定时的业务逻辑。
|
376
|
+
|
377
|
+
**调度类型说明**:
|
378
|
+
1. **cron**: 使用 Cron 表达式定义复杂的调度规则
|
379
|
+
- 格式: `秒 分 时 日 月 周`
|
380
|
+
- 示例: `0 0 * * *` (每天零点执行)
|
381
|
+
- 支持标准 Cron 语法和扩展语法
|
382
|
+
|
383
|
+
2. **interval**: 固定时间间隔执行
|
384
|
+
- 以秒为单位的间隔时间
|
385
|
+
- 适用于简单的周期性任务
|
386
|
+
- 示例: `3600` (每小时执行一次)
|
387
|
+
|
388
|
+
3. **date**: 指定具体日期时间执行一次
|
389
|
+
- 用于一次性定时任务
|
390
|
+
- 示例: `2025-12-31T23:59:59Z`
|
391
|
+
|
392
|
+
4. **every**: 周期性执行(语义化表达)
|
393
|
+
- 支持: seconds, minutes, hours, days, weeks
|
394
|
+
- 示例: `{"value": 30, "unit": "minutes"}`
|
395
|
+
|
396
|
+
**请求体参数**:
|
397
|
+
```json
|
398
|
+
{
|
399
|
+
"namespace": "default",
|
400
|
+
"name": "每日数据统计",
|
401
|
+
"queue_name": "stat_queue",
|
402
|
+
"task_data": {
|
403
|
+
"function": "tasks.daily_statistics",
|
404
|
+
"args": [],
|
405
|
+
"kwargs": {"date": "today"}
|
406
|
+
},
|
407
|
+
"schedule_type": "cron",
|
408
|
+
"schedule_config": {
|
409
|
+
"cron": "0 0 * * *",
|
410
|
+
"timezone": "Asia/Shanghai"
|
411
|
+
},
|
412
|
+
"is_active": true,
|
413
|
+
"description": "每天零点执行数据统计任务",
|
414
|
+
"max_retry": 3,
|
415
|
+
"timeout": 300
|
416
|
+
}
|
417
|
+
```
|
418
|
+
|
419
|
+
**配置参数说明**:
|
420
|
+
- `namespace`: 命名空间(默认: default)
|
421
|
+
- `name`: 任务名称(必填,唯一标识)
|
422
|
+
- `queue_name`: 目标队列名称
|
423
|
+
- `task_data`: 任务执行数据(function、args、kwargs)
|
424
|
+
- `schedule_type`: 调度类型(cron/interval/date/every)
|
425
|
+
- `schedule_config`: 调度配置(根据类型不同而不同)
|
426
|
+
- `is_active`: 是否立即启用(默认: true)
|
427
|
+
- `description`: 任务描述
|
428
|
+
- `max_retry`: 最大重试次数(默认: 3)
|
429
|
+
- `timeout`: 超时时间(秒,默认: 300)
|
430
|
+
|
431
|
+
**返回信息包括**:
|
432
|
+
- 任务 ID 和基本信息
|
433
|
+
- 调度配置
|
434
|
+
- 下次执行时间
|
435
|
+
- 创建时间
|
436
|
+
|
437
|
+
**使用场景**:
|
438
|
+
- 定期数据统计和报表
|
439
|
+
- 自动化备份任务
|
440
|
+
- 定时清理和维护
|
441
|
+
- 周期性数据同步
|
442
|
+
- 定时消息推送
|
443
|
+
|
444
|
+
**示例请求**:
|
445
|
+
```bash
|
446
|
+
# 创建 Cron 定时任务
|
447
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/" \\
|
448
|
+
-H "Content-Type: application/json" \\
|
449
|
+
-d '{
|
450
|
+
"namespace": "default",
|
451
|
+
"name": "每日数据统计",
|
452
|
+
"queue_name": "stat_queue",
|
453
|
+
"task_data": {
|
454
|
+
"function": "tasks.daily_statistics",
|
455
|
+
"args": [],
|
456
|
+
"kwargs": {"date": "today"}
|
457
|
+
},
|
458
|
+
"schedule_type": "cron",
|
459
|
+
"schedule_config": {
|
460
|
+
"cron": "0 0 * * *",
|
461
|
+
"timezone": "Asia/Shanghai"
|
462
|
+
},
|
463
|
+
"is_active": true,
|
464
|
+
"description": "每天零点执行数据统计",
|
465
|
+
"max_retry": 3,
|
466
|
+
"timeout": 300
|
467
|
+
}'
|
468
|
+
|
469
|
+
# 创建固定间隔任务
|
470
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/" \\
|
471
|
+
-H "Content-Type: application/json" \\
|
472
|
+
-d '{
|
473
|
+
"namespace": "default",
|
474
|
+
"name": "健康检查",
|
475
|
+
"queue_name": "health_queue",
|
476
|
+
"task_data": {
|
477
|
+
"function": "tasks.health_check",
|
478
|
+
"args": [],
|
479
|
+
"kwargs": {}
|
480
|
+
},
|
481
|
+
"schedule_type": "interval",
|
482
|
+
"schedule_config": {
|
483
|
+
"interval": 60
|
484
|
+
},
|
485
|
+
"is_active": true,
|
486
|
+
"description": "每分钟执行健康检查",
|
487
|
+
"max_retry": 1,
|
488
|
+
"timeout": 30
|
489
|
+
}'
|
490
|
+
|
491
|
+
# 创建一次性定时任务
|
492
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/" \\
|
493
|
+
-H "Content-Type: application/json" \\
|
494
|
+
-d '{
|
495
|
+
"namespace": "default",
|
496
|
+
"name": "年度报表生成",
|
497
|
+
"queue_name": "report_queue",
|
498
|
+
"task_data": {
|
499
|
+
"function": "tasks.generate_annual_report",
|
500
|
+
"args": [2025],
|
501
|
+
"kwargs": {}
|
502
|
+
},
|
503
|
+
"schedule_type": "date",
|
504
|
+
"schedule_config": {
|
505
|
+
"run_date": "2025-12-31T23:59:00Z"
|
506
|
+
},
|
507
|
+
"is_active": true,
|
508
|
+
"description": "年底生成年度报表"
|
509
|
+
}'
|
510
|
+
```
|
511
|
+
|
512
|
+
**注意事项**:
|
513
|
+
- 任务名称在同一命名空间内必须唯一
|
514
|
+
- Cron 表达式会在创建时验证格式
|
515
|
+
- 确保目标队列已存在且可用
|
516
|
+
- 任务函数路径必须可导入
|
517
|
+
- 时区默认为 UTC,建议明确指定
|
518
|
+
- 创建后任务会根据调度配置自动计算下次执行时间
|
519
|
+
- 如果 is_active 为 false,任务不会自动执行
|
119
520
|
"""
|
120
521
|
try:
|
121
522
|
app = request.app
|
@@ -158,14 +559,176 @@ async def create_scheduled_task(request: Request, task_request: ScheduledTaskReq
|
|
158
559
|
raise HTTPException(status_code=500, detail=str(e))
|
159
560
|
|
160
561
|
|
161
|
-
@router.put(
|
562
|
+
@router.put(
|
563
|
+
"/{task_id}",
|
564
|
+
summary="更新定时任务",
|
565
|
+
description="更新指定定时任务的配置信息,支持部分更新",
|
566
|
+
responses={
|
567
|
+
200: {
|
568
|
+
"description": "定时任务更新成功",
|
569
|
+
"content": {
|
570
|
+
"application/json": {
|
571
|
+
"example": {
|
572
|
+
"success": True,
|
573
|
+
"data": {
|
574
|
+
"id": "task-001",
|
575
|
+
"name": "每日数据统计(已更新)",
|
576
|
+
"namespace": "default",
|
577
|
+
"queue_name": "stat_queue",
|
578
|
+
"schedule_type": "cron",
|
579
|
+
"schedule_config": {"cron": "0 1 * * *"},
|
580
|
+
"is_active": True,
|
581
|
+
"updated_at": "2025-10-19T12:30:00Z"
|
582
|
+
},
|
583
|
+
"message": "定时任务更新成功"
|
584
|
+
}
|
585
|
+
}
|
586
|
+
}
|
587
|
+
},
|
588
|
+
400: {
|
589
|
+
"description": "请求参数错误",
|
590
|
+
"content": {
|
591
|
+
"application/json": {
|
592
|
+
"example": {"detail": "调度配置无效"}
|
593
|
+
}
|
594
|
+
}
|
595
|
+
},
|
596
|
+
404: {
|
597
|
+
"description": "任务不存在",
|
598
|
+
"content": {
|
599
|
+
"application/json": {
|
600
|
+
"example": {"detail": "定时任务不存在"}
|
601
|
+
}
|
602
|
+
}
|
603
|
+
},
|
604
|
+
500: {"description": "服务器内部错误"}
|
605
|
+
}
|
606
|
+
)
|
162
607
|
async def update_scheduled_task(request: Request, task_id: str, task_request: ScheduledTaskRequest):
|
163
608
|
"""
|
164
|
-
更新定时任务
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
609
|
+
## 更新定时任务
|
610
|
+
|
611
|
+
更新指定定时任务的配置信息,可以修改调度规则、任务数据、执行参数等。
|
612
|
+
|
613
|
+
**可更新的字段**:
|
614
|
+
- 任务名称和描述
|
615
|
+
- 命名空间和队列名称
|
616
|
+
- 调度类型和配置
|
617
|
+
- 任务执行数据(function、args、kwargs)
|
618
|
+
- 启用状态
|
619
|
+
- 重试次数和超时时间
|
620
|
+
|
621
|
+
**请求体参数**:
|
622
|
+
```json
|
623
|
+
{
|
624
|
+
"namespace": "default",
|
625
|
+
"name": "每日数据统计(已更新)",
|
626
|
+
"queue_name": "stat_queue",
|
627
|
+
"task_data": {
|
628
|
+
"function": "tasks.daily_statistics",
|
629
|
+
"args": [],
|
630
|
+
"kwargs": {"date": "today", "format": "detailed"}
|
631
|
+
},
|
632
|
+
"schedule_type": "cron",
|
633
|
+
"schedule_config": {
|
634
|
+
"cron": "0 1 * * *",
|
635
|
+
"timezone": "Asia/Shanghai"
|
636
|
+
},
|
637
|
+
"is_active": true,
|
638
|
+
"description": "每天凌晨1点执行详细数据统计",
|
639
|
+
"max_retry": 5,
|
640
|
+
"timeout": 600
|
641
|
+
}
|
642
|
+
```
|
643
|
+
|
644
|
+
**返回信息包括**:
|
645
|
+
- 更新后的完整任务信息
|
646
|
+
- 新的下次执行时间
|
647
|
+
- 更新时间戳
|
648
|
+
|
649
|
+
**使用场景**:
|
650
|
+
- 调整任务执行时间
|
651
|
+
- 修改任务参数
|
652
|
+
- 更新调度策略
|
653
|
+
- 临时禁用或启用任务
|
654
|
+
- 调整超时和重试配置
|
655
|
+
|
656
|
+
**示例请求**:
|
657
|
+
```bash
|
658
|
+
# 更新任务执行时间
|
659
|
+
curl -X PUT "http://localhost:8001/api/v1/scheduled/task-001" \\
|
660
|
+
-H "Content-Type: application/json" \\
|
661
|
+
-d '{
|
662
|
+
"namespace": "default",
|
663
|
+
"name": "每日数据统计",
|
664
|
+
"queue_name": "stat_queue",
|
665
|
+
"task_data": {
|
666
|
+
"function": "tasks.daily_statistics",
|
667
|
+
"args": [],
|
668
|
+
"kwargs": {"date": "today"}
|
669
|
+
},
|
670
|
+
"schedule_type": "cron",
|
671
|
+
"schedule_config": {
|
672
|
+
"cron": "0 1 * * *",
|
673
|
+
"timezone": "Asia/Shanghai"
|
674
|
+
},
|
675
|
+
"is_active": true,
|
676
|
+
"description": "调整为凌晨1点执行"
|
677
|
+
}'
|
678
|
+
|
679
|
+
# 修改为间隔调度
|
680
|
+
curl -X PUT "http://localhost:8001/api/v1/scheduled/task-001" \\
|
681
|
+
-H "Content-Type: application/json" \\
|
682
|
+
-d '{
|
683
|
+
"namespace": "default",
|
684
|
+
"name": "数据统计",
|
685
|
+
"queue_name": "stat_queue",
|
686
|
+
"task_data": {
|
687
|
+
"function": "tasks.statistics",
|
688
|
+
"args": [],
|
689
|
+
"kwargs": {}
|
690
|
+
},
|
691
|
+
"schedule_type": "interval",
|
692
|
+
"schedule_config": {
|
693
|
+
"interval": 7200
|
694
|
+
},
|
695
|
+
"is_active": true,
|
696
|
+
"description": "改为每2小时执行一次"
|
697
|
+
}'
|
698
|
+
|
699
|
+
# 更新任务参数
|
700
|
+
curl -X PUT "http://localhost:8001/api/v1/scheduled/task-001" \\
|
701
|
+
-H "Content-Type: application/json" \\
|
702
|
+
-d '{
|
703
|
+
"namespace": "default",
|
704
|
+
"name": "每日数据统计",
|
705
|
+
"queue_name": "stat_queue",
|
706
|
+
"task_data": {
|
707
|
+
"function": "tasks.daily_statistics",
|
708
|
+
"args": [],
|
709
|
+
"kwargs": {
|
710
|
+
"date": "today",
|
711
|
+
"include_details": true,
|
712
|
+
"send_email": true
|
713
|
+
}
|
714
|
+
},
|
715
|
+
"schedule_type": "cron",
|
716
|
+
"schedule_config": {
|
717
|
+
"cron": "0 0 * * *",
|
718
|
+
"timezone": "Asia/Shanghai"
|
719
|
+
},
|
720
|
+
"is_active": true,
|
721
|
+
"description": "增加邮件通知功能"
|
722
|
+
}'
|
723
|
+
```
|
724
|
+
|
725
|
+
**注意事项**:
|
726
|
+
- 所有字段都必须提供完整值(非部分更新)
|
727
|
+
- 更新调度配置后会重新计算下次执行时间
|
728
|
+
- 如果任务正在执行,更新会在执行完成后生效
|
729
|
+
- 修改 schedule_type 时必须同时提供对应的 schedule_config
|
730
|
+
- 更新后的任务名称不能与其他任务重复
|
731
|
+
- 调度配置会立即验证,无效配置会返回 400 错误
|
169
732
|
"""
|
170
733
|
try:
|
171
734
|
app = request.app
|
@@ -208,13 +771,68 @@ async def update_scheduled_task(request: Request, task_id: str, task_request: Sc
|
|
208
771
|
raise HTTPException(status_code=500, detail=str(e))
|
209
772
|
|
210
773
|
|
211
|
-
@router.delete(
|
774
|
+
@router.delete(
|
775
|
+
"/{task_id}",
|
776
|
+
summary="删除定时任务",
|
777
|
+
description="删除指定的定时任务,操作不可逆",
|
778
|
+
responses={
|
779
|
+
200: {
|
780
|
+
"description": "定时任务删除成功",
|
781
|
+
"content": {
|
782
|
+
"application/json": {
|
783
|
+
"example": {
|
784
|
+
"success": True,
|
785
|
+
"message": "定时任务 task-001 已删除"
|
786
|
+
}
|
787
|
+
}
|
788
|
+
}
|
789
|
+
},
|
790
|
+
404: {
|
791
|
+
"description": "任务不存在",
|
792
|
+
"content": {
|
793
|
+
"application/json": {
|
794
|
+
"example": {"detail": "定时任务不存在"}
|
795
|
+
}
|
796
|
+
}
|
797
|
+
},
|
798
|
+
500: {"description": "服务器内部错误"}
|
799
|
+
}
|
800
|
+
)
|
212
801
|
async def delete_scheduled_task(request: Request, task_id: str):
|
213
802
|
"""
|
214
|
-
删除定时任务
|
215
|
-
|
216
|
-
|
217
|
-
|
803
|
+
## 删除定时任务
|
804
|
+
|
805
|
+
删除指定的定时任务,包括任务配置和调度信息。
|
806
|
+
|
807
|
+
**删除影响**:
|
808
|
+
- 任务配置将被永久删除
|
809
|
+
- 调度器将停止该任务的自动执行
|
810
|
+
- 任务执行历史仍会保留(不会删除)
|
811
|
+
- 如果任务正在执行,不会中断当前执行
|
812
|
+
|
813
|
+
**返回信息包括**:
|
814
|
+
- 操作成功标识
|
815
|
+
- 删除确认消息
|
816
|
+
|
817
|
+
**使用场景**:
|
818
|
+
- 清理不再需要的定时任务
|
819
|
+
- 移除测试任务
|
820
|
+
- 任务下线
|
821
|
+
- 系统维护和清理
|
822
|
+
|
823
|
+
**示例请求**:
|
824
|
+
```bash
|
825
|
+
# 删除指定定时任务
|
826
|
+
curl -X DELETE "http://localhost:8001/api/v1/scheduled/task-001"
|
827
|
+
```
|
828
|
+
|
829
|
+
**注意事项**:
|
830
|
+
- 删除操作不可逆,请谨慎操作
|
831
|
+
- 建议删除前先禁用任务,观察一段时间
|
832
|
+
- 如果任务正在执行,删除后不会中断当前执行
|
833
|
+
- 任务的历史执行记录不会被删除
|
834
|
+
- 删除失败时会返回 404 错误
|
835
|
+
- 建议在删除前导出重要的任务配置
|
218
836
|
"""
|
219
837
|
try:
|
220
838
|
app = request.app
|
@@ -240,13 +858,97 @@ async def delete_scheduled_task(request: Request, task_id: str):
|
|
240
858
|
raise HTTPException(status_code=500, detail=str(e))
|
241
859
|
|
242
860
|
|
243
|
-
@router.post(
|
861
|
+
@router.post(
|
862
|
+
"/{task_id}/toggle",
|
863
|
+
summary="启用/禁用定时任务",
|
864
|
+
description="切换定时任务的启用状态,启用的任务会自动执行,禁用的任务会暂停调度",
|
865
|
+
responses={
|
866
|
+
200: {
|
867
|
+
"description": "任务状态切换成功",
|
868
|
+
"content": {
|
869
|
+
"application/json": {
|
870
|
+
"example": {
|
871
|
+
"success": True,
|
872
|
+
"data": {
|
873
|
+
"id": "task-001",
|
874
|
+
"is_active": False
|
875
|
+
},
|
876
|
+
"message": "定时任务状态已更新"
|
877
|
+
}
|
878
|
+
}
|
879
|
+
}
|
880
|
+
},
|
881
|
+
404: {
|
882
|
+
"description": "任务不存在",
|
883
|
+
"content": {
|
884
|
+
"application/json": {
|
885
|
+
"example": {"detail": "定时任务不存在"}
|
886
|
+
}
|
887
|
+
}
|
888
|
+
},
|
889
|
+
500: {"description": "服务器内部错误"}
|
890
|
+
}
|
891
|
+
)
|
244
892
|
async def toggle_scheduled_task(request: Request, task_id: str):
|
245
893
|
"""
|
246
|
-
启用/禁用定时任务
|
247
|
-
|
248
|
-
|
249
|
-
|
894
|
+
## 启用/禁用定时任务
|
895
|
+
|
896
|
+
切换定时任务的启用状态,用于快速暂停或恢复任务的自动执行。
|
897
|
+
|
898
|
+
**状态说明**:
|
899
|
+
- **启用(is_active=true)**: 任务会按照调度配置自动执行
|
900
|
+
- **禁用(is_active=false)**: 任务暂停调度,不会自动执行
|
901
|
+
|
902
|
+
**操作特点**:
|
903
|
+
- 切换操作是幂等的(启用状态下再次启用不会报错)
|
904
|
+
- 禁用任务不会中断正在执行的实例
|
905
|
+
- 启用任务会立即重新计算下次执行时间
|
906
|
+
- 保留所有任务配置和历史记录
|
907
|
+
|
908
|
+
**返回信息包括**:
|
909
|
+
- 任务 ID
|
910
|
+
- 更新后的启用状态
|
911
|
+
- 操作确认消息
|
912
|
+
|
913
|
+
**使用场景**:
|
914
|
+
- 临时暂停任务执行
|
915
|
+
- 维护期间禁用定时任务
|
916
|
+
- 快速恢复任务调度
|
917
|
+
- 测试和调试
|
918
|
+
- 紧急情况下快速停止任务
|
919
|
+
|
920
|
+
**示例请求**:
|
921
|
+
```bash
|
922
|
+
# 切换任务状态(启用↔禁用)
|
923
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/toggle"
|
924
|
+
```
|
925
|
+
|
926
|
+
**工作流示例**:
|
927
|
+
```bash
|
928
|
+
# 场景1: 维护期间暂停任务
|
929
|
+
# 1. 禁用任务
|
930
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/toggle"
|
931
|
+
# 2. 执行系统维护
|
932
|
+
# 3. 维护完成后恢复任务
|
933
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/toggle"
|
934
|
+
|
935
|
+
# 场景2: 配合更新操作
|
936
|
+
# 1. 先禁用任务
|
937
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/toggle"
|
938
|
+
# 2. 更新任务配置
|
939
|
+
curl -X PUT "http://localhost:8001/api/v1/scheduled/task-001" \\
|
940
|
+
-H "Content-Type: application/json" \\
|
941
|
+
-d '{...}'
|
942
|
+
# 3. 测试无误后启用任务
|
943
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/toggle"
|
944
|
+
```
|
945
|
+
|
946
|
+
**注意事项**:
|
947
|
+
- 禁用任务不会删除任务配置
|
948
|
+
- 禁用期间的执行历史仍会保留
|
949
|
+
- 启用后会从当前时间重新计算下次执行时间
|
950
|
+
- 如果任务正在执行,禁用操作不会中断当前执行
|
951
|
+
- 频繁切换状态不会影响任务的正常运行
|
250
952
|
"""
|
251
953
|
try:
|
252
954
|
app = request.app
|
@@ -276,13 +978,124 @@ async def toggle_scheduled_task(request: Request, task_id: str):
|
|
276
978
|
raise HTTPException(status_code=500, detail=str(e))
|
277
979
|
|
278
980
|
|
279
|
-
@router.post(
|
981
|
+
@router.post(
|
982
|
+
"/{task_id}/execute",
|
983
|
+
summary="立即执行定时任务",
|
984
|
+
description="手动触发定时任务立即执行一次,不影响原有调度计划",
|
985
|
+
responses={
|
986
|
+
200: {
|
987
|
+
"description": "任务已加入执行队列",
|
988
|
+
"content": {
|
989
|
+
"application/json": {
|
990
|
+
"example": {
|
991
|
+
"success": True,
|
992
|
+
"data": {
|
993
|
+
"task_id": "task-001",
|
994
|
+
"job_id": "job-123456",
|
995
|
+
"queue_name": "stat_queue",
|
996
|
+
"status": "queued"
|
997
|
+
},
|
998
|
+
"message": "定时任务已加入执行队列"
|
999
|
+
}
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
},
|
1003
|
+
400: {
|
1004
|
+
"description": "请求参数错误",
|
1005
|
+
"content": {
|
1006
|
+
"application/json": {
|
1007
|
+
"example": {"detail": "任务已禁用,无法执行"}
|
1008
|
+
}
|
1009
|
+
}
|
1010
|
+
},
|
1011
|
+
404: {
|
1012
|
+
"description": "任务不存在",
|
1013
|
+
"content": {
|
1014
|
+
"application/json": {
|
1015
|
+
"example": {"detail": "定时任务不存在"}
|
1016
|
+
}
|
1017
|
+
}
|
1018
|
+
},
|
1019
|
+
500: {"description": "服务器内部错误"}
|
1020
|
+
}
|
1021
|
+
)
|
280
1022
|
async def execute_scheduled_task_now(request: Request, task_id: str):
|
281
1023
|
"""
|
282
|
-
立即执行定时任务
|
283
|
-
|
284
|
-
|
285
|
-
|
1024
|
+
## 立即执行定时任务
|
1025
|
+
|
1026
|
+
手动触发定时任务立即执行一次,用于测试、紧急执行或补偿性执行。
|
1027
|
+
|
1028
|
+
**执行特点**:
|
1029
|
+
- 不影响原有的调度计划
|
1030
|
+
- 任务会立即加入执行队列
|
1031
|
+
- 可以多次手动触发(不受调度限制)
|
1032
|
+
- 禁用状态的任务也可以手动执行
|
1033
|
+
- 执行结果会记录到执行历史
|
1034
|
+
|
1035
|
+
**执行流程**:
|
1036
|
+
1. 验证任务是否存在
|
1037
|
+
2. 读取任务配置(function、args、kwargs)
|
1038
|
+
3. 创建执行任务并加入队列
|
1039
|
+
4. 返回任务 ID 和队列信息
|
1040
|
+
5. 后台 Worker 异步执行任务
|
1041
|
+
|
1042
|
+
**返回信息包括**:
|
1043
|
+
- 定时任务 ID
|
1044
|
+
- 执行任务 ID(job_id)
|
1045
|
+
- 目标队列名称
|
1046
|
+
- 任务状态(queued)
|
1047
|
+
|
1048
|
+
**使用场景**:
|
1049
|
+
- 测试定时任务配置
|
1050
|
+
- 紧急执行未到时间的任务
|
1051
|
+
- 补偿执行失败的任务
|
1052
|
+
- 手动触发数据处理
|
1053
|
+
- 调试和故障排查
|
1054
|
+
|
1055
|
+
**示例请求**:
|
1056
|
+
```bash
|
1057
|
+
# 立即执行定时任务
|
1058
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/execute"
|
1059
|
+
```
|
1060
|
+
|
1061
|
+
**应用场景示例**:
|
1062
|
+
```bash
|
1063
|
+
# 场景1: 测试新创建的定时任务
|
1064
|
+
# 1. 创建定时任务
|
1065
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/" \\
|
1066
|
+
-H "Content-Type: application/json" \\
|
1067
|
+
-d '{
|
1068
|
+
"name": "每日报表",
|
1069
|
+
"schedule_type": "cron",
|
1070
|
+
"schedule_config": {"cron": "0 0 * * *"},
|
1071
|
+
...
|
1072
|
+
}'
|
1073
|
+
# 2. 立即执行测试
|
1074
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/execute"
|
1075
|
+
|
1076
|
+
# 场景2: 紧急执行数据处理
|
1077
|
+
# 1. 查看任务状态
|
1078
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/?search=数据处理"
|
1079
|
+
# 2. 立即执行
|
1080
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/execute"
|
1081
|
+
# 3. 查看执行历史
|
1082
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history"
|
1083
|
+
|
1084
|
+
# 场景3: 补偿执行失败任务
|
1085
|
+
# 1. 查看失败历史
|
1086
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history"
|
1087
|
+
# 2. 修复问题后重新执行
|
1088
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/execute"
|
1089
|
+
```
|
1090
|
+
|
1091
|
+
**注意事项**:
|
1092
|
+
- 立即执行不会影响下次调度时间
|
1093
|
+
- 任务会按照配置的 max_retry 和 timeout 参数执行
|
1094
|
+
- 如果队列积压严重,可能需要等待一段时间才能执行
|
1095
|
+
- 即使任务被禁用,仍然可以手动执行
|
1096
|
+
- 执行结果可以通过执行历史接口查询
|
1097
|
+
- 频繁手动执行可能导致队列积压,请谨慎使用
|
1098
|
+
- 返回 200 状态码仅表示任务已加入队列,不代表执行成功
|
286
1099
|
"""
|
287
1100
|
try:
|
288
1101
|
app = request.app
|
@@ -314,20 +1127,134 @@ async def execute_scheduled_task_now(request: Request, task_id: str):
|
|
314
1127
|
|
315
1128
|
# ============ 执行历史和统计 ============
|
316
1129
|
|
317
|
-
@router.get(
|
1130
|
+
@router.get(
|
1131
|
+
"/{task_id}/history",
|
1132
|
+
summary="获取定时任务执行历史",
|
1133
|
+
description="获取指定定时任务的执行历史记录,支持分页查询",
|
1134
|
+
responses={
|
1135
|
+
200: {
|
1136
|
+
"description": "成功返回执行历史",
|
1137
|
+
"content": {
|
1138
|
+
"application/json": {
|
1139
|
+
"example": {
|
1140
|
+
"success": True,
|
1141
|
+
"data": [
|
1142
|
+
{
|
1143
|
+
"id": "exec-001",
|
1144
|
+
"task_id": "task-001",
|
1145
|
+
"job_id": "job-123456",
|
1146
|
+
"status": "completed",
|
1147
|
+
"start_time": "2025-10-19T00:00:00Z",
|
1148
|
+
"end_time": "2025-10-19T00:05:30Z",
|
1149
|
+
"duration": 330,
|
1150
|
+
"result": "success",
|
1151
|
+
"error_message": None
|
1152
|
+
},
|
1153
|
+
{
|
1154
|
+
"id": "exec-002",
|
1155
|
+
"task_id": "task-001",
|
1156
|
+
"job_id": "job-123455",
|
1157
|
+
"status": "failed",
|
1158
|
+
"start_time": "2025-10-18T00:00:00Z",
|
1159
|
+
"end_time": "2025-10-18T00:00:30Z",
|
1160
|
+
"duration": 30,
|
1161
|
+
"result": "error",
|
1162
|
+
"error_message": "Database connection timeout"
|
1163
|
+
}
|
1164
|
+
],
|
1165
|
+
"total": 150,
|
1166
|
+
"page": 1,
|
1167
|
+
"page_size": 20
|
1168
|
+
}
|
1169
|
+
}
|
1170
|
+
}
|
1171
|
+
},
|
1172
|
+
404: {
|
1173
|
+
"description": "任务不存在",
|
1174
|
+
"content": {
|
1175
|
+
"application/json": {
|
1176
|
+
"example": {"detail": "定时任务不存在"}
|
1177
|
+
}
|
1178
|
+
}
|
1179
|
+
},
|
1180
|
+
500: {"description": "服务器内部错误"}
|
1181
|
+
}
|
1182
|
+
)
|
318
1183
|
async def get_scheduled_task_history(
|
319
1184
|
request: Request,
|
320
1185
|
task_id: str,
|
321
|
-
page: int = Query(1, ge=1),
|
322
|
-
page_size: int = Query(20, ge=1, le=100)
|
1186
|
+
page: int = Query(1, ge=1, description="页码,从 1 开始", example=1),
|
1187
|
+
page_size: int = Query(20, ge=1, le=100, description="每页数量,范围 1-100", example=20)
|
323
1188
|
):
|
324
1189
|
"""
|
325
|
-
获取定时任务执行历史
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
1190
|
+
## 获取定时任务执行历史
|
1191
|
+
|
1192
|
+
获取指定定时任务的所有执行历史记录,包括成功、失败和运行中的执行记录。
|
1193
|
+
|
1194
|
+
**返回信息包括**:
|
1195
|
+
- 执行记录 ID
|
1196
|
+
- 任务 ID 和任务 ID
|
1197
|
+
- 执行状态(completed/failed/running)
|
1198
|
+
- 开始时间和结束时间
|
1199
|
+
- 执行时长(秒)
|
1200
|
+
- 执行结果
|
1201
|
+
- 错误信息(如果失败)
|
1202
|
+
|
1203
|
+
**执行状态说明**:
|
1204
|
+
- `queued`: 已加入队列,等待执行
|
1205
|
+
- `running`: 正在执行中
|
1206
|
+
- `completed`: 执行成功完成
|
1207
|
+
- `failed`: 执行失败
|
1208
|
+
- `timeout`: 执行超时
|
1209
|
+
- `cancelled`: 已取消
|
1210
|
+
|
1211
|
+
**历史记录特点**:
|
1212
|
+
- 按执行时间倒序排列(最新的在前)
|
1213
|
+
- 包含手动触发和自动调度的执行记录
|
1214
|
+
- 失败记录包含详细的错误信息
|
1215
|
+
- 执行时长精确到秒
|
1216
|
+
|
1217
|
+
**使用场景**:
|
1218
|
+
- 查看任务执行情况
|
1219
|
+
- 分析任务失败原因
|
1220
|
+
- 统计任务执行时长
|
1221
|
+
- 监控任务健康度
|
1222
|
+
- 故障排查和审计
|
1223
|
+
|
1224
|
+
**示例请求**:
|
1225
|
+
```bash
|
1226
|
+
# 获取执行历史(第一页)
|
1227
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history?page=1&page_size=20"
|
1228
|
+
|
1229
|
+
# 获取更多历史记录
|
1230
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history?page=2&page_size=50"
|
1231
|
+
|
1232
|
+
# 查看最近10条执行记录
|
1233
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history?page=1&page_size=10"
|
1234
|
+
```
|
1235
|
+
|
1236
|
+
**配合其他接口使用**:
|
1237
|
+
```bash
|
1238
|
+
# 查看任务详情
|
1239
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/?search=数据统计"
|
1240
|
+
|
1241
|
+
# 查看执行历史
|
1242
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history"
|
1243
|
+
|
1244
|
+
# 如果发现失败,立即重新执行
|
1245
|
+
curl -X POST "http://localhost:8001/api/v1/scheduled/task-001/execute"
|
1246
|
+
|
1247
|
+
# 查看执行趋势
|
1248
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/trend?time_range=7d"
|
1249
|
+
```
|
1250
|
+
|
1251
|
+
**注意事项**:
|
1252
|
+
- 历史记录会永久保留,建议定期清理旧数据
|
1253
|
+
- 分页从 1 开始
|
1254
|
+
- 每页最多返回 100 条记录
|
1255
|
+
- 执行时长为实际执行时间,不包括排队等待时间
|
1256
|
+
- 手动触发的执行也会记录在历史中
|
1257
|
+
- 错误信息可能包含敏感信息,请注意安全
|
331
1258
|
"""
|
332
1259
|
try:
|
333
1260
|
app = request.app
|
@@ -356,18 +1283,146 @@ async def get_scheduled_task_history(
|
|
356
1283
|
raise HTTPException(status_code=500, detail=str(e))
|
357
1284
|
|
358
1285
|
|
359
|
-
@router.get(
|
1286
|
+
@router.get(
|
1287
|
+
"/{task_id}/trend",
|
1288
|
+
summary="获取定时任务执行趋势",
|
1289
|
+
description="获取指定定时任务的执行趋势数据,用于可视化分析和监控",
|
1290
|
+
responses={
|
1291
|
+
200: {
|
1292
|
+
"description": "成功返回执行趋势数据",
|
1293
|
+
"content": {
|
1294
|
+
"application/json": {
|
1295
|
+
"example": {
|
1296
|
+
"success": True,
|
1297
|
+
"data": {
|
1298
|
+
"task_id": "task-001",
|
1299
|
+
"time_points": [
|
1300
|
+
"2025-10-13T00:00:00Z",
|
1301
|
+
"2025-10-14T00:00:00Z",
|
1302
|
+
"2025-10-15T00:00:00Z",
|
1303
|
+
"2025-10-16T00:00:00Z",
|
1304
|
+
"2025-10-17T00:00:00Z",
|
1305
|
+
"2025-10-18T00:00:00Z",
|
1306
|
+
"2025-10-19T00:00:00Z"
|
1307
|
+
],
|
1308
|
+
"success_count": [10, 12, 11, 13, 12, 14, 15],
|
1309
|
+
"failed_count": [1, 0, 2, 0, 1, 0, 0],
|
1310
|
+
"avg_duration": [320, 315, 330, 310, 325, 318, 312],
|
1311
|
+
"total_executions": 91,
|
1312
|
+
"success_rate": 95.6
|
1313
|
+
},
|
1314
|
+
"time_range": "7d"
|
1315
|
+
}
|
1316
|
+
}
|
1317
|
+
}
|
1318
|
+
},
|
1319
|
+
400: {
|
1320
|
+
"description": "请求参数错误",
|
1321
|
+
"content": {
|
1322
|
+
"application/json": {
|
1323
|
+
"example": {"detail": "无效的时间范围参数"}
|
1324
|
+
}
|
1325
|
+
}
|
1326
|
+
},
|
1327
|
+
404: {
|
1328
|
+
"description": "任务不存在",
|
1329
|
+
"content": {
|
1330
|
+
"application/json": {
|
1331
|
+
"example": {"detail": "定时任务不存在"}
|
1332
|
+
}
|
1333
|
+
}
|
1334
|
+
},
|
1335
|
+
500: {"description": "服务器内部错误"}
|
1336
|
+
}
|
1337
|
+
)
|
360
1338
|
async def get_scheduled_task_execution_trend(
|
361
1339
|
request: Request,
|
362
1340
|
task_id: str,
|
363
|
-
time_range: str = Query("7d", description="
|
1341
|
+
time_range: str = Query("7d", description="时间范围,支持: 1h, 24h, 7d, 30d, 90d", example="7d")
|
364
1342
|
):
|
365
1343
|
"""
|
366
|
-
获取定时任务执行趋势
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
1344
|
+
## 获取定时任务执行趋势
|
1345
|
+
|
1346
|
+
获取指定定时任务在特定时间范围内的执行趋势数据,用于监控和分析任务执行情况。
|
1347
|
+
|
1348
|
+
**返回数据包括**:
|
1349
|
+
- 时间点序列(X 轴)
|
1350
|
+
- 成功执行次数(按时间点统计)
|
1351
|
+
- 失败执行次数(按时间点统计)
|
1352
|
+
- 平均执行时长(秒,按时间点统计)
|
1353
|
+
- 总执行次数
|
1354
|
+
- 成功率(百分比)
|
1355
|
+
|
1356
|
+
**时间范围参数**:
|
1357
|
+
- `1h`: 最近1小时(按分钟聚合)
|
1358
|
+
- `24h`: 最近24小时(按小时聚合)
|
1359
|
+
- `7d`: 最近7天(按天聚合)
|
1360
|
+
- `30d`: 最近30天(按天聚合)
|
1361
|
+
- `90d`: 最近90天(按周聚合)
|
1362
|
+
|
1363
|
+
**数据聚合规则**:
|
1364
|
+
- 1小时范围:按分钟聚合,返回60个数据点
|
1365
|
+
- 24小时范围:按小时聚合,返回24个数据点
|
1366
|
+
- 7天范围:按天聚合,返回7个数据点
|
1367
|
+
- 30天范围:按天聚合,返回30个数据点
|
1368
|
+
- 90天范围:按周聚合,返回13个数据点
|
1369
|
+
|
1370
|
+
**使用场景**:
|
1371
|
+
- 任务执行情况可视化
|
1372
|
+
- 性能趋势分析
|
1373
|
+
- 故障模式识别
|
1374
|
+
- 容量规划
|
1375
|
+
- SLA 监控
|
1376
|
+
|
1377
|
+
**示例请求**:
|
1378
|
+
```bash
|
1379
|
+
# 查看最近7天的执行趋势
|
1380
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/trend?time_range=7d"
|
1381
|
+
|
1382
|
+
# 查看最近24小时的执行趋势
|
1383
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/trend?time_range=24h"
|
1384
|
+
|
1385
|
+
# 查看最近30天的执行趋势
|
1386
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/trend?time_range=30d"
|
1387
|
+
```
|
1388
|
+
|
1389
|
+
**数据可视化示例**:
|
1390
|
+
```bash
|
1391
|
+
# 获取趋势数据并用于图表展示
|
1392
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/trend?time_range=7d" \\
|
1393
|
+
| jq '.data'
|
1394
|
+
|
1395
|
+
# 返回的数据可以直接用于前端图表库(如 ECharts、Chart.js)
|
1396
|
+
# X轴: time_points
|
1397
|
+
# Y轴(折线图1): success_count (成功次数)
|
1398
|
+
# Y轴(折线图2): failed_count (失败次数)
|
1399
|
+
# Y轴(折线图3): avg_duration (平均时长)
|
1400
|
+
```
|
1401
|
+
|
1402
|
+
**配合其他接口使用**:
|
1403
|
+
```bash
|
1404
|
+
# 1. 查看任务列表
|
1405
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/"
|
1406
|
+
|
1407
|
+
# 2. 查看执行趋势(发现异常)
|
1408
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/trend?time_range=7d"
|
1409
|
+
|
1410
|
+
# 3. 查看详细执行历史
|
1411
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history"
|
1412
|
+
|
1413
|
+
# 4. 如果发现问题,更新任务配置
|
1414
|
+
curl -X PUT "http://localhost:8001/api/v1/scheduled/task-001" \\
|
1415
|
+
-H "Content-Type: application/json" \\
|
1416
|
+
-d '{...}'
|
1417
|
+
```
|
1418
|
+
|
1419
|
+
**注意事项**:
|
1420
|
+
- 时间范围参数必须是支持的值之一
|
1421
|
+
- 趋势数据基于历史执行记录计算
|
1422
|
+
- 如果时间范围内没有执行记录,对应数据点为 0
|
1423
|
+
- 平均时长只计算成功执行的任务
|
1424
|
+
- 成功率 = (成功次数 / 总执行次数) × 100%
|
1425
|
+
- 数据会实时计算,可能有轻微延迟
|
371
1426
|
"""
|
372
1427
|
try:
|
373
1428
|
app = request.app
|
@@ -393,13 +1448,151 @@ async def get_scheduled_task_execution_trend(
|
|
393
1448
|
raise HTTPException(status_code=500, detail=str(e))
|
394
1449
|
|
395
1450
|
|
396
|
-
@router.get(
|
1451
|
+
@router.get(
|
1452
|
+
"/statistics/{namespace}",
|
1453
|
+
summary="获取定时任务统计数据",
|
1454
|
+
description="获取指定命名空间下所有定时任务的统计数据和概览信息",
|
1455
|
+
responses={
|
1456
|
+
200: {
|
1457
|
+
"description": "成功返回统计数据",
|
1458
|
+
"content": {
|
1459
|
+
"application/json": {
|
1460
|
+
"example": {
|
1461
|
+
"success": True,
|
1462
|
+
"data": {
|
1463
|
+
"namespace": "default",
|
1464
|
+
"total_tasks": 25,
|
1465
|
+
"active_tasks": 18,
|
1466
|
+
"inactive_tasks": 7,
|
1467
|
+
"total_executions_today": 156,
|
1468
|
+
"successful_executions_today": 148,
|
1469
|
+
"failed_executions_today": 8,
|
1470
|
+
"success_rate_today": 94.9,
|
1471
|
+
"avg_execution_duration": 285,
|
1472
|
+
"next_executions": [
|
1473
|
+
{
|
1474
|
+
"task_id": "task-001",
|
1475
|
+
"task_name": "每日数据统计",
|
1476
|
+
"next_run_time": "2025-10-20T00:00:00Z"
|
1477
|
+
},
|
1478
|
+
{
|
1479
|
+
"task_id": "task-002",
|
1480
|
+
"task_name": "健康检查",
|
1481
|
+
"next_run_time": "2025-10-19T13:00:00Z"
|
1482
|
+
}
|
1483
|
+
],
|
1484
|
+
"task_type_distribution": {
|
1485
|
+
"cron": 15,
|
1486
|
+
"interval": 8,
|
1487
|
+
"date": 2
|
1488
|
+
}
|
1489
|
+
}
|
1490
|
+
}
|
1491
|
+
}
|
1492
|
+
}
|
1493
|
+
},
|
1494
|
+
404: {
|
1495
|
+
"description": "命名空间不存在",
|
1496
|
+
"content": {
|
1497
|
+
"application/json": {
|
1498
|
+
"example": {"detail": "命名空间 'production' 不存在"}
|
1499
|
+
}
|
1500
|
+
}
|
1501
|
+
},
|
1502
|
+
500: {"description": "服务器内部错误"}
|
1503
|
+
}
|
1504
|
+
)
|
397
1505
|
async def get_scheduled_tasks_statistics(request: Request, namespace: str):
|
398
1506
|
"""
|
399
|
-
获取定时任务统计数据
|
400
|
-
|
401
|
-
|
402
|
-
|
1507
|
+
## 获取定时任务统计数据
|
1508
|
+
|
1509
|
+
获取指定命名空间下所有定时任务的统计信息和运行概览。
|
1510
|
+
|
1511
|
+
**统计数据包括**:
|
1512
|
+
- 任务总数、活跃任务数、非活跃任务数
|
1513
|
+
- 今日执行总次数、成功次数、失败次数
|
1514
|
+
- 今日成功率(百分比)
|
1515
|
+
- 平均执行时长(秒)
|
1516
|
+
- 即将执行的任务列表(最近5个)
|
1517
|
+
- 任务类型分布(cron/interval/date)
|
1518
|
+
|
1519
|
+
**任务状态统计**:
|
1520
|
+
- `total_tasks`: 命名空间下所有定时任务总数
|
1521
|
+
- `active_tasks`: 启用状态的任务数量
|
1522
|
+
- `inactive_tasks`: 禁用状态的任务数量
|
1523
|
+
|
1524
|
+
**执行统计**:
|
1525
|
+
- `total_executions_today`: 今日(UTC 0点至当前时间)总执行次数
|
1526
|
+
- `successful_executions_today`: 今日成功执行次数
|
1527
|
+
- `failed_executions_today`: 今日失败执行次数
|
1528
|
+
- `success_rate_today`: 今日成功率 = (成功次数 / 总次数) × 100%
|
1529
|
+
|
1530
|
+
**性能统计**:
|
1531
|
+
- `avg_execution_duration`: 今日平均执行时长(秒,仅计算成功的任务)
|
1532
|
+
|
1533
|
+
**即将执行的任务**:
|
1534
|
+
- 按下次执行时间升序排列
|
1535
|
+
- 最多返回5个即将执行的任务
|
1536
|
+
- 包含任务 ID、名称和下次执行时间
|
1537
|
+
|
1538
|
+
**使用场景**:
|
1539
|
+
- 定时任务管理首页概览
|
1540
|
+
- 运营监控看板
|
1541
|
+
- 定时任务健康度评估
|
1542
|
+
- 容量规划和分析
|
1543
|
+
- 管理决策支持
|
1544
|
+
|
1545
|
+
**示例请求**:
|
1546
|
+
```bash
|
1547
|
+
# 获取默认命名空间的统计数据
|
1548
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/statistics/default"
|
1549
|
+
|
1550
|
+
# 获取生产环境的统计数据
|
1551
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/statistics/production"
|
1552
|
+
|
1553
|
+
# 获取测试环境的统计数据
|
1554
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/statistics/test"
|
1555
|
+
```
|
1556
|
+
|
1557
|
+
**与其他接口配合使用**:
|
1558
|
+
```bash
|
1559
|
+
# 1. 查看所有命名空间
|
1560
|
+
curl -X GET "http://localhost:8001/api/v1/namespaces/"
|
1561
|
+
|
1562
|
+
# 2. 查看特定命名空间的定时任务统计
|
1563
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/statistics/default"
|
1564
|
+
|
1565
|
+
# 3. 查看该命名空间下的所有定时任务
|
1566
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/?page=1&page_size=50"
|
1567
|
+
|
1568
|
+
# 4. 查看某个任务的详细执行历史
|
1569
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/history"
|
1570
|
+
|
1571
|
+
# 5. 查看某个任务的执行趋势
|
1572
|
+
curl -X GET "http://localhost:8001/api/v1/scheduled/task-001/trend?time_range=7d"
|
1573
|
+
```
|
1574
|
+
|
1575
|
+
**监控告警示例**:
|
1576
|
+
```bash
|
1577
|
+
# 定期检查统计数据,设置告警阈值
|
1578
|
+
STATS=$(curl -s "http://localhost:8001/api/v1/scheduled/statistics/default")
|
1579
|
+
SUCCESS_RATE=$(echo $STATS | jq '.data.success_rate_today')
|
1580
|
+
|
1581
|
+
# 如果成功率低于 95%,触发告警
|
1582
|
+
if (( $(echo "$SUCCESS_RATE < 95" | bc -l) )); then
|
1583
|
+
echo "Alert: Success rate is below 95%: $SUCCESS_RATE%"
|
1584
|
+
# 发送告警通知
|
1585
|
+
fi
|
1586
|
+
```
|
1587
|
+
|
1588
|
+
**注意事项**:
|
1589
|
+
- 统计数据基于 UTC 时区计算
|
1590
|
+
- 今日数据从 UTC 0点开始统计
|
1591
|
+
- 命名空间参数区分大小写
|
1592
|
+
- 如果命名空间不存在,返回 404 错误
|
1593
|
+
- 统计数据实时计算,可能有轻微延迟(通常小于1秒)
|
1594
|
+
- 即将执行的任务列表只包含启用状态的任务
|
1595
|
+
- 平均执行时长不包括失败和超时的任务
|
403
1596
|
"""
|
404
1597
|
try:
|
405
1598
|
app = request.app
|