from __future__ import annotations import os from collections.abc import Iterable from fastapi import HTTPException from sqlalchemy import or_, select from sqlalchemy.ext.asyncio import AsyncSession from app.enums.video_upscale import VideoUpscaleTaskStatus from app.models.video_upscale_task import VideoUpscaleTask async def assert_no_recoverable_failed_upscale_tasks( db: AsyncSession, chat_task_ids: Iterable[str] | None = None, *, generation_record_ids: Iterable[str] | None = None, ) -> None: """阻止删除仍保留本地源视频、可由管理员人工恢复的超分失败任务。""" chat_ids = list(dict.fromkeys(str(item) for item in (chat_task_ids or []) if item)) record_ids = list(dict.fromkeys(str(item) for item in (generation_record_ids or []) if item)) if not chat_ids and not record_ids: return owner_conditions = [] if chat_ids: owner_conditions.append(VideoUpscaleTask.chat_generation_task_id.in_(chat_ids)) if record_ids: owner_conditions.append(VideoUpscaleTask.generation_record_id.in_(record_ids)) result = await db.execute( select( VideoUpscaleTask.chat_generation_task_id, VideoUpscaleTask.generation_record_id, VideoUpscaleTask.source_local_path, ).where( or_(*owner_conditions), VideoUpscaleTask.status == VideoUpscaleTaskStatus.FAILED.value, VideoUpscaleTask.source_local_path.isnot(None), VideoUpscaleTask.source_deleted_at.is_(None), ) ) recoverable = [] for chat_task_id, generation_record_id, source_local_path in result.all(): if source_local_path and os.path.isfile(str(source_local_path)): recoverable.append( { "owner_type": "chat_generation_task" if chat_task_id else "generation_record", "owner_id": str(chat_task_id or generation_record_id), } ) if recoverable: raise HTTPException( status_code=409, detail={ "message": "当前生成任务异常暂不能删除", "items": recoverable, "task_count": len(recoverable), }, )