582 lines
18 KiB
Python
582 lines
18 KiB
Python
|
|
"""scheduler 限界上下文用例 DTO 定义。
|
|||
|
|
|
|||
|
|
包含:
|
|||
|
|
- 任务管理 Input/Output DTO(CRUD / 暂停 / 恢复 / 手动触发)
|
|||
|
|
- 执行日志 Input/Output DTO(列表查询)
|
|||
|
|
- 可观测性 Output DTO(handler 摘要 / 状态计数 / 即将执行 / 健康检查)
|
|||
|
|
- 运维恢复 Input/Output DTO(回收僵尸执行)
|
|||
|
|
|
|||
|
|
字段约束(``min_length`` / ``max_length`` / ``pattern`` / ``ge`` / ``le``)在 DTO 中
|
|||
|
|
声明,由 Pydantic 在构造时校验。DateTime 字段在 Output DTO 中以 ISO 格式字符串
|
|||
|
|
表达(``str | None``),由 Router 层在序列化时格式化。JSON 字段(如 ``payload``)
|
|||
|
|
使用 ``dict[str, Any]``。
|
|||
|
|
|
|||
|
|
对齐 ``external_systems/use_cases/dto/system.py`` 写法。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
from typing import Any
|
|||
|
|
|
|||
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# 任务管理 DTO
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CreateTaskInput(BaseModel):
|
|||
|
|
"""创建定时任务输入 DTO。
|
|||
|
|
|
|||
|
|
``schedule_kind`` 决定调度类型:``cron`` 必填 ``cron_expression``,
|
|||
|
|
``at`` 必填 ``run_at``。``task_id`` 由系统生成(UUID4),不接受客户端指定。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
handler_name: str = Field(
|
|||
|
|
...,
|
|||
|
|
min_length=1,
|
|||
|
|
max_length=128,
|
|||
|
|
pattern=r"^[a-zA-Z_][a-zA-Z0-9_-]*$",
|
|||
|
|
description="handler 标识,与 TaskHandler.name 引用一致",
|
|||
|
|
)
|
|||
|
|
owner_scope: str = Field(
|
|||
|
|
...,
|
|||
|
|
min_length=1,
|
|||
|
|
max_length=64,
|
|||
|
|
description="任务归属范围:system(平台级)/ business(业务方)",
|
|||
|
|
)
|
|||
|
|
owner_id: str = Field(
|
|||
|
|
...,
|
|||
|
|
min_length=1,
|
|||
|
|
max_length=128,
|
|||
|
|
description="业务方标识,格式如 channel_type:account_id",
|
|||
|
|
)
|
|||
|
|
schedule_kind: str = Field(
|
|||
|
|
...,
|
|||
|
|
pattern=r"^(cron|at)$",
|
|||
|
|
description="调度类型:cron(周期)/ at(一次性)",
|
|||
|
|
)
|
|||
|
|
cron_expression: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
max_length=128,
|
|||
|
|
description="cron 表达式(标准 5 字段),schedule_kind=cron 时必填",
|
|||
|
|
)
|
|||
|
|
run_at: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="一次性执行时间(ISO 格式字符串),schedule_kind=at 时必填且须为未来时间",
|
|||
|
|
)
|
|||
|
|
tz: str = Field(
|
|||
|
|
default="Asia/Shanghai",
|
|||
|
|
max_length=64,
|
|||
|
|
description="时区标识,如 Asia/Shanghai",
|
|||
|
|
)
|
|||
|
|
payload: dict[str, Any] = Field(
|
|||
|
|
default_factory=dict,
|
|||
|
|
description="任务执行时传给 handler 的参数,序列化后不超过 64KB",
|
|||
|
|
)
|
|||
|
|
enabled: bool = Field(default=True, description="是否启用")
|
|||
|
|
delete_after_run: bool = Field(
|
|||
|
|
default=False,
|
|||
|
|
description="一次性任务执行完成后是否自动删除(schedule_kind=at 时有意义)",
|
|||
|
|
)
|
|||
|
|
block_strategy: str = Field(
|
|||
|
|
default="discard_later",
|
|||
|
|
pattern=r"^(discard_later)$",
|
|||
|
|
description="阻塞策略:discard_later(存在 running 执行时丢弃本次触发)",
|
|||
|
|
)
|
|||
|
|
created_by: str = Field(default="system", max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UpdateTaskInput(BaseModel):
|
|||
|
|
"""更新定时任务输入 DTO。
|
|||
|
|
|
|||
|
|
允许更新字段(PRD §FR-ST-05):``cron_expression`` / ``run_at`` / ``tz`` /
|
|||
|
|
``payload`` / ``enabled`` / ``delete_after_run`` / ``owner_scope`` / ``owner_id``。
|
|||
|
|
``schedule_kind`` 与 ``handler_name`` 不可修改。修改 ``cron_expression`` 时
|
|||
|
|
由用例层重新计算 ``next_run_at`` 与 ``stagger_seconds``。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(
|
|||
|
|
...,
|
|||
|
|
min_length=1,
|
|||
|
|
max_length=64,
|
|||
|
|
description="任务标识(UUID4)",
|
|||
|
|
)
|
|||
|
|
cron_expression: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
max_length=128,
|
|||
|
|
description="新的 cron 表达式,传入时重算 next_run_at 与 stagger_seconds",
|
|||
|
|
)
|
|||
|
|
run_at: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="新的一次性执行时间(ISO 格式字符串)",
|
|||
|
|
)
|
|||
|
|
tz: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
max_length=64,
|
|||
|
|
description="新的时区标识",
|
|||
|
|
)
|
|||
|
|
payload: dict[str, Any] | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="新的 payload,传入时整体替换",
|
|||
|
|
)
|
|||
|
|
enabled: bool | None = Field(default=None, description="是否启用")
|
|||
|
|
delete_after_run: bool | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="一次性任务执行完成后是否自动删除",
|
|||
|
|
)
|
|||
|
|
owner_scope: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
min_length=1,
|
|||
|
|
max_length=64,
|
|||
|
|
)
|
|||
|
|
owner_id: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
min_length=1,
|
|||
|
|
max_length=128,
|
|||
|
|
)
|
|||
|
|
updated_by: str | None = Field(default=None, max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class GetTaskInput(BaseModel):
|
|||
|
|
"""获取任务详情输入 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DeleteTaskInput(BaseModel):
|
|||
|
|
"""删除任务输入 DTO(软删除)。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
updated_by: str | None = Field(default=None, max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class PauseTaskInput(BaseModel):
|
|||
|
|
"""暂停任务输入 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
updated_by: str | None = Field(default=None, max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ResumeTaskInput(BaseModel):
|
|||
|
|
"""恢复任务输入 DTO。
|
|||
|
|
|
|||
|
|
对 ``status=paused`` 任务调用 ``pause`` 的逆操作;对 ``status=dead_letter``
|
|||
|
|
任务重置 ``consecutive_errors`` / ``last_error`` 并恢复为 ``active``。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
updated_by: str | None = Field(default=None, max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TriggerTaskInput(BaseModel):
|
|||
|
|
"""手动触发任务输入 DTO。
|
|||
|
|
|
|||
|
|
``triggered_by`` 标识触发来源(``auto`` / ``manual``),手动触发不入正常
|
|||
|
|
``next_run_at`` 流,不影响下一次自动执行。死信任务允许手动触发用于验证
|
|||
|
|
handler 是否已修复,但不重置 ``status``。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
triggered_by: str = Field(
|
|||
|
|
default="manual",
|
|||
|
|
pattern=r"^(auto|manual)$",
|
|||
|
|
description="触发来源:auto(tick 自动)/ manual(管理员手动)",
|
|||
|
|
)
|
|||
|
|
payload: dict[str, Any] | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="可选 payload 覆盖,None 时使用任务定义中的 payload",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TaskOutput(BaseModel):
|
|||
|
|
"""任务输出 DTO。字段与 ``ScheduledTask`` dataclass 对齐。
|
|||
|
|
|
|||
|
|
DateTime 字段(``run_at`` / ``last_run_at`` / ``next_run_at`` / ``created_at``
|
|||
|
|
/ ``updated_at`` / ``deleted_at``)以 ISO 格式字符串表达,由 Router 层格式化。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str
|
|||
|
|
handler_name: str
|
|||
|
|
owner_scope: str
|
|||
|
|
owner_id: str
|
|||
|
|
schedule_kind: str
|
|||
|
|
cron_expression: str | None = None
|
|||
|
|
run_at: str | None = None
|
|||
|
|
tz: str = "Asia/Shanghai"
|
|||
|
|
payload: dict[str, Any] = Field(default_factory=dict)
|
|||
|
|
enabled: bool = True
|
|||
|
|
delete_after_run: bool = False
|
|||
|
|
block_strategy: str = "discard_later"
|
|||
|
|
stagger_seconds: int | None = None
|
|||
|
|
consecutive_errors: int = 0
|
|||
|
|
status: str = "active"
|
|||
|
|
last_run_at: str | None = None
|
|||
|
|
next_run_at: str | None = None
|
|||
|
|
last_error: str | None = None
|
|||
|
|
created_by: str = "system"
|
|||
|
|
updated_by: str | None = None
|
|||
|
|
id: int | None = None
|
|||
|
|
created_at: str | None = None
|
|||
|
|
updated_at: str | None = None
|
|||
|
|
is_deleted: int = 0
|
|||
|
|
deleted_at: str | None = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListTasksInput(BaseModel):
|
|||
|
|
"""列出任务输入 DTO。
|
|||
|
|
|
|||
|
|
过滤字段对齐 PRD §FR-ST-05 列表 API 查询参数与仓储层 ``list`` 方法签名。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
page: int = Field(default=1, ge=1, description="页码,从 1 开始")
|
|||
|
|
page_size: int = Field(default=20, ge=1, le=100, description="每页条数,最大 100")
|
|||
|
|
owner_scope: str | None = Field(default=None, max_length=64)
|
|||
|
|
owner_id: str | None = Field(default=None, max_length=128)
|
|||
|
|
handler_name: str | None = Field(default=None, max_length=128)
|
|||
|
|
enabled: bool | None = None
|
|||
|
|
status: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
pattern=r"^(active|paused|dead_letter)$",
|
|||
|
|
description="任务状态过滤",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListTasksOutput(BaseModel):
|
|||
|
|
"""列出任务输出 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
items: list[TaskOutput] = Field(default_factory=list)
|
|||
|
|
total: int = 0
|
|||
|
|
page: int = 1
|
|||
|
|
page_size: int = 20
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# 执行日志 DTO
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListRunLogsInput(BaseModel):
|
|||
|
|
"""列出执行日志输入 DTO。
|
|||
|
|
|
|||
|
|
``task_id`` 必填,按任务维度查询执行明细。``status`` / ``start_date`` /
|
|||
|
|
``end_date`` 为可选过滤字段,``start_date`` / ``end_date`` 为 ISO 格式字符串,
|
|||
|
|
由用例层解析为 ``datetime`` 后传入仓储层。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
page: int = Field(default=1, ge=1)
|
|||
|
|
page_size: int = Field(default=20, ge=1, le=100)
|
|||
|
|
status: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
pattern=r"^(running|success|failure|timeout|skipped)$",
|
|||
|
|
description="执行状态过滤",
|
|||
|
|
)
|
|||
|
|
start_date: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="起始时间(ISO 格式字符串),按 started_at 过滤",
|
|||
|
|
)
|
|||
|
|
end_date: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="截止时间(ISO 格式字符串),按 started_at 过滤",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class GetRunLogInput(BaseModel):
|
|||
|
|
"""获取执行日志详情输入 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
run_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RunLogOutput(BaseModel):
|
|||
|
|
"""执行日志输出 DTO。字段与 ``ScheduledTaskRunLog`` dataclass 对齐。
|
|||
|
|
|
|||
|
|
DateTime 字段(``started_at`` / ``finished_at`` / ``created_at`` /
|
|||
|
|
``updated_at`` / ``deleted_at``)以 ISO 格式字符串表达。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str
|
|||
|
|
run_id: str
|
|||
|
|
triggered_by: str = "auto"
|
|||
|
|
status: str = "running"
|
|||
|
|
error_message: str | None = None
|
|||
|
|
output: dict[str, Any] | None = None
|
|||
|
|
started_at: str | None = None
|
|||
|
|
finished_at: str | None = None
|
|||
|
|
created_by: str = "system"
|
|||
|
|
updated_by: str | None = None
|
|||
|
|
id: int | None = None
|
|||
|
|
created_at: str | None = None
|
|||
|
|
updated_at: str | None = None
|
|||
|
|
is_deleted: int = 0
|
|||
|
|
deleted_at: str | None = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListRunLogsOutput(BaseModel):
|
|||
|
|
"""列出执行日志输出 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
items: list[RunLogOutput] = Field(default_factory=list)
|
|||
|
|
total: int = 0
|
|||
|
|
page: int = 1
|
|||
|
|
page_size: int = 20
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListAllRunLogsInput(BaseModel):
|
|||
|
|
"""跨任务执行日志查询输入 DTO。
|
|||
|
|
|
|||
|
|
``task_id`` 与 ``status`` 均可选,但不可同时为空(由用例层校验),
|
|||
|
|
避免无过滤的全表扫描。``start_date`` / ``end_date`` 为 ISO 格式字符串,
|
|||
|
|
由用例层解析为 ``datetime`` 后传入仓储层。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
page: int = Field(default=1, ge=1)
|
|||
|
|
page_size: int = Field(default=20, ge=1, le=100)
|
|||
|
|
status: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
pattern=r"^(running|success|failure|timeout|skipped)$",
|
|||
|
|
description="执行状态过滤",
|
|||
|
|
)
|
|||
|
|
start_date: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="起始时间(ISO 格式字符串),按 started_at 过滤",
|
|||
|
|
)
|
|||
|
|
end_date: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="截止时间(ISO 格式字符串),按 started_at 过滤",
|
|||
|
|
)
|
|||
|
|
task_id: str | None = Field(default=None, min_length=1, max_length=64)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# 可观测性 DTO
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
|
|||
|
|
class HandlerSummaryOutput(BaseModel):
|
|||
|
|
"""handler 聚合摘要输出 DTO。
|
|||
|
|
|
|||
|
|
对齐 PRD §FR-ST-05 ``GET /handlers`` 端点响应字段,走 DB 聚合
|
|||
|
|
(``task_repo.list_handler_summary``),不查内存注册表。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
handler_name: str
|
|||
|
|
description: str = ""
|
|||
|
|
task_count: int = 0
|
|||
|
|
active_task_count: int = 0
|
|||
|
|
last_active_at: str | None = None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListHandlerSummaryOutput(BaseModel):
|
|||
|
|
"""handler 聚合摘要列表输出 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
items: list[HandlerSummaryOutput] = Field(default_factory=list)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CountByStatusOutput(BaseModel):
|
|||
|
|
"""任务状态计数输出 DTO。
|
|||
|
|
|
|||
|
|
对齐 PRD §FR-ST-07 健康检查与指标需求,按 ``status`` 维度统计任务数。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
active: int = 0
|
|||
|
|
paused: int = 0
|
|||
|
|
dead_letter: int = 0
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListUpcomingInput(BaseModel):
|
|||
|
|
"""即将执行任务查询输入 DTO。
|
|||
|
|
|
|||
|
|
对齐 PRD §FR-ST-09 统计查询,按 ``next_run_at`` 升序返回即将执行的任务列表。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
limit: int = Field(default=100, ge=1, le=1000, description="返回条数上限")
|
|||
|
|
handler_name: str | None = Field(default=None, max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListUpcomingOutput(BaseModel):
|
|||
|
|
"""即将执行任务列表输出 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
items: list[TaskOutput] = Field(default_factory=list)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class GetHealthOutput(BaseModel):
|
|||
|
|
"""调度器健康检查输出 DTO。
|
|||
|
|
|
|||
|
|
对齐 PRD §FR-ST-07 健康检查端点响应:``status`` 表示 worker 是否在最近
|
|||
|
|
5 分钟内执行 tick;``last_tick_at`` 为最近 tick 时间;``active_task_count``
|
|||
|
|
/ ``dead_letter_count`` / ``running_count`` 为关键状态计数。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
status: str = Field(
|
|||
|
|
default="healthy",
|
|||
|
|
pattern=r"^(healthy|unhealthy)$",
|
|||
|
|
description="健康状态:healthy(最近 5 分钟内有 tick)/ unhealthy",
|
|||
|
|
)
|
|||
|
|
last_tick_at: str | None = Field(default=None, description="最近 tick 时间(ISO 格式字符串)")
|
|||
|
|
active_task_count: int = 0
|
|||
|
|
dead_letter_count: int = 0
|
|||
|
|
running_count: int = 0
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# 日聚合统计 DTO
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListDailyStatsInput(BaseModel):
|
|||
|
|
"""日聚合统计查询输入 DTO。
|
|||
|
|
|
|||
|
|
``start_date`` / ``end_date`` 为 ISO 日期字符串(``YYYY-MM-DD``),由用例层
|
|||
|
|
解析为 ``date`` 后传入仓储层。日期范围上限 90 天。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
start_date: str = Field(..., description="ISO 日期 YYYY-MM-DD")
|
|||
|
|
end_date: str = Field(..., description="ISO 日期 YYYY-MM-DD")
|
|||
|
|
handler_name: str | None = Field(default=None, max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class DailyStatOutput(BaseModel):
|
|||
|
|
"""日聚合统计输出 DTO。字段对齐 ``DailyStat`` dataclass,补充派生字段。
|
|||
|
|
|
|||
|
|
``total_count`` / ``success_rate`` 为派生字段,由 mapper 计算:
|
|||
|
|
``total_count = success_count + failure_count + timeout_count``,
|
|||
|
|
``success_rate = success_count / total_count if total_count > 0 else 0.0``。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
stat_date: str
|
|||
|
|
handler_name: str
|
|||
|
|
success_count: int = 0
|
|||
|
|
failure_count: int = 0
|
|||
|
|
timeout_count: int = 0
|
|||
|
|
dead_letter_count: int = 0
|
|||
|
|
total_count: int = 0
|
|||
|
|
success_rate: float = 0.0
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListDailyStatsOutput(BaseModel):
|
|||
|
|
"""日聚合统计列表输出 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
items: list[DailyStatOutput] = Field(default_factory=list)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# 运维恢复 DTO
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ReclaimStaleRunsInput(BaseModel):
|
|||
|
|
"""回收僵尸执行输入 DTO。
|
|||
|
|
|
|||
|
|
用于 scheduler 重启后回收 ``status=running`` 但实际已超时的执行记录,
|
|||
|
|
将其标记为 ``failure`` 并写入回收错误信息。``timeout_seconds`` 为可选参数,
|
|||
|
|
未传入时由用例层从配置取默认值。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
timeout_seconds: int | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
ge=1,
|
|||
|
|
description="执行超时阈值(秒),None 时从配置取默认值",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ReclaimStaleRunsOutput(BaseModel):
|
|||
|
|
"""回收僵尸执行输出 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
reclaimed_count: int = 0
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
# 任务回收站 DTO
|
|||
|
|
# ---------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
|
|||
|
|
class ListDeletedTasksInput(BaseModel):
|
|||
|
|
"""列出已删除任务(回收站)输入 DTO。
|
|||
|
|
|
|||
|
|
``start_date`` / ``end_date`` 为 ISO 格式字符串,由用例层解析为 ``datetime``
|
|||
|
|
后按 ``deleted_at`` 过滤。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
page: int = Field(default=1, ge=1)
|
|||
|
|
page_size: int = Field(default=20, ge=1, le=100)
|
|||
|
|
start_date: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="起始时间(ISO 格式字符串),按 deleted_at 过滤",
|
|||
|
|
)
|
|||
|
|
end_date: str | None = Field(
|
|||
|
|
default=None,
|
|||
|
|
description="截止时间(ISO 格式字符串),按 deleted_at 过滤",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class RestoreTaskInput(BaseModel):
|
|||
|
|
"""恢复已删除任务输入 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
updated_by: str | None = Field(default=None, max_length=128)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class HardDeleteTaskInput(BaseModel):
|
|||
|
|
"""硬删除任务输入 DTO。"""
|
|||
|
|
|
|||
|
|
model_config = ConfigDict(frozen=True)
|
|||
|
|
|
|||
|
|
task_id: str = Field(..., min_length=1, max_length=64)
|
|||
|
|
updated_by: str | None = Field(default=None, max_length=128)
|