63 lines
2.2 KiB
Python
63 lines
2.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""ETL任务基类"""
|
|
from datetime import datetime, timedelta
|
|
from zoneinfo import ZoneInfo
|
|
|
|
class BaseTask:
|
|
"""ETL任务基类"""
|
|
|
|
def __init__(self, config, db_connection, api_client, logger):
|
|
self.config = config
|
|
self.db = db_connection
|
|
self.api = api_client
|
|
self.logger = logger
|
|
self.tz = ZoneInfo(config.get("app.timezone", "Asia/Taipei"))
|
|
|
|
def get_task_code(self) -> str:
|
|
"""获取任务代码"""
|
|
raise NotImplementedError("子类需实现 get_task_code 方法")
|
|
|
|
def execute(self) -> dict:
|
|
"""执行任务"""
|
|
raise NotImplementedError("子类需实现 execute 方法")
|
|
|
|
def _get_time_window(self, cursor_data: dict = None) -> tuple:
|
|
"""计算时间窗口"""
|
|
now = datetime.now(self.tz)
|
|
|
|
# 判断是否在闲时窗口
|
|
idle_start = self.config.get("run.idle_window.start", "04:00")
|
|
idle_end = self.config.get("run.idle_window.end", "16:00")
|
|
|
|
is_idle = self._is_in_idle_window(now, idle_start, idle_end)
|
|
|
|
# 获取窗口大小
|
|
if is_idle:
|
|
window_minutes = self.config.get("run.window_minutes.default_idle", 180)
|
|
else:
|
|
window_minutes = self.config.get("run.window_minutes.default_busy", 30)
|
|
|
|
# 计算窗口
|
|
overlap_seconds = self.config.get("run.overlap_seconds", 120)
|
|
|
|
if cursor_data and cursor_data.get("last_end"):
|
|
window_start = cursor_data["last_end"] - timedelta(seconds=overlap_seconds)
|
|
else:
|
|
window_start = now - timedelta(minutes=window_minutes)
|
|
|
|
window_end = now
|
|
|
|
return window_start, window_end, window_minutes
|
|
|
|
def _is_in_idle_window(self, dt: datetime, start_time: str, end_time: str) -> bool:
|
|
"""判断是否在闲时窗口"""
|
|
current_time = dt.strftime("%H:%M")
|
|
return start_time <= current_time <= end_time
|
|
|
|
def _build_result(self, status: str, counts: dict) -> dict:
|
|
"""构建结果字典"""
|
|
return {
|
|
"status": status,
|
|
"counts": counts
|
|
}
|