Initial commit
This commit is contained in:
commit
9198139d0f
1793 files changed
+405866
No files matched your search
+271
@@ -0,0 +1,271 @@
|
||||
import uuid
|
||||
import pytz
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Optional, Callable, Any
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.date import DateTrigger
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
from apscheduler.job import Job
|
||||
|
||||
class TaskScheduler:
|
||||
def __init__(self, ExecuteCallBack: Callable[[str, str, str], Any] = lambda x, y, z: exec(y)):
|
||||
"""
|
||||
初始化调度器
|
||||
|
||||
参数:
|
||||
ExecuteCallBack: 任务执行时的回调函数,默认为直接执行Python代码
|
||||
"""
|
||||
self.scheduler = BackgroundScheduler(timezone=pytz.timezone('Asia/Shanghai')) # UTC+8时区
|
||||
self.task_dict: Dict[str, Dict] = {} # 存储所有任务数据
|
||||
self.ExecuteCallBack = ExecuteCallBack
|
||||
self.scheduler.start()
|
||||
|
||||
def _generate_uuid(self) -> str:
|
||||
"""生成唯一任务ID"""
|
||||
return str(uuid.uuid4())
|
||||
|
||||
def _parse_time(self, time_str: str, time_format: str) -> datetime:
|
||||
"""解析时间字符串为datetime对象"""
|
||||
return datetime.strptime(time_str, time_format).replace(tzinfo=pytz.timezone('Asia/Shanghai'))
|
||||
|
||||
def _get_trigger(self, task_data: Dict) -> Any:
|
||||
"""根据任务数据获取对应的触发器"""
|
||||
rtime_type = task_data['RTimeType']
|
||||
now = datetime.now(pytz.timezone('Asia/Shanghai'))
|
||||
|
||||
if rtime_type == '1': # 具体时间执行一次
|
||||
run_time = self._parse_time(task_data['RTime'], '%Y-%m-%d %H:%M:%S')
|
||||
return DateTrigger(run_date=run_time)
|
||||
|
||||
elif rtime_type == '2': # 每日固定时间执行
|
||||
hour, minute, second = map(int, task_data['RTime'].split(':'))
|
||||
return CronTrigger(hour=hour, minute=minute, second=second, timezone='Asia/Shanghai')
|
||||
|
||||
elif rtime_type == '3': # 每周特定星期几执行
|
||||
day_of_week, time_part = task_data['RTime'].split(' ', 1)
|
||||
hour, minute, second = map(int, time_part.split(':'))
|
||||
return CronTrigger(day_of_week=day_of_week, hour=hour, minute=minute, second=second, timezone='Asia/Shanghai')
|
||||
|
||||
elif rtime_type == '4': # 每月特定日期执行
|
||||
day, time_part = task_data['RTime'].split(' ', 1)
|
||||
hour, minute, second = map(int, time_part.split(':'))
|
||||
return CronTrigger(day=day, hour=hour, minute=minute, second=second, timezone='Asia/Shanghai')
|
||||
|
||||
elif rtime_type == '5': # 固定间隔执行
|
||||
hours, minutes, seconds = map(int, task_data['RTime'].split(':'))
|
||||
delta = timedelta(hours=hours, minutes=minutes, seconds=seconds)
|
||||
return IntervalTrigger(seconds=delta.total_seconds(), timezone='Asia/Shanghai')
|
||||
|
||||
elif rtime_type == '6': # 开机立即执行
|
||||
return DateTrigger(run_date=now)
|
||||
|
||||
elif rtime_type == '7': # 开机延时执行
|
||||
seconds = int(task_data['RTime'])
|
||||
return DateTrigger(run_date=now + timedelta(seconds=seconds))
|
||||
|
||||
else:
|
||||
raise ValueError(f"未知的执行时间类型: {rtime_type}")
|
||||
|
||||
def _check_task_valid(self, task_data: Dict) -> bool:
|
||||
"""检查任务是否有效(未过期)"""
|
||||
now = datetime.now(pytz.timezone('Asia/Shanghai'))
|
||||
|
||||
# 检查生效时间
|
||||
etime_type = task_data['ETimeType']
|
||||
if etime_type == '1': # 具体时间生效
|
||||
effective_time = self._parse_time(task_data['ETime'], '%Y-%m-%d %H:%M:%S')
|
||||
if now < effective_time:
|
||||
return False
|
||||
elif etime_type == '2': # 每日时间生效
|
||||
effective_time = self._parse_time(task_data['ETime'], '%H:%M:%S')
|
||||
now_time = now.time()
|
||||
if now_time < effective_time.time():
|
||||
return False
|
||||
|
||||
# 检查失效时间
|
||||
ltime_type = task_data['LTimeType']
|
||||
if ltime_type == '1': # 具体时间失效
|
||||
expire_time = self._parse_time(task_data['LTime'], '%Y-%m-%d %H:%M:%S')
|
||||
if now >= expire_time:
|
||||
return False
|
||||
elif ltime_type == '2': # 每日时间失效
|
||||
expire_time = self._parse_time(task_data['LTime'], '%H:%M:%S')
|
||||
now_time = now.time()
|
||||
if now_time >= expire_time.time():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _execute_task(self, task_uuid: str):
|
||||
"""执行任务的实际函数"""
|
||||
task_data = self.task_dict.get(task_uuid)
|
||||
if not task_data:
|
||||
return
|
||||
|
||||
# 检查任务是否仍然有效
|
||||
if not self._check_task_valid(task_data):
|
||||
task_data['working_status'] = False
|
||||
return
|
||||
|
||||
# 执行回调
|
||||
try:
|
||||
self.ExecuteCallBack(task_uuid, task_data['RCode'], task_data.get('RType', ''))
|
||||
except Exception as e:
|
||||
print(f"执行任务 {task_uuid} 时出错: {str(e)}")
|
||||
|
||||
# 如果是一次性任务,执行后设置为失效
|
||||
if task_data['RTimeType'] in ['1', '6', '7']:
|
||||
task_data['working_status'] = False
|
||||
|
||||
def add_task(self, task_data: Dict) -> bool:
|
||||
"""
|
||||
添加任务到调度器
|
||||
|
||||
参数:
|
||||
task_data: 包含任务配置的字典
|
||||
|
||||
返回值:
|
||||
bool: 是否添加成功
|
||||
"""
|
||||
try:
|
||||
# 验证必要字段
|
||||
required_fields = ['ETimeType', 'RTimeType', 'LTimeType', 'RCode']
|
||||
for field in required_fields:
|
||||
if field not in task_data:
|
||||
raise ValueError(f"缺少必要字段: {field}")
|
||||
|
||||
# 生成或使用提供的任务ID
|
||||
task_uuid = task_data.get('TaskUUID', self._generate_uuid())
|
||||
task_data['TaskUUID'] = task_uuid
|
||||
|
||||
# 设置任务初始状态
|
||||
task_data['working_status'] = self._check_task_valid(task_data)
|
||||
|
||||
# 如果任务已经失效,不添加到调度器
|
||||
if not task_data['working_status']:
|
||||
self.task_dict[task_uuid] = task_data
|
||||
return True
|
||||
|
||||
# 获取触发器
|
||||
trigger = self._get_trigger(task_data)
|
||||
|
||||
# 添加到调度器
|
||||
job = self.scheduler.add_job(
|
||||
self._execute_task,
|
||||
trigger=trigger,
|
||||
args=[task_uuid],
|
||||
id=task_uuid
|
||||
)
|
||||
|
||||
# 存储任务数据
|
||||
self.task_dict[task_uuid] = task_data
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"添加任务失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def remove_task(self, task_uuid: str) -> bool:
|
||||
"""
|
||||
从调度器中移除任务
|
||||
|
||||
参数:
|
||||
task_uuid: 要移除的任务ID
|
||||
|
||||
返回值:
|
||||
bool: 是否移除成功
|
||||
"""
|
||||
if task_uuid not in self.task_dict:
|
||||
return False
|
||||
|
||||
# 从调度器中移除
|
||||
self.scheduler.remove_job(task_uuid)
|
||||
|
||||
# 从任务字典中移除
|
||||
del self.task_dict[task_uuid]
|
||||
return True
|
||||
|
||||
def get_task(self, task_uuid: str) -> Optional[Dict]:
|
||||
"""
|
||||
获取单个任务信息
|
||||
|
||||
参数:
|
||||
task_uuid: 要查询的任务ID
|
||||
|
||||
返回值:
|
||||
dict: 包含任务信息的字典或None
|
||||
"""
|
||||
return self.task_dict.get(task_uuid)
|
||||
|
||||
def list_tasks(self) -> Dict[str, Dict]:
|
||||
"""
|
||||
列出所有任务
|
||||
|
||||
返回值:
|
||||
dict: 所有任务信息的字典,键为任务ID
|
||||
"""
|
||||
return self.task_dict.copy()
|
||||
|
||||
def shutdown(self):
|
||||
"""停止调度器运行,释放资源"""
|
||||
self.scheduler.shutdown(wait=True)
|
||||
|
||||
# 示例使用
|
||||
if __name__ == "__main__":
|
||||
# 初始化调度器
|
||||
scheduler = TaskScheduler()
|
||||
|
||||
# 示例1: 已过期的任务
|
||||
task1 = {
|
||||
'ETimeType': '3',
|
||||
'RTimeType': '6',
|
||||
'LTimeType': '1',
|
||||
'LTime': '2025-05-18 03:16:00',
|
||||
'RCode': 'print("HI from task1")',
|
||||
'TaskName': 'HI'
|
||||
}
|
||||
scheduler.add_task(task1)
|
||||
|
||||
# 示例2: 未过期的任务(会立即执行)
|
||||
task2 = {
|
||||
'ETimeType': '3',
|
||||
'RTimeType': '6',
|
||||
'LTimeType': '1',
|
||||
'LTime': '2025-05-18 09:10:00',
|
||||
'RCode': 'print("HI from task2")',
|
||||
'TaskName': 'HI'
|
||||
}
|
||||
scheduler.add_task(task2)
|
||||
|
||||
# 示例3: 未到生效时间的任务
|
||||
task3 = {
|
||||
'ETimeType': '1',
|
||||
'ETime': '2025-05-18 09:29:10',
|
||||
'RTimeType': '1',
|
||||
'RTime': '2025-05-18 09:29:20',
|
||||
'LTimeType': '1',
|
||||
'LTime': '2025-05-18 09:30:00',
|
||||
'RCode': 'print("HI from task3")',
|
||||
'TaskName': 'HI_Expired'
|
||||
}
|
||||
scheduler.add_task(task3)
|
||||
|
||||
# 示例4: 到达生效时间后立即执行的任务
|
||||
task4 = {
|
||||
'ETimeType': '1',
|
||||
'ETime': '2025-05-18 07:20:40',
|
||||
'RTimeType': '6',
|
||||
'LTimeType': '1',
|
||||
'LTime': '2025-05-18 07:25:00',
|
||||
'RCode': 'print("HI from task4")',
|
||||
'TaskName': 'HI_Expired'
|
||||
}
|
||||
scheduler.add_task(task4)
|
||||
|
||||
# 保持程序运行
|
||||
try:
|
||||
while True:
|
||||
pass
|
||||
except KeyboardInterrupt:
|
||||
scheduler.shutdown()
|
||||
Reference in new issue
Block a user