Files
FunMD_Convert/docling/app/core/ScheduledTask.py

206 lines
6.7 KiB
Python
Raw Permalink 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.

"""
定时任务基类
提供 crontab 表达式到 APScheduler CronTrigger 的转换功能
以及便捷的任务添加方法
"""
import logging
from typing import Optional
from app.core.ScheduledTaskManager import ScheduledTaskManager
from apscheduler.triggers.cron import CronTrigger
logger = logging.getLogger(__name__)
class ScheduledTask:
"""
定时任务基类
提供将标准 crontab 表达式转换为 APScheduler CronTrigger 对象的功能
Crontab 表达式格式:分 时 日 月 周
例如:
- "0 2 * * *" -> 每天凌晨 2:00 执行
- "*/5 * * * *" -> 每 5 分钟执行一次
- "0 0 * * 0" -> 每周日 00:00 执行
- "0 0 1 * *" -> 每月 1 日 00:00 执行
"""
def __init__(self, task_manager: Optional[ScheduledTaskManager] = None):
"""
初始化定时任务
Args:
task_manager: 可选的调度任务管理器实例,如果不提供则需要外部提供
"""
self.task_manager = task_manager
def add_cron_task(
self,
task_id: str,
task_name: str,
crontab: str,
task_func,
args=None,
kwargs=None
):
"""
通过 crontab 表达式添加定时任务
Args:
task_id: 任务唯一标识符
task_name: 任务名称
crontab: crontab 表达式字符串,格式为 "分 时 日 月 周"
例如: "0 2 * * *" 表示每天凌晨 2 点执行
task_func: 要执行的任务函数
args: 传递给任务函数的位置参数(元组)
kwargs: 传递给任务函数的关键字参数(字典)
Returns:
bool: 添加成功返回 True失败返回 False
示例:
task = ScheduledTask(task_manager)
task.add_cron_task(
task_id="daily_backup",
task_name="每日备份",
crontab="0 2 * * *",
task_func=backup_function,
args=()
)
"""
if self.task_manager is None:
logger.error("任务管理器未初始化,无法添加任务")
return False
try:
# 将 crontab 表达式转换为 CronTrigger
trigger = self.covert(crontab)
if trigger is None:
logger.error(f"crontab 表达式转换失败: {crontab}")
return False
# 使用调度器添加任务
job = self.task_manager.scheduler.add_job(
task_func,
trigger,
id=task_id,
name=task_name,
args=args,
kwargs=kwargs
)
# 记录到 jobs 字典中
self.task_manager.jobs[task_id] = job
logger.info(f"已添加定时任务: {task_name} ({task_id}), crontab: {crontab}")
return True
except Exception as e:
logger.error(f"添加定时任务失败: {e}")
return False
def covert(self, crontab: str) -> Optional[CronTrigger]:
"""
将字符串的 crontab 表达式转换为 CronTrigger 对象
Args:
crontab: crontab 表达式字符串
格式: "分钟 小时 日期 月份 星期"
支持的特殊字符: * (任意值), / (间隔), - (范围), , (列表)
Returns:
Optional[CronTrigger]: CronTrigger 对象,解析失败返回 None
Crontab 字段说明:
字段 取值范围 特殊字符
分钟 0-59 * / - ,
小时 0-23 * / - ,
日期 1-31 * / - ,
月份 1-12 * / - ,
星期 0-7 (0和7都代表周日) * / - ,
示例:
"0 2 * * *" -> 每天凌晨 2:00
"*/5 * * * *" -> 每 5 分钟
"0 0 * * 0" -> 每周日 00:00
"0 0 1 * *" -> 每月 1 日 00:00
"30 8 * * 1-5" -> 工作日早上 8:30
"0 9-17 * * 1-5" -> 工作日每小时 9:00-17:00
"""
if not crontab or not isinstance(crontab, str):
logger.error(f"无效的 crontab 表达式: {crontab}")
return None
try:
# 去除首尾空格并按空格分割
parts = crontab.strip().split()
# 验证格式crontab 表达式应该有 5 或 6 个部分
# 5 部分: 分 时 日 月 周
# 6 部分: 分 时 日 月 周 年 (可选)
if len(parts) < 5 or len(parts) > 6:
logger.error(f"crontab 表达式格式错误,应为 5 或 6 个部分,实际为 {len(parts)} 个: {crontab}")
return None
# 解析各个字段
minute = parts[0] # 分钟: 0-59
hour = parts[1] # 小时: 0-23
day = parts[2] # 日期: 1-31
month = parts[3] # 月份: 1-12
day_of_week = parts[4] # 星期: 0-7 (0 和 7 都代表周日)
# 处理星期字段的特殊值
# 在 crontab 中0 和 7 都代表周日
# 在 APScheduler 中,周日使用 6 (mon=0, tue=1, ..., sun=6)
# 需要进行转换
if day_of_week in ['0', '7']:
day_of_week = '6' # 转换为 APScheduler 的周日表示
# 创建 CronTrigger 对象
trigger = CronTrigger(
minute=minute,
hour=hour,
day=day,
month=month,
day_of_week=day_of_week
)
logger.debug(f"成功转换 crontab 表达式: {crontab} -> {trigger}")
return trigger
except ValueError as e:
logger.error(f"crontab 表达式值无效: {crontab}, 错误: {e}")
return None
except Exception as e:
logger.error(f"转换 crontab 表达式失败: {crontab}, 错误: {e}")
return None
# 使用示例
if __name__ == "__main__":
# 创建任务实例
task = ScheduledTask()
# 测试各种 crontab 表达式
test_cases = [
"0 2 * * *", # 每天凌晨 2 点
"*/5 * * * *", # 每 5 分钟
"0 0 * * 0", # 每周日
"0 0 1 * *", # 每月 1 日
"30 8 * * 1-5", # 工作日早上 8:30
"0 9-17 * * 1-5", # 工作日每小时
]
print("Crontab 表达式转换测试:")
print("-" * 60)
for crontab in test_cases:
trigger = task.covert(crontab)
if trigger:
print(f"{crontab:20s} -> {trigger}")
else:
print(f"{crontab:20s} -> 转换失败")