改 相对路径 完成客户端
This commit is contained in:
@@ -97,8 +97,14 @@ class ScheduleConfig:
|
||||
return f"Cron: {self.cron_expression}"
|
||||
return "未知"
|
||||
|
||||
# 首次执行延迟秒数
|
||||
FIRST_RUN_DELAY_SECONDS = 60
|
||||
|
||||
def get_next_run_time(self, last_run: Optional[datetime] = None) -> Optional[datetime]:
|
||||
"""计算下次运行时间"""
|
||||
"""计算下次运行时间
|
||||
|
||||
注意:首次执行(last_run 为 None)时会延迟 60 秒,避免创建后立即执行
|
||||
"""
|
||||
now = datetime.now()
|
||||
|
||||
# 检查日期范围
|
||||
@@ -112,12 +118,15 @@ class ScheduleConfig:
|
||||
if now >= end:
|
||||
return None
|
||||
|
||||
# 首次执行延迟 60 秒
|
||||
first_run_time = now + timedelta(seconds=self.FIRST_RUN_DELAY_SECONDS)
|
||||
|
||||
if self.schedule_type == ScheduleType.ONCE:
|
||||
return None if last_run else now
|
||||
return None if last_run else first_run_time
|
||||
|
||||
elif self.schedule_type == ScheduleType.INTERVAL:
|
||||
if not last_run:
|
||||
return now
|
||||
return first_run_time
|
||||
if self.interval_unit == IntervalUnit.MINUTES:
|
||||
delta = timedelta(minutes=self.interval_value)
|
||||
elif self.interval_unit == IntervalUnit.HOURS:
|
||||
@@ -177,6 +186,47 @@ class ScheduleConfig:
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScheduleExecutionRecord:
|
||||
"""调度执行记录"""
|
||||
task_id: str # 关联的 QueuedTask ID
|
||||
executed_at: datetime # 执行时间
|
||||
status: str = "" # 状态:success, failed, pending
|
||||
exit_code: Optional[int] = None # 退出码
|
||||
duration_seconds: float = 0.0 # 耗时(秒)
|
||||
summary: str = "" # 执行摘要
|
||||
output: str = "" # 完整执行日志
|
||||
error: str = "" # 错误信息
|
||||
|
||||
# 日志最大长度限制(字符数)
|
||||
MAX_OUTPUT_LENGTH: int = 100000 # 100KB
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"task_id": self.task_id,
|
||||
"executed_at": self.executed_at.isoformat(),
|
||||
"status": self.status,
|
||||
"exit_code": self.exit_code,
|
||||
"duration_seconds": self.duration_seconds,
|
||||
"summary": self.summary,
|
||||
"output": self.output[:self.MAX_OUTPUT_LENGTH] if self.output else "",
|
||||
"error": self.error[:5000] if self.error else "",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict) -> "ScheduleExecutionRecord":
|
||||
return cls(
|
||||
task_id=data.get("task_id", ""),
|
||||
executed_at=datetime.fromisoformat(data["executed_at"]) if data.get("executed_at") else datetime.now(),
|
||||
status=data.get("status", ""),
|
||||
exit_code=data.get("exit_code"),
|
||||
duration_seconds=data.get("duration_seconds", 0.0),
|
||||
summary=data.get("summary", ""),
|
||||
output=data.get("output", ""),
|
||||
error=data.get("error", ""),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScheduledTask:
|
||||
"""调度任务"""
|
||||
@@ -193,9 +243,33 @@ class ScheduledTask:
|
||||
run_count: int = 0
|
||||
last_status: str = ""
|
||||
|
||||
# 执行历史(最近 N 次执行记录)
|
||||
execution_history: List[ScheduleExecutionRecord] = field(default_factory=list)
|
||||
MAX_HISTORY_SIZE: int = field(default=50, repr=False) # 保留最近50次执行记录
|
||||
|
||||
created_at: datetime = field(default_factory=datetime.now)
|
||||
updated_at: datetime = field(default_factory=datetime.now)
|
||||
|
||||
def add_execution_record(self, record: ScheduleExecutionRecord):
|
||||
"""添加执行记录"""
|
||||
self.execution_history.insert(0, record)
|
||||
# 限制历史记录数量
|
||||
if len(self.execution_history) > self.MAX_HISTORY_SIZE:
|
||||
self.execution_history = self.execution_history[:self.MAX_HISTORY_SIZE]
|
||||
|
||||
def update_execution_record(self, task_id: str, status: str, exit_code: int, duration: float,
|
||||
summary: str, output: str = "", error: str = ""):
|
||||
"""更新执行记录状态"""
|
||||
for record in self.execution_history:
|
||||
if record.task_id == task_id:
|
||||
record.status = status
|
||||
record.exit_code = exit_code
|
||||
record.duration_seconds = duration
|
||||
record.summary = summary
|
||||
record.output = output
|
||||
record.error = error
|
||||
break
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""转换为字典"""
|
||||
return {
|
||||
@@ -209,6 +283,7 @@ class ScheduledTask:
|
||||
"next_run": self.next_run.isoformat() if self.next_run else None,
|
||||
"run_count": self.run_count,
|
||||
"last_status": self.last_status,
|
||||
"execution_history": [r.to_dict() for r in self.execution_history],
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"updated_at": self.updated_at.isoformat(),
|
||||
}
|
||||
@@ -216,6 +291,9 @@ class ScheduledTask:
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict) -> "ScheduledTask":
|
||||
"""从字典创建"""
|
||||
history_data = data.get("execution_history", [])
|
||||
execution_history = [ScheduleExecutionRecord.from_dict(r) for r in history_data]
|
||||
|
||||
return cls(
|
||||
id=data["id"],
|
||||
name=data["name"],
|
||||
@@ -227,6 +305,7 @@ class ScheduledTask:
|
||||
next_run=datetime.fromisoformat(data["next_run"]) if data.get("next_run") else None,
|
||||
run_count=data.get("run_count", 0),
|
||||
last_status=data.get("last_status", ""),
|
||||
execution_history=execution_history,
|
||||
created_at=datetime.fromisoformat(data["created_at"]) if data.get("created_at") else datetime.now(),
|
||||
updated_at=datetime.fromisoformat(data["updated_at"]) if data.get("updated_at") else datetime.now(),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user