mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
refactor: 移除 Python 后端源码,全面迁移至 Rust gateway 架构
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/ - 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层 - 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构 - 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations) - 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image - 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
This commit is contained in:
134
_deprecated_py_src/services/task/video/operations.py
Normal file
134
_deprecated_py_src/services/task/video/operations.py
Normal file
@@ -0,0 +1,134 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from src.models.database import VideoTask
|
||||
from src.services.task.core.exceptions import TaskNotFoundError
|
||||
from src.services.task.core.schema import TaskStatusResult
|
||||
from src.services.task.video.billing import VideoTaskBillingService
|
||||
from src.services.task.video.cancel import VideoTaskCancelService
|
||||
|
||||
|
||||
class VideoTaskOperationsService:
|
||||
"""视频任务相关应用服务(轮询/取消/终态结算)。"""
|
||||
|
||||
def __init__(self, db: Session, redis_client: Any | None = None) -> None:
|
||||
self.db = db
|
||||
self.redis = redis_client
|
||||
self._billing_ops = VideoTaskBillingService(db)
|
||||
self._cancel_ops = VideoTaskCancelService(db)
|
||||
|
||||
def _extract_short_id(self, task_id: str) -> str:
|
||||
# Keep the parsing rule consistent with handlers:
|
||||
# - models/{model}/operations/{short_id}
|
||||
# - operations/{short_id}
|
||||
# - {short_id}
|
||||
return task_id.rsplit("/", 1)[-1] if "/" in task_id else task_id
|
||||
|
||||
def _get_video_task_for_user(self, task_id: str, *, user_id: str) -> Any:
|
||||
"""
|
||||
Resolve a video task by:
|
||||
- internal UUID (VideoTask.id)
|
||||
- external operation id (VideoTask.short_id)
|
||||
"""
|
||||
task = (
|
||||
self.db.query(VideoTask)
|
||||
.filter(VideoTask.id == task_id, VideoTask.user_id == user_id)
|
||||
.first()
|
||||
)
|
||||
if task:
|
||||
return task
|
||||
|
||||
short_id = self._extract_short_id(task_id)
|
||||
task = (
|
||||
self.db.query(VideoTask)
|
||||
.filter(VideoTask.short_id == short_id, VideoTask.user_id == user_id)
|
||||
.first()
|
||||
)
|
||||
if not task:
|
||||
raise TaskNotFoundError(task_id)
|
||||
return task
|
||||
|
||||
async def poll(self, task_id: str, *, user_id: str) -> TaskStatusResult:
|
||||
"""Read task status from DB (does not trigger polling)."""
|
||||
task = self._get_video_task_for_user(task_id, user_id=user_id)
|
||||
|
||||
result_url = None
|
||||
if getattr(task, "status", None) == "completed":
|
||||
result_url = getattr(task, "video_url", None)
|
||||
|
||||
error_message = None
|
||||
if getattr(task, "status", None) == "failed":
|
||||
error_message = getattr(task, "error_message", None) or getattr(
|
||||
task, "error_code", None
|
||||
)
|
||||
|
||||
return TaskStatusResult(
|
||||
task_id=str(getattr(task, "id", task_id)),
|
||||
status=str(getattr(task, "status", "unknown")),
|
||||
progress_percent=int(getattr(task, "progress_percent", 0) or 0),
|
||||
result_url=result_url,
|
||||
error_message=str(error_message) if error_message else None,
|
||||
provider_id=(
|
||||
str(getattr(task, "provider_id", None))
|
||||
if getattr(task, "provider_id", None)
|
||||
else None
|
||||
),
|
||||
provider_name=(
|
||||
str(getattr(task, "provider_name", None))
|
||||
if getattr(task, "provider_name", None)
|
||||
else None
|
||||
),
|
||||
endpoint_id=(
|
||||
str(getattr(task, "endpoint_id", None))
|
||||
if getattr(task, "endpoint_id", None)
|
||||
else None
|
||||
),
|
||||
key_id=str(getattr(task, "key_id", None)) if getattr(task, "key_id", None) else None,
|
||||
)
|
||||
|
||||
async def poll_now(self, task_id: str, *, user_id: str) -> TaskStatusResult:
|
||||
"""
|
||||
Trigger a single polling attempt (best-effort), then return latest DB status.
|
||||
|
||||
Note: this uses the poller adapter's single-task method and may hold a DB
|
||||
connection during the upstream HTTP request; keep usage low.
|
||||
"""
|
||||
from src.services.task.video.poller_adapter import VideoTaskPollerAdapter
|
||||
|
||||
task = self._get_video_task_for_user(task_id, user_id=user_id)
|
||||
adapter = VideoTaskPollerAdapter()
|
||||
await adapter.poll_single_task(self.db, task, redis_client=self.redis)
|
||||
self.db.commit()
|
||||
return await self.poll(task_id, user_id=user_id)
|
||||
|
||||
async def cancel(
|
||||
self,
|
||||
task_id: str,
|
||||
*,
|
||||
user_id: str,
|
||||
original_headers: dict[str, str] | None = None,
|
||||
) -> Any:
|
||||
from fastapi import HTTPException
|
||||
|
||||
try:
|
||||
task = self._get_video_task_for_user(task_id, user_id=user_id)
|
||||
except TaskNotFoundError:
|
||||
raise HTTPException(status_code=404, detail="Video task not found")
|
||||
return await self._cancel_ops.cancel_task(
|
||||
task=task,
|
||||
task_id=task_id,
|
||||
original_headers=original_headers,
|
||||
)
|
||||
|
||||
async def finalize_video_task(self, task: Any) -> bool:
|
||||
return await self._billing_ops.finalize_video_task(task)
|
||||
|
||||
async def finalize(self, task_id: str) -> bool:
|
||||
"""Finalize a task by internal id (best-effort)."""
|
||||
task = self.db.query(VideoTask).filter(VideoTask.id == task_id).first()
|
||||
if not task:
|
||||
return False
|
||||
return await self.finalize_video_task(task)
|
||||
Reference in New Issue
Block a user