# -*- coding: utf-8 -*- """任务数据模型""" from dataclasses import dataclass, field from datetime import datetime from enum import Enum from typing import Optional, List, Dict, Any class TaskStatus(Enum): """任务状态枚举""" PENDING = "pending" # 待执行 RUNNING = "running" # 执行中 SUCCESS = "success" # 成功 FAILED = "failed" # 失败 CANCELLED = "cancelled" # 已取消 class TaskCategory(Enum): """任务分类""" ODS = "ODS" # ODS 数据抓取任务 DWD = "DWD" # DWD 装载任务 DWS = "DWS" # DWS 汇总任务 SCHEMA = "Schema" # Schema 初始化任务 QUALITY = "Quality" # 质量检查任务 OTHER = "Other" # 其他任务 # 任务分类映射 TASK_CATEGORIES: Dict[str, TaskCategory] = { # ODS 任务 "ODS_PAYMENT": TaskCategory.ODS, "ODS_MEMBER": TaskCategory.ODS, "ODS_MEMBER_CARD": TaskCategory.ODS, "ODS_MEMBER_BALANCE": TaskCategory.ODS, "ODS_SETTLEMENT_RECORDS": TaskCategory.ODS, "ODS_TABLE_USE": TaskCategory.ODS, "ODS_ASSISTANT_ACCOUNT": TaskCategory.ODS, "ODS_ASSISTANT_LEDGER": TaskCategory.ODS, "ODS_ASSISTANT_ABOLISH": TaskCategory.ODS, "ODS_REFUND": TaskCategory.ODS, "ODS_PLATFORM_COUPON": TaskCategory.ODS, "ODS_RECHARGE_SETTLE": TaskCategory.ODS, "ODS_GROUP_PACKAGE": TaskCategory.ODS, "ODS_GROUP_BUY_REDEMPTION": TaskCategory.ODS, "ODS_INVENTORY_STOCK": TaskCategory.ODS, "ODS_INVENTORY_CHANGE": TaskCategory.ODS, "ODS_TABLES": TaskCategory.ODS, "ODS_GOODS_CATEGORY": TaskCategory.ODS, "ODS_STORE_GOODS": TaskCategory.ODS, "ODS_STORE_GOODS_SALES": TaskCategory.ODS, "ODS_TABLE_FEE_DISCOUNT": TaskCategory.ODS, "ODS_TENANT_GOODS": TaskCategory.ODS, "ODS_SETTLEMENT_TICKET": TaskCategory.ODS, # DWD 任务 "DWD_LOAD_FROM_ODS": TaskCategory.DWD, "DWD_QUALITY_CHECK": TaskCategory.QUALITY, "PAYMENTS_DWD": TaskCategory.DWD, "MEMBERS_DWD": TaskCategory.DWD, "TICKET_DWD": TaskCategory.DWD, # DWS 任务 "INIT_DWS_SCHEMA": TaskCategory.SCHEMA, "DWS_BUILD_ORDER_SUMMARY": TaskCategory.DWS, # Schema 任务 "INIT_ODS_SCHEMA": TaskCategory.SCHEMA, "INIT_DWD_SCHEMA": TaskCategory.SCHEMA, # 其他任务 "MANUAL_INGEST": TaskCategory.OTHER, "CHECK_CUTOFF": TaskCategory.OTHER, "DATA_INTEGRITY_CHECK": TaskCategory.QUALITY, "ODS_JSON_ARCHIVE": TaskCategory.OTHER, # 旧版任务(兼容) "PRODUCTS": TaskCategory.ODS, "TABLES": TaskCategory.ODS, "MEMBERS": TaskCategory.ODS, "ASSISTANTS": TaskCategory.ODS, "PACKAGES_DEF": TaskCategory.ODS, "ORDERS": TaskCategory.ODS, "PAYMENTS": TaskCategory.ODS, "REFUNDS": TaskCategory.ODS, "COUPON_USAGE": TaskCategory.ODS, "INVENTORY_CHANGE": TaskCategory.ODS, "TOPUPS": TaskCategory.ODS, "TABLE_DISCOUNT": TaskCategory.ODS, "ASSISTANT_ABOLISH": TaskCategory.ODS, "LEDGER": TaskCategory.ODS, } def get_task_category(task_code: str) -> TaskCategory: """获取任务分类""" return TASK_CATEGORIES.get(task_code.upper(), TaskCategory.OTHER) @dataclass class TaskItem: """任务项""" task_code: str name: str = "" description: str = "" category: TaskCategory = TaskCategory.OTHER enabled: bool = True def __post_init__(self): if not self.name: self.name = self.task_code if not self.category or self.category == TaskCategory.OTHER: self.category = get_task_category(self.task_code) @dataclass class TaskConfig: """任务执行配置""" tasks: List[str] = field(default_factory=list) pipeline_flow: str = "FULL" # FULL, FETCH_ONLY, INGEST_ONLY dry_run: bool = False window_start: Optional[str] = None window_end: Optional[str] = None window_split: Optional[str] = None # none, month window_compensation: int = 0 # 补偿小时数 ingest_source: Optional[str] = None store_id: Optional[int] = None pg_dsn: Optional[str] = None api_token: Optional[str] = None extra_args: Dict[str, Any] = field(default_factory=dict) env_vars: Dict[str, str] = field(default_factory=dict) # 额外环境变量 @dataclass class TaskHistory: """任务执行历史""" id: str task_codes: List[str] status: TaskStatus start_time: datetime end_time: Optional[datetime] = None exit_code: Optional[int] = None command: str = "" output_log: str = "" error_message: str = "" summary: Dict[str, Any] = field(default_factory=dict) @property def duration_seconds(self) -> Optional[float]: """执行时长(秒)""" if self.end_time and self.start_time: return (self.end_time - self.start_time).total_seconds() return None @property def duration_str(self) -> str: """格式化的执行时长""" secs = self.duration_seconds if secs is None: return "-" if secs < 60: return f"{secs:.1f}秒" elif secs < 3600: mins = int(secs // 60) secs = secs % 60 return f"{mins}分{secs:.0f}秒" else: hours = int(secs // 3600) mins = int((secs % 3600) // 60) return f"{hours}时{mins}分" @dataclass class QueuedTask: """队列中的任务""" id: str config: TaskConfig status: TaskStatus = TaskStatus.PENDING created_at: datetime = field(default_factory=datetime.now) started_at: Optional[datetime] = None finished_at: Optional[datetime] = None output: str = "" error: str = "" exit_code: Optional[int] = None