2026-06-22 20:07:20 +08:00
|
|
|
|
"""ORM ↔ dataclass 映射边界。
|
|
|
|
|
|
|
|
|
|
|
|
本模块是 ``adapters/persistence`` 层负责 ORM 实例 ↔ core dataclass 转换的位置。
|
|
|
|
|
|
scheduler 限界上下文无敏感字段,mapper 仅做字段映射,不调用 ``encrypt`` /
|
|
|
|
|
|
``decrypt``(与 ``external_systems/adapters/persistence/mappers.py`` 不同)。
|
|
|
|
|
|
|
|
|
|
|
|
设计原则(见设计方案 §10):
|
|
|
|
|
|
|
|
|
|
|
|
- **mapper 不调用 ORM 的 ``to_dict`` 方法**:直接读 ORM 字段构造 dataclass。
|
|
|
|
|
|
原因:``to_dict`` 返回的 dict 结构与 dataclass 字段不完全一致(如 DateTime
|
|
|
|
|
|
字段在 ``to_dict`` 中被 ``format_utc_datetime`` 格式化为字符串)。
|
|
|
|
|
|
- **无敏感字段**:scheduler 域所有字段均为明文存储,无需加解密。
|
|
|
|
|
|
- **处理 None 入参**:ORM 实例为 None 时返回 None,避免调用方需额外判空。
|
|
|
|
|
|
- **仓储层 dataclass → core dataclass**:``DailyStat`` / ``HandlerSummary`` 在
|
|
|
|
|
|
仓储层与 core 层均有同名 dataclass,mapper 按字段名转换。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
|
|
|
|
|
|
|
|
from yuxi.scheduler.core.models import (
|
|
|
|
|
|
DailyStat as DailyStatDC,
|
2026-06-22 21:22:07 +08:00
|
|
|
|
)
|
|
|
|
|
|
from yuxi.scheduler.core.models import (
|
2026-06-22 20:07:20 +08:00
|
|
|
|
HandlerSummary as HandlerSummaryDC,
|
2026-06-22 21:22:07 +08:00
|
|
|
|
)
|
|
|
|
|
|
from yuxi.scheduler.core.models import (
|
2026-06-22 20:07:20 +08:00
|
|
|
|
ScheduledTask as ScheduledTaskDC,
|
2026-06-22 21:22:07 +08:00
|
|
|
|
)
|
|
|
|
|
|
from yuxi.scheduler.core.models import (
|
2026-06-22 20:07:20 +08:00
|
|
|
|
ScheduledTaskRunLog as ScheduledTaskRunLogDC,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
|
from yuxi.storage.postgres.models_scheduler import (
|
|
|
|
|
|
ScheduledTask as ScheduledTaskORM,
|
2026-06-22 21:22:07 +08:00
|
|
|
|
)
|
|
|
|
|
|
from yuxi.storage.postgres.models_scheduler import (
|
2026-06-22 20:07:20 +08:00
|
|
|
|
ScheduledTaskRunLog as ScheduledTaskRunLogORM,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def orm_to_task(orm: ScheduledTaskORM | None) -> ScheduledTaskDC | None:
|
|
|
|
|
|
"""ORM → dataclass,``ScheduledTask`` 字段一一映射。
|
|
|
|
|
|
|
|
|
|
|
|
DateTime 字段直接赋值(core 用 ``Any``),JSON 字段直接赋值。
|
|
|
|
|
|
入参为 None 时返回 None,避免调用方需额外判空。
|
|
|
|
|
|
"""
|
|
|
|
|
|
if orm is None:
|
|
|
|
|
|
return None
|
|
|
|
|
|
return ScheduledTaskDC(
|
|
|
|
|
|
id=orm.id,
|
|
|
|
|
|
task_id=orm.task_id,
|
|
|
|
|
|
handler_name=orm.handler_name,
|
|
|
|
|
|
owner_scope=orm.owner_scope,
|
|
|
|
|
|
owner_id=orm.owner_id,
|
|
|
|
|
|
schedule_kind=orm.schedule_kind,
|
|
|
|
|
|
cron_expression=orm.cron_expression,
|
|
|
|
|
|
run_at=orm.run_at,
|
|
|
|
|
|
tz=orm.tz,
|
|
|
|
|
|
payload=orm.payload or {},
|
|
|
|
|
|
enabled=orm.enabled,
|
|
|
|
|
|
delete_after_run=orm.delete_after_run,
|
|
|
|
|
|
block_strategy=orm.block_strategy,
|
|
|
|
|
|
stagger_seconds=orm.stagger_seconds,
|
|
|
|
|
|
consecutive_errors=orm.consecutive_errors,
|
|
|
|
|
|
status=orm.status,
|
|
|
|
|
|
last_run_at=orm.last_run_at,
|
|
|
|
|
|
next_run_at=orm.next_run_at,
|
|
|
|
|
|
last_error=orm.last_error,
|
|
|
|
|
|
created_by=orm.created_by,
|
|
|
|
|
|
updated_by=orm.updated_by,
|
|
|
|
|
|
created_at=orm.created_at,
|
|
|
|
|
|
updated_at=orm.updated_at,
|
|
|
|
|
|
is_deleted=orm.is_deleted,
|
|
|
|
|
|
deleted_at=orm.deleted_at,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def orm_to_run_log(orm: ScheduledTaskRunLogORM | None) -> ScheduledTaskRunLogDC | None:
|
|
|
|
|
|
"""ORM → dataclass,``ScheduledTaskRunLog`` 字段一一映射。
|
|
|
|
|
|
|
|
|
|
|
|
入参为 None 时返回 None,避免调用方需额外判空。
|
|
|
|
|
|
"""
|
|
|
|
|
|
if orm is None:
|
|
|
|
|
|
return None
|
|
|
|
|
|
return ScheduledTaskRunLogDC(
|
|
|
|
|
|
id=orm.id,
|
|
|
|
|
|
task_id=orm.task_id,
|
|
|
|
|
|
run_id=orm.run_id,
|
|
|
|
|
|
triggered_by=orm.triggered_by,
|
|
|
|
|
|
status=orm.status,
|
|
|
|
|
|
error_message=orm.error_message,
|
|
|
|
|
|
output=orm.output,
|
|
|
|
|
|
started_at=orm.started_at,
|
|
|
|
|
|
finished_at=orm.finished_at,
|
|
|
|
|
|
created_by=orm.created_by,
|
|
|
|
|
|
updated_by=orm.updated_by,
|
|
|
|
|
|
created_at=orm.created_at,
|
|
|
|
|
|
updated_at=orm.updated_at,
|
|
|
|
|
|
is_deleted=orm.is_deleted,
|
|
|
|
|
|
deleted_at=orm.deleted_at,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def daily_stat_row_to_dataclass(row: Any) -> DailyStatDC:
|
|
|
|
|
|
"""仓储层 ``DailyStat`` → core ``DailyStat`` dataclass,按字段名映射。
|
|
|
|
|
|
|
|
|
|
|
|
仓储层 ``DailyStat`` 是 dataclass,row 可能是 NamedTuple 或 dataclass 实例,
|
|
|
|
|
|
统一按字段名读取并转换为 core dataclass。
|
|
|
|
|
|
"""
|
|
|
|
|
|
return DailyStatDC(
|
|
|
|
|
|
stat_date=row.stat_date,
|
|
|
|
|
|
handler_name=row.handler_name,
|
|
|
|
|
|
success_count=int(row.success_count),
|
|
|
|
|
|
failure_count=int(row.failure_count),
|
|
|
|
|
|
timeout_count=int(row.timeout_count),
|
|
|
|
|
|
dead_letter_count=int(row.dead_letter_count),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def handler_summary_row_to_dataclass(row: Any) -> HandlerSummaryDC:
|
|
|
|
|
|
"""仓储层 ``HandlerSummary`` → core ``HandlerSummary`` dataclass,按字段名映射。
|
|
|
|
|
|
|
|
|
|
|
|
仓储层 ``HandlerSummary`` 是 dataclass,row 可能是 NamedTuple 或 dataclass 实例,
|
|
|
|
|
|
统一按字段名读取并转换为 core dataclass。
|
|
|
|
|
|
"""
|
|
|
|
|
|
return HandlerSummaryDC(
|
|
|
|
|
|
name=row.name,
|
|
|
|
|
|
task_count=int(row.task_count),
|
|
|
|
|
|
last_active_at=row.last_active_at,
|
|
|
|
|
|
)
|