from datetime import datetime from apscheduler.schedulers.background import BackgroundScheduler from apscheduler.events import EVENT_JOB_EXECUTED, EVENT_JOB_ERROR from apscheduler.triggers.date import DateTrigger from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.interval import IntervalTrigger import pytz import logging import uuid import traceback import typing import random def GUUID(): return str(uuid.uuid3(uuid.NAMESPACE_DNS, str(random.randint(0, 10000000000000000000)))) class Task: """Task Type, Encapsulating Task Attributes And Behaviors.""" def __init__(self, task_data: typing.Dict): """ 初始化任务 :param task_data: 任务数据字典 """ self.task_data=task_data self.uuid = task_data.get('TaskUUID', GUUID()) self.name = task_data.get('TaskName', 'Unnamed Task') self.e_time_type = task_data.get('ETimeType') self.e_time = task_data.get('ETime') self.r_time_type = task_data.get('RTimeType') self.r_time = task_data.get('RTime') self.r_code = task_data.get('RCode') self.r_type = task_data.get('RType') # 验证必要字段 if not all([self.e_time_type, self.r_time_type, self.r_code]): raise ValueError("Missing Necessary Task Parameters") def is_effective(self) -> bool: """检查任务是否在生效时间内""" now = datetime.now(pytz.timezone('Asia/Shanghai')) try: if self.e_time_type == '1': # 具体时间 effective_time = datetime.strptime(self.e_time, '%Y-%m-%d %H:%M:%S') effective_time = pytz.timezone('Asia/Shanghai').localize(effective_time) return now >= effective_time elif self.e_time_type == '2': # 每日执行时间 hour, minute, second = map(int, self.e_time.split(':')) effective_time = now.replace(hour=hour, minute=minute, second=second) return now >= effective_time # 其他类型默认返回True return True except Exception as e: print(f"Failed To Check The Effective Time Of Task {self.uuid}: {e}") return False def create_trigger(self) -> typing.Optional[typing.Union[DateTrigger, CronTrigger, IntervalTrigger]]: """根据时间类型创建对应的触发器""" try: if self.r_time_type == '1': # 具体时间 return DateTrigger(run_date=datetime.strptime(self.r_time, '%Y-%m-%d %H:%M:%S')) elif self.r_time_type == '2': # 每日执行时间 hour, minute, second = map(int, self.r_time.split(':')) return CronTrigger(hour=hour, minute=minute, second=second) elif self.r_time_type == '3': # 每周循环时间 parts = self.r_time.split() day_of_week = parts[0] # 1-7,1=周一 hour, minute, second = map(int, parts[1].split(':')) return CronTrigger(day_of_week=day_of_week, hour=hour, minute=minute, second=second) elif self.r_time_type == '4': # 月循环时间 parts = self.r_time.split() day = parts[0] # 1-31 hour, minute, second = map(int, parts[1].split(':')) return CronTrigger(day=day, hour=hour, minute=minute, second=second) elif self.r_time_type == '5': # 间隔时间 hour, minute, second = map(int, self.r_time.split(':')) return IntervalTrigger(hours=hour, minutes=minute, seconds=second) elif self.r_time_type == '6': # 开机时执行 return None # 已在调度器中特殊处理 return None except Exception as e: print(f"Failed To Create Scheduler For Task {self.uuid} : {e}") return None class TaskScheduler: """任务调度器类,管理所有任务的调度""" def __init__(self, ExecuteCallBack=lambda x, y, z: exec(y)): self.ExecuteCallBack=ExecuteCallBack """初始化调度器""" # 初始化调度器,使用上海时区 self.scheduler = BackgroundScheduler(timezone=pytz.timezone('Asia/Shanghai')) self.scheduler.start() # 存储任务对象 self.tasks: typing.Dict[str, Task] = {} # 添加事件监听 self.scheduler.add_listener(self._job_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR) def _job_listener(self, event): """Task Execution Event Listener""" if event.code == EVENT_JOB_EXECUTED: print(f"Task {event.job_id} Executed Successfully") elif event.code == EVENT_JOB_ERROR: print(f"Task {event.job_id} Execution Failed: {event.exception}") def add_task(self, task_data: typing.Dict) -> bool: """ Add Tasks To The Scheduler :param task_data: Task Data Dictionary :return: Was It Added Successfully? """ try: task = Task(task_data) # 检查生效时间是否已过 if not task.is_effective(): print(f"Task {task.uuid} The Effective Date Has Passed.") return False # 处理开机执行的任务 if task.r_time_type == '6': print(f"Immediately Execute The StartUp Task: {task.name}") self._execute_code(task_data, task.r_code, task.r_type) return True # 创建触发器 trigger = task.create_trigger() if not trigger: print(f"Unable To Create A Trigger For Task: {task.uuid}") return False # 添加任务到调度器 self.scheduler.add_job( self._execute_code, trigger=trigger, args=[task_data, task.r_code, task.r_type], id=task.uuid, name=task.name, misfire_grace_time=60, # 允许60秒的延迟执行 ) # 存储任务对象 self.tasks[task.uuid] = task print(f"Task {task.uuid} Has Been Added.") return True except Exception as e: print(f"Failed To Add Task: {e}") return False def remove_task(self, task_uuid: str) -> bool: """ 移除任务 :param task_uuid: 任务UUID :return: 是否移除成功 """ if task_uuid not in self.tasks: print(f"任务 {task_uuid} 不存在") return False try: # 从调度器中移除 try: self.scheduler.remove_job(task_uuid) except: pass # 从任务字典中移除 del self.tasks[task_uuid] print(f"Task {task_uuid} Has Been Removed") return True except Exception as e: print(traceback.format_exc()) print(f"Failed To Remove Task {task_uuid}: {e}") return False def get_task(self, task_uuid: str) -> typing.Optional[typing.Dict]: """ 获取任务信息 :param task_uuid: 任务UUID :return: 任务信息字典或None """ task = self.tasks.get(task_uuid) if not task: return None return { 'TaskUUID': task.uuid, 'TaskName': task.name, 'ETimeType': task.e_time_type, 'ETime': task.e_time, 'RTimeType': task.r_time_type, 'RTime': task.r_time, 'RCode': task.r_code, 'RType': task.r_type } def list_tasks(self) -> typing.Dict[str, typing.Dict]: """Get All Task Information""" return {uuid: self.get_task(uuid) for uuid in self.tasks} def _execute_code(self, task: dict, _code: str, _type: str): self.ExecuteCallBack(task, _code, _type) def shutdown(self): """关闭调度器""" self.scheduler.shutdown() print("Task Scheduler Has Been Closed.") """ 请你使用python apscheduler做一个调度器,实现以下功能: ### 1. 初始化调度器 ```python scheduler = TaskScheduler(ExecuteCallBack=lambda x, y, z: exec(y)) ``` - **参数**: - `ExecuteCallBack`: 任务执行时的回调函数,默认为直接执行Python代码 - **功能**: 创建后台调度器实例,使用UTC-8时区 ### 2. 添加任务 ```python add_task(task_data: dict) -> bool ``` - **参数**: - `task_data`: 包含任务配置的字典,必须包含以下关键字段: - `ETimeType`: 生效时间类型 - `ETime`: 生效时间 - `RTimeType`: 执行时间类型 - `RTime`: 执行时间 - `LTimeType`: 失效时间类型 - `LTime`: 失效时间 - `RCode`: 要执行的代码 - **返回值**: 布尔值表示是否添加成功 - **功能**: 根据任务配置创建并调度任务 ### 3. 移除任务 ```python remove_task(task_uuid: str) -> bool ``` - **参数**: - `task_uuid`: 要移除的任务ID - **返回值**: 布尔值表示是否移除成功 - **功能**: 从调度器中移除指定任务 ### 4. 获取单个任务信息 ```python get_task(task_uuid: str) -> Optional[dict] ``` - **参数**: - `task_uuid`: 要查询的任务ID - **返回值**: 包含任务信息的字典或None - **功能**: 获取指定任务的详细配置信息 ### 5. 列出所有任务 ```python list_tasks() -> Dict[str, Dict] ``` - **返回值**: 字典格式的所有任务信息,键为任务ID - **功能**: 获取当前所有已调度任务的列表 ### 6. 关闭调度器 ```python shutdown() ``` - **功能**: 停止调度器运行,释放资源 ## 任务配置参数详解 ### 生效时间类型 (ETimeType) | 值 | 类型 | ETime格式 | 描述 | |----|------|-----------|------| | '1' | 具体时间 | 'YYYY-MM-DD HH:MM:SS' | 在指定时间点后生效 | | '2' | 每日时间 | 'HH:MM:SS' | 每天指定时间后生效 | | '3' | 永久生效 | 无 | 永久生效 | ### 执行时间类型 (RTimeType) | 值 | 类型 | RTime格式 | 描述 | |----|------|-----------|------| | '1' | 具体时间 | 'YYYY-MM-DD HH:MM:SS' | 在指定时间点执行一次 | | '2' | 每日执行 | 'HH:MM:SS' | 每天固定时间执行 | | '3' | 每周执行 | 'D HH:MM:SS' | 每周特定星期几执行 (1=周一) | | '4' | 每月执行 | 'D HH:MM:SS' | 每月特定日期执行 | | '5' | 间隔执行 | 'HH:MM:SS' | 按固定间隔重复执行 | | '6' | 开机执行 | 无 | 调度器启动时立即执行 | | '7' | 开机延时执行 | 'SS' | 调度器启动时等待一段时间执行 | ### 失效时间类型 (LTimeType) | 值 | 类型 | LTime格式 | 描述 | |----|------|-----------|------| | '1' | 具体时间 | 'YYYY-MM-DD HH:MM:SS' | 在指定时间点失生效 | | '2' | 每日时间 | 'HH:MM:SS' | 每天指定时间后失效 | | '3' | 永久生效 | 无 | 不会失效 | ### 其他参数 - `TaskName`: 任务名称(可选) - `TaskUUID`: 任务ID(可选,不提供会自动生成) - `RCode`: 要执行的代码内容 - `RType`: 代码类型(Python/Shell等,直接提交给回调,不由调度器处理) ## 使用流程示例 1. 初始化调度器 2. 准备任务配置字典 3. 调用`add_task()`添加任务 4. (可选)使用`list_tasks()`查看任务 5. (可选)使用`remove_task()`删除任务 6. 程序结束时调用`shutdown()`关闭调度器 ## 注意事项 注意除调度器外还有一个task_dict用于存储所有的task_data,格式{"UUID": task_data},task_data也有相同的UUID,还有是否生效的标志working_status。 1. 所有时间都使用UTF-8时区(东八区)。 2. 任务ID(TaskUUID)必须唯一,不提供会自动生成。 3. 开机执行的任务(RTimeType='6')会立即执行,会被添加到调度器中,但是被设置为失效。 4. 调度器使用后台线程,主程序需要保持运行状态才能执行定时任务 5. 注意任务完成和失效后不要从调度器中移除任务,而是将属性设置为失效。 6. 如果是2345时间类型的任务则不可能失效。 7. 生效时间3和执行时间6还有失效时间3可以没有ETime和RTime和LTime(或者可以任意填写)。 8. 生效时间类型的优先级低于失效时间优先级,也就是即使生效时间为永久生效,如果设置了具体时间失效仍然会失效 9. 如果添加出错,允许返回错误信息,以便用户定位问题 使用以下作为示例 1: 'ETimeType': '3', 'RTimeType': '6', 'LTimeType': '1', 'LTime': '2025-05-18 03:16:00', 'RCode': 'print("HI")', 'TaskName': 'HI' 正常情况下这个示例由于已经过失效时间而被视为失效,会添加但是被设置为失效因此无法执行 2: 'ETimeType': '3', 'RTimeType': '6', 'LTimeType': '1', 'LTime': '2025-05-18 04:10:00', 'RCode': 'print("HI")', 'TaskName': 'HI' 正常情况下这个示例由于还没过失效时间而不被视为失效,会在添加的同时被执行 3: 'ETimeType': '1', 'ETime': '2025-05-18 07:20:40', 'RTimeType': '1', 'RTime': '2025-05-18 07:20:50', 'LTimeType': '1', 'LTime': '2025-05-18 07:25:00', 'RCode': 'print("HI")', 'TaskName': 'HI_Expired' 正常情况下这个示例由于还没到生效时间因此不被执行,但是仍然有效,因此到达生效时间和执行时间后才会执行,执行后会被设置为失效,执行前一直有效但不执行 但是注意,到达生效时间后不会立刻执行,而是继续等到执行时间才会继续执行,因此任务会在生效时间生效,并在执行时间执行,然后根据类型被选择是否设置为失效 如果未到达生效时间不应当被判断为失效,应当使用调度器设置在到达生效时间后自动生效,并在符合执行时间时执行!!! 如果未到达生效时间不应当被判断为失效,应当使用调度器设置在到达生效时间后自动生效,并在符合执行时间时执行!!! 如果未到达生效时间不应当被判断为失效,应当使用调度器设置在到达生效时间后自动生效,并在符合执行时间时执行!!! 如果未到达生效时间不应当被判断为失效,应当使用调度器设置在到达生效时间后自动生效,并在符合执行时间时执行!!! 如果未到达生效时间不应当被判断为失效,应当使用调度器设置在到达生效时间后自动生效,并在符合执行时间时执行!!! 4: 'ETimeType': '1', 'ETime': '2025-05-18 07:20:40', 'RTimeType': '6', 'LTimeType': '1', 'LTime': '2025-05-18 07:25:00', 'RCode': 'print("HI")', 'TaskName': 'HI_Expired' 该任务会在到达生效时间后立即执行,因为RTimeType为6开机执行,执行后会被设置为失效,如果当前时间超过失效时间后则不会执行,直接判断为失效"""