43366f290c
Deploy / Build Production Runtime Images (push) Has been skipped
Deploy / Deploy Production (push) Has been skipped
Deploy / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 181h43m15s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 181h43m19s
Deploy / Deploy Staging (push) Failing after 181h59m26s
CI/CD Pipeline / Frontend Lint (push) Failing after 181h59m53s
CI/CD Pipeline / Validate Code Quality And Tests (push) Failing after 182h0m0s
- 新增 Job 领域模型(状态机、JobType/JobStatus 枚举) - 新增 JobRepository 端口 + SQLAlchemy 实现 - 新增 10 个 Use Cases(CreateJob/Submit/Progress/Complete/Fail/Retry/Cancel/Get/List/Statistics) - 新增 JobService 服务层,集成 VideoComposeService - 新增 Pydantic schemas + RESTful API 路由 - 新增 Celery compose_video 任务(含进度追踪) - 新增数据库迁移 018(jobs 表) - 新增 44 个单元测试,全部通过 - 修复状态转换:允许 pending→success(快速完成场景)
110 lines
3.1 KiB
Python
Executable File
110 lines
3.1 KiB
Python
Executable File
"""Job API schemas — Phase 8 任务 2.10."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime
|
||
from typing import Any, Optional
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class CreateJobRequest(BaseModel):
|
||
"""创建任务请求体。"""
|
||
|
||
project_id: str = Field(..., min_length=1, description="项目 ID")
|
||
job_type: str = Field(
|
||
...,
|
||
description="任务类型: video_compose / render_edit_plan / asset_ingest / classification / voice_extraction / generation",
|
||
)
|
||
payload: dict[str, Any] = Field(default_factory=dict, description="任务输入参数")
|
||
source_id: str = Field(default="", description="关联的业务实体 ID(如 edit_plan_id)")
|
||
max_retries: int = Field(default=3, ge=0, le=10, description="最大重试次数")
|
||
|
||
|
||
class UpdateProgressRequest(BaseModel):
|
||
"""更新任务进度请求体。"""
|
||
|
||
progress: float = Field(..., ge=0.0, le=100.0, description="进度百分比")
|
||
current_stage: str = Field(default="", description="当前阶段描述")
|
||
|
||
|
||
class CompleteJobRequest(BaseModel):
|
||
"""完成任务请求体。"""
|
||
|
||
result: dict[str, Any] = Field(default_factory=dict, description="任务结果")
|
||
|
||
|
||
class FailJobRequest(BaseModel):
|
||
"""标记任务失败请求体。"""
|
||
|
||
error_message: str = Field(..., min_length=1, description="错误信息")
|
||
|
||
|
||
class JobResponse(BaseModel):
|
||
"""任务响应体。"""
|
||
|
||
id: str
|
||
project_id: str
|
||
job_type: str
|
||
status: str
|
||
progress: float
|
||
current_stage: str
|
||
payload: dict[str, Any]
|
||
result: dict[str, Any]
|
||
error_message: str
|
||
retry_count: int
|
||
max_retries: int
|
||
celery_task_id: str
|
||
source_id: str
|
||
created_by_user_id: str
|
||
is_retryable: bool
|
||
started_at: Optional[datetime] = None
|
||
completed_at: Optional[datetime] = None
|
||
created_at: datetime
|
||
updated_at: datetime
|
||
|
||
model_config = {"from_attributes": True}
|
||
|
||
|
||
class ListJobsResponse(BaseModel):
|
||
"""任务列表响应体。"""
|
||
|
||
items: list[JobResponse]
|
||
total: int
|
||
|
||
|
||
class JobStatisticsResponse(BaseModel):
|
||
"""任务统计响应体。"""
|
||
|
||
project_id: str
|
||
total: int
|
||
pending: int
|
||
running: int
|
||
success: int
|
||
failed: int
|
||
|
||
|
||
def job_to_response(job) -> JobResponse:
|
||
"""将 Job 领域对象转换为 API 响应。"""
|
||
return JobResponse(
|
||
id=job.id,
|
||
project_id=job.project_id,
|
||
job_type=job.job_type.value if hasattr(job.job_type, "value") else str(job.job_type),
|
||
status=job.status.value if hasattr(job.status, "value") else str(job.status),
|
||
progress=job.progress,
|
||
current_stage=job.current_stage,
|
||
payload=job.payload,
|
||
result=job.result,
|
||
error_message=job.error_message,
|
||
retry_count=job.retry_count,
|
||
max_retries=job.max_retries,
|
||
celery_task_id=job.celery_task_id,
|
||
source_id=job.source_id,
|
||
created_by_user_id=job.created_by_user_id,
|
||
is_retryable=job.is_retryable,
|
||
started_at=job.started_at,
|
||
completed_at=job.completed_at,
|
||
created_at=job.created_at,
|
||
updated_at=job.updated_at,
|
||
)
|