zhenxun_bot/zhenxun/services/scheduler/adapter.py
Rumio be86e0bb7f
Some checks are pending
检查bot是否运行正常 / bot check (push) Waiting to run
CodeQL Code Security Analysis / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
CodeQL Code Security Analysis / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
Sequential Lint and Type Check / ruff-call (push) Waiting to run
Sequential Lint and Type Check / pyright-call (push) Blocked by required conditions
Release Drafter / Update Release Draft (push) Waiting to run
Force Sync to Aliyun / sync (push) Waiting to run
Update Version / update-version (push) Waiting to run
♻️ refactor(scheduler): 重构定时任务系统并增强功能 (#2009)
* ♻️ refactor(scheduler): 重构定时任务系统并增强功能

- 【模型重命名】将 `ScheduleInfo` 模型及其数据库表重命名为 `ScheduledJob`,以提高语义清晰度。
- 【触发器抽象】引入 `Trigger` 工厂类,提供类型安全的 Cron、Interval 和 Date 触发器配置。
- 【执行策略】新增 `ExecutionPolicy` 模型,允许为定时任务定义重试策略、延迟、异常类型以及成功/失败回调。
- 【任务执行】重构任务执行逻辑,支持 NoneBot 的依赖注入,并根据 `ExecutionPolicy` 处理任务的重试和回调。
- 【临时任务】增加声明式和编程式的临时任务调度能力,支持非持久化任务在运行时动态创建和执行。
- 【管理命令】更新定时任务管理命令 (`schedule_admin`),使其适配新的 `ScheduledJob` 模型和参数验证逻辑。
- 【展示优化】改进定时任务列表和状态展示,使用新的触发器格式化逻辑和参数模型信息。
- 【重试装饰器】为 `Retry.api` 装饰器添加 `on_success` 回调,允许在任务成功执行后触发额外操作。

* 🚨 auto fix by pre-commit hooks

---------

Co-authored-by: webjoin111 <455457521@qq.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-08-06 09:02:23 +08:00

175 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
引擎适配层 (Adapter)
封装所有对具体调度器引擎 (APScheduler) 的操作,
使上层服务与调度器实现解耦。
"""
from collections.abc import Callable
from nonebot_plugin_apscheduler import scheduler
from zhenxun.models.scheduled_job import ScheduledJob
from zhenxun.services.log import logger
from .job import ScheduleContext, _execute_job
JOB_PREFIX = "zhenxun_schedule_"
class APSchedulerAdapter:
"""封装对 APScheduler 的操作"""
@staticmethod
def _get_job_id(schedule_id: int) -> str:
"""
生成 APScheduler 的 Job ID
参数:
schedule_id: 定时任务的ID。
返回:
str: APScheduler 使用的 Job ID。
"""
return f"{JOB_PREFIX}{schedule_id}"
@staticmethod
def add_or_reschedule_job(schedule: ScheduledJob):
"""
根据 ScheduledJob 添加或重新调度一个 APScheduler 任务
参数:
schedule: 定时任务对象,包含任务的所有配置信息。
"""
job_id = APSchedulerAdapter._get_job_id(schedule.id)
if not isinstance(schedule.trigger_config, dict):
logger.error(
f"任务 {schedule.id} 的 trigger_config 不是字典类型: "
f"{type(schedule.trigger_config)}"
)
return
job = scheduler.get_job(job_id)
if job:
scheduler.reschedule_job(
job_id, trigger=schedule.trigger_type, **schedule.trigger_config
)
logger.debug(f"已更新APScheduler任务: {job_id}")
else:
scheduler.add_job(
_execute_job,
trigger=schedule.trigger_type,
id=job_id,
misfire_grace_time=300,
args=[schedule.id],
**schedule.trigger_config,
)
logger.debug(f"已添加新的APScheduler任务: {job_id}")
@staticmethod
def remove_job(schedule_id: int):
"""
移除一个 APScheduler 任务
参数:
schedule_id: 要移除的定时任务ID。
"""
job_id = APSchedulerAdapter._get_job_id(schedule_id)
try:
scheduler.remove_job(job_id)
logger.debug(f"已从APScheduler中移除任务: {job_id}")
except Exception:
pass
@staticmethod
def pause_job(schedule_id: int):
"""
暂停一个 APScheduler 任务
参数:
schedule_id: 要暂停的定时任务ID。
"""
job_id = APSchedulerAdapter._get_job_id(schedule_id)
try:
scheduler.pause_job(job_id)
except Exception:
pass
@staticmethod
def resume_job(schedule_id: int):
"""
恢复一个 APScheduler 任务
参数:
schedule_id: 要恢复的定时任务ID。
"""
job_id = APSchedulerAdapter._get_job_id(schedule_id)
try:
scheduler.resume_job(job_id)
except Exception:
import asyncio
from .repository import ScheduleRepository
async def _re_add_job():
schedule = await ScheduleRepository.get_by_id(schedule_id)
if schedule:
APSchedulerAdapter.add_or_reschedule_job(schedule)
asyncio.create_task(_re_add_job()) # noqa: RUF006
@staticmethod
def get_job_status(schedule_id: int) -> dict:
"""
获取 APScheduler Job 的状态
参数:
schedule_id: 定时任务的ID。
返回:
dict: 包含任务状态信息的字典包含next_run_time等字段。
"""
job_id = APSchedulerAdapter._get_job_id(schedule_id)
job = scheduler.get_job(job_id)
return {
"next_run_time": job.next_run_time.strftime("%Y-%m-%d %H:%M:%S")
if job and job.next_run_time
else "N/A",
"is_paused_in_scheduler": not bool(job.next_run_time) if job else "N/A",
}
@staticmethod
def add_ephemeral_job(
job_id: str,
func: Callable,
trigger_type: str,
trigger_config: dict,
context: ScheduleContext,
):
"""
直接向 APScheduler 添加一个临时的、非持久化的任务
参数:
job_id: 临时任务的唯一ID。
func: 要执行的函数。
trigger_type: 触发器类型。
trigger_config: 触发器配置字典。
context: 任务执行上下文。
"""
job = scheduler.get_job(job_id)
if job:
logger.warning(f"尝试添加一个已存在的临时任务ID: {job_id},操作被忽略。")
return
scheduler.add_job(
_execute_job,
trigger=trigger_type,
id=job_id,
misfire_grace_time=60,
args=[None],
kwargs={"context_override": context},
**trigger_config,
)
logger.debug(f"已添加新的临时APScheduler任务: {job_id}")