ForcePilot/backend/package/yuxi/scheduler/adapters/persistence/mappers.py
Kris 2a401450cb refactor(scheduler): 整理代码格式与导入,新增调度器配置与handler注册框架
本次提交包含多类改进:
1.  清理冗余空行、修复导入格式与类型检查占位
2.  新增external_systems与scheduler上下文的handler注册框架
3.  补全调度器相关配置项与环境变量覆盖逻辑
4.  新增配置校验与敏感字段脱敏逻辑
5.  简化协议方法声明格式
2026-06-22 21:22:07 +08:00

134 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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 层均有同名 dataclassmapper 按字段名转换。
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
from yuxi.scheduler.core.models import (
DailyStat as DailyStatDC,
)
from yuxi.scheduler.core.models import (
HandlerSummary as HandlerSummaryDC,
)
from yuxi.scheduler.core.models import (
ScheduledTask as ScheduledTaskDC,
)
from yuxi.scheduler.core.models import (
ScheduledTaskRunLog as ScheduledTaskRunLogDC,
)
if TYPE_CHECKING:
from yuxi.storage.postgres.models_scheduler import (
ScheduledTask as ScheduledTaskORM,
)
from yuxi.storage.postgres.models_scheduler import (
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`` 是 dataclassrow 可能是 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`` 是 dataclassrow 可能是 NamedTuple 或 dataclass 实例,
统一按字段名读取并转换为 core dataclass。
"""
return HandlerSummaryDC(
name=row.name,
task_count=int(row.task_count),
last_active_at=row.last_active_at,
)