93 lines
3.7 KiB
Python
93 lines
3.7 KiB
Python
"""任务 + 任务日志。"""
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
|
||
from sqlalchemy import (
|
||
JSON,
|
||
DateTime,
|
||
ForeignKey,
|
||
Integer,
|
||
String,
|
||
Text,
|
||
UniqueConstraint,
|
||
func,
|
||
)
|
||
from sqlalchemy.orm import Mapped, mapped_column
|
||
|
||
from app.db.base import Base, TimestampMixin
|
||
|
||
|
||
class Task(Base, TimestampMixin):
|
||
__tablename__ = "task"
|
||
|
||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||
# 任务编号(人类可读),由服务层生成,如 T20260424-0001
|
||
task_no: Mapped[str] = mapped_column(String(32), nullable=False, unique=True, index=True)
|
||
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
||
|
||
skill_id: Mapped[int] = mapped_column(
|
||
ForeignKey("skill.id", ondelete="RESTRICT"), nullable=False, index=True
|
||
)
|
||
skill_code: Mapped[str] = mapped_column(String(64), nullable=False)
|
||
product_id: Mapped[int | None] = mapped_column(
|
||
ForeignKey("product.id", ondelete="SET NULL"), nullable=True, index=True
|
||
)
|
||
|
||
# LOBSTER / SCRIPT
|
||
executor_type: Mapped[str] = mapped_column(String(16), nullable=False)
|
||
# 用户输入的参数(JSON Schema 校验过)
|
||
input_data: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||
# 经过 param_mapping 翻译后传给执行器的实际参数
|
||
resolved_input: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||
# 执行器返回的结果
|
||
result_data: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||
|
||
# PENDING / QUEUED / RUNNING / SUCCEEDED / FAILED / TIMEOUT / CANCELED
|
||
status: Mapped[str] = mapped_column(
|
||
String(16), nullable=False, default="PENDING", index=True
|
||
)
|
||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||
|
||
# 外部执行系统返回的参考 ID(龙虾 run_id / celery task id)
|
||
external_ref: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
|
||
|
||
# 任务触发来源:wizard / retry / schedule
|
||
source: Mapped[str] = mapped_column(String(16), default="wizard", nullable=False)
|
||
schedule_id: Mapped[int | None] = mapped_column(
|
||
ForeignKey("schedule.id", ondelete="SET NULL"), nullable=True, index=True
|
||
)
|
||
retry_of_id: Mapped[int | None] = mapped_column(
|
||
ForeignKey("task.id", ondelete="SET NULL"), nullable=True
|
||
)
|
||
|
||
operator_id: Mapped[int | None] = mapped_column(
|
||
ForeignKey("user.id", ondelete="SET NULL"), nullable=True, index=True
|
||
)
|
||
operator_name: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||
|
||
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||
cost_ms: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
||
timeout_seconds: Mapped[int] = mapped_column(Integer, default=60, nullable=False)
|
||
|
||
|
||
class TaskLog(Base):
|
||
__tablename__ = "task_log"
|
||
__table_args__ = (
|
||
UniqueConstraint("task_id", "external_event_id", name="uq_task_log_event"),
|
||
)
|
||
|
||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||
task_id: Mapped[int] = mapped_column(
|
||
ForeignKey("task.id", ondelete="CASCADE"), nullable=False, index=True
|
||
)
|
||
# INFO / WARN / ERROR / STATUS
|
||
level: Mapped[str] = mapped_column(String(8), default="INFO", nullable=False)
|
||
message: Mapped[str] = mapped_column(Text, nullable=False)
|
||
# 龙虾回调带的事件 id(同一事件不重复写)
|
||
external_event_id: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||
created_at: Mapped[datetime] = mapped_column(
|
||
DateTime(timezone=True), server_default=func.now(), nullable=False, index=True
|
||
)
|