微信小程序页面迁移校验之前 P5任务处理之前
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""调度任务 CRUD API
|
||||
|
||||
提供 5 个端点:
|
||||
提供 8 个端点:
|
||||
- GET /api/schedules — 列表(按 site_id 过滤)
|
||||
- POST /api/schedules — 创建
|
||||
- POST /api/schedules — 创建(支持 run_immediately)
|
||||
- PUT /api/schedules/{id} — 更新
|
||||
- DELETE /api/schedules/{id} — 删除
|
||||
- PATCH /api/schedules/{id}/toggle — 启用/禁用
|
||||
- GET /api/schedules/{id}/history — 调度任务执行历史
|
||||
- POST /api/schedules/{id}/run — 手动执行一次(不更新调度间隔)
|
||||
|
||||
所有端点需要 JWT 认证,site_id 从 JWT 提取。
|
||||
"""
|
||||
@@ -17,7 +19,7 @@ import json
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
|
||||
from app.auth.dependencies import CurrentUser, get_current_user
|
||||
from app.database import get_connection
|
||||
@@ -26,7 +28,10 @@ from app.schemas.schedules import (
|
||||
ScheduleResponse,
|
||||
UpdateScheduleRequest,
|
||||
)
|
||||
from app.schemas.execution import ExecutionHistoryItem
|
||||
from app.schemas.tasks import TaskConfigSchema
|
||||
from app.services.scheduler import calculate_next_run
|
||||
from app.services.task_queue import task_queue
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -92,7 +97,7 @@ async def create_schedule(
|
||||
body: CreateScheduleRequest,
|
||||
user: CurrentUser = Depends(get_current_user),
|
||||
) -> ScheduleResponse:
|
||||
"""创建调度任务,自动计算 next_run_at。"""
|
||||
"""创建调度任务,自动计算 next_run_at。支持 run_immediately 立即入队执行一次。"""
|
||||
now = datetime.now(timezone.utc)
|
||||
next_run = calculate_next_run(body.schedule_config, now)
|
||||
|
||||
@@ -124,7 +129,18 @@ async def create_schedule(
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
return _row_to_response(row)
|
||||
response = _row_to_response(row)
|
||||
|
||||
# 立即执行一次(入队,不影响调度间隔)
|
||||
if body.run_immediately:
|
||||
try:
|
||||
config = TaskConfigSchema(**body.task_config)
|
||||
config = config.model_copy(update={"store_id": user.site_id})
|
||||
task_queue.enqueue(config, user.site_id, schedule_id=response.id)
|
||||
except Exception:
|
||||
logger.warning("创建调度后立即执行入队失败 schedule_id=%s", response.id, exc_info=True)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# ── PUT /api/schedules/{id} — 更新 ──────────────────────────
|
||||
@@ -291,3 +307,88 @@ async def toggle_schedule(
|
||||
conn.close()
|
||||
|
||||
return _row_to_response(updated_row)
|
||||
|
||||
|
||||
# ── POST /api/schedules/{id}/run — 手动执行一次 ──────────────
|
||||
|
||||
@router.post("/{schedule_id}/run")
|
||||
async def run_schedule_now(
|
||||
schedule_id: str,
|
||||
user: CurrentUser = Depends(get_current_user),
|
||||
) -> dict:
|
||||
"""手动触发调度任务执行一次,不更新 last_run_at / next_run_at / run_count。
|
||||
|
||||
读取调度任务的 task_config,构造 TaskConfigSchema 后入队执行。
|
||||
"""
|
||||
conn = get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT task_config, site_id FROM scheduled_tasks WHERE id = %s AND site_id = %s",
|
||||
(schedule_id, user.site_id),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if row is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="调度任务不存在",
|
||||
)
|
||||
|
||||
task_config_raw = row[0] if isinstance(row[0], dict) else json.loads(row[0])
|
||||
config = TaskConfigSchema(**task_config_raw)
|
||||
config = config.model_copy(update={"store_id": user.site_id})
|
||||
task_id = task_queue.enqueue(config, user.site_id, schedule_id=schedule_id)
|
||||
|
||||
return {"message": "已提交到执行队列", "task_id": task_id}
|
||||
|
||||
|
||||
# ── GET /api/schedules/{id}/history — 执行历史 ────────────────
|
||||
|
||||
@router.get("/{schedule_id}/history", response_model=list[ExecutionHistoryItem])
|
||||
async def get_schedule_history(
|
||||
schedule_id: str,
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(50, ge=1, le=200),
|
||||
user: CurrentUser = Depends(get_current_user),
|
||||
) -> list[ExecutionHistoryItem]:
|
||||
"""获取调度任务的执行历史记录,按开始时间倒序,支持分页。"""
|
||||
offset = (page - 1) * page_size
|
||||
conn = get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT id, site_id, task_codes, status, started_at, finished_at,
|
||||
exit_code, duration_ms, command, summary, schedule_id
|
||||
FROM task_execution_log
|
||||
WHERE schedule_id = %s AND site_id = %s
|
||||
ORDER BY started_at DESC
|
||||
LIMIT %s OFFSET %s
|
||||
""",
|
||||
(schedule_id, user.site_id, page_size, offset),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
return [
|
||||
ExecutionHistoryItem(
|
||||
id=str(row[0]),
|
||||
site_id=row[1],
|
||||
task_codes=row[2] or [],
|
||||
status=row[3],
|
||||
started_at=row[4],
|
||||
finished_at=row[5],
|
||||
exit_code=row[6],
|
||||
duration_ms=row[7],
|
||||
command=row[8],
|
||||
summary=row[9],
|
||||
schedule_id=str(row[10]) if row[10] else None,
|
||||
)
|
||||
for row in rows
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user