2025-07-10 22:20:08 +08:00
|
|
|
|
"""
|
|
|
|
|
|
引擎适配层 (Adapter)
|
|
|
|
|
|
|
|
|
|
|
|
封装所有对具体调度器引擎 (APScheduler) 的操作,
|
|
|
|
|
|
使上层服务与调度器实现解耦。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2025-08-06 09:02:23 +08:00
|
|
|
|
from collections.abc import Callable
|
|
|
|
|
|
|
2025-07-10 22:20:08 +08:00
|
|
|
|
from nonebot_plugin_apscheduler import scheduler
|
|
|
|
|
|
|
2025-08-06 09:02:23 +08:00
|
|
|
|
from zhenxun.models.scheduled_job import ScheduledJob
|
2025-07-10 22:20:08 +08:00
|
|
|
|
from zhenxun.services.log import logger
|
|
|
|
|
|
|
2025-08-06 09:02:23 +08:00
|
|
|
|
from .job import ScheduleContext, _execute_job
|
2025-07-10 22:20:08 +08:00
|
|
|
|
|
|
|
|
|
|
JOB_PREFIX = "zhenxun_schedule_"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class APSchedulerAdapter:
|
|
|
|
|
|
"""封装对 APScheduler 的操作"""
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def _get_job_id(schedule_id: int) -> str:
|
2025-08-06 09:02:23 +08:00
|
|
|
|
"""
|
|
|
|
|
|
生成 APScheduler 的 Job ID
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
schedule_id: 定时任务的ID。
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
str: APScheduler 使用的 Job ID。
|
|
|
|
|
|
"""
|
2025-07-10 22:20:08 +08:00
|
|
|
|
return f"{JOB_PREFIX}{schedule_id}"
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2025-08-06 09:02:23 +08:00
|
|
|
|
def add_or_reschedule_job(schedule: ScheduledJob):
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据 ScheduledJob 添加或重新调度一个 APScheduler 任务
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
schedule: 定时任务对象,包含任务的所有配置信息。
|
|
|
|
|
|
"""
|
2025-07-10 22:20:08 +08:00
|
|
|
|
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):
|
2025-08-06 09:02:23 +08:00
|
|
|
|
"""
|
|
|
|
|
|
移除一个 APScheduler 任务
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
schedule_id: 要移除的定时任务ID。
|
|
|
|
|
|
"""
|
2025-07-10 22:20:08 +08:00
|
|
|
|
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):
|
2025-08-06 09:02:23 +08:00
|
|
|
|
"""
|
|
|
|
|
|
暂停一个 APScheduler 任务
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
schedule_id: 要暂停的定时任务ID。
|
|
|
|
|
|
"""
|
2025-07-10 22:20:08 +08:00
|
|
|
|
job_id = APSchedulerAdapter._get_job_id(schedule_id)
|
|
|
|
|
|
try:
|
|
|
|
|
|
scheduler.pause_job(job_id)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
def resume_job(schedule_id: int):
|
2025-08-06 09:02:23 +08:00
|
|
|
|
"""
|
|
|
|
|
|
恢复一个 APScheduler 任务
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
schedule_id: 要恢复的定时任务ID。
|
|
|
|
|
|
"""
|
2025-07-10 22:20:08 +08:00
|
|
|
|
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:
|
2025-08-06 09:02:23 +08:00
|
|
|
|
"""
|
|
|
|
|
|
获取 APScheduler Job 的状态
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
schedule_id: 定时任务的ID。
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
dict: 包含任务状态信息的字典,包含next_run_time等字段。
|
|
|
|
|
|
"""
|
2025-07-10 22:20:08 +08:00
|
|
|
|
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",
|
|
|
|
|
|
}
|
2025-08-06 09:02:23 +08:00
|
|
|
|
|
|
|
|
|
|
@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}")
|