拆镜复刻片段重试切片API | 真人/虚拟素材库上传API

This commit is contained in:
2026-07-09 12:07:28 +08:00
parent c05a52498e
commit 2b996e114e
18 changed files with 737 additions and 25 deletions
+94 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from urllib.parse import urlencode, unquote
from fastapi import APIRouter, Depends, HTTPException, Query, Request
from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile
from fastapi.responses import RedirectResponse
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@@ -18,6 +18,7 @@ from app.enums.private_portrait import (
)
from app.models.private_portrait import PrivatePortraitAsset, PrivatePortraitProject
from app.models.user import User
from app.enums.upload_resource import UploadResourceTypeEnum
from app.schemas.private_portrait import (
PrivatePortraitAssetCreate,
PrivatePortraitAssetListOut,
@@ -31,6 +32,7 @@ from app.schemas.private_portrait import (
PrivatePortraitProjectOut,
PrivatePortraitProjectUpdate,
PrivatePortraitSelectableAssetListOut,
PrivatePortraitUploadOut,
PrivatePortraitValidateSessionCreate,
PrivatePortraitValidateSessionOut,
build_private_portrait_enum_meta,
@@ -55,6 +57,8 @@ from app.services.private_portrait.project_service import (
refresh_project_counters,
soft_delete_project,
)
from app.services.private_portrait.upload_service import upload_private_portrait_asset_file
from app.services.upload_resource import cleanup_upload_resource_files_after_commit
from app.services.private_portrait.real_person.service import (
create_real_person_asset,
create_real_person_project,
@@ -124,6 +128,62 @@ async def get_private_portrait_enum_meta():
return build_private_portrait_enum_meta()
@router.post(
"/private-portrait/uploads/image",
response_model=PrivatePortraitUploadOut,
summary="上传真人图片素材",
description="上传真人图片素材并写入 UploadResourcemodule=private_portrait_real。创建素材时需回传 resource_id 到 upload_resource_id。",
)
async def upload_private_portrait_image(file: UploadFile = File(...), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
try:
out = await upload_private_portrait_asset_file(
db,
file=file,
current_user=current_user,
library_type=PrivatePortraitLibraryType.REAL_PERSON.value,
resource_type=UploadResourceTypeEnum.IMAGE.value,
)
await db.commit()
return out
except HTTPException:
await db.rollback()
raise
except Exception as exc:
await db.rollback()
raise HTTPException(status_code=500, detail=f"上传真人图片素材失败: {exc}")
@router.post(
"/private-portrait/uploads/video",
response_model=PrivatePortraitUploadOut,
summary="上传真人视频素材",
description="上传真人视频素材并写入 UploadResourcemodule=private_portrait_real。创建素材时需回传 resource_id 到 upload_resource_id。",
)
async def upload_private_portrait_video(
file: UploadFile = File(...),
duration_seconds: float | None = Query(None, description="客户端解析的视频秒数,服务端会写入 UploadResource 并在创建素材时回填"),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
try:
out = await upload_private_portrait_asset_file(
db,
file=file,
current_user=current_user,
library_type=PrivatePortraitLibraryType.REAL_PERSON.value,
resource_type=UploadResourceTypeEnum.VIDEO.value,
duration_seconds=duration_seconds,
)
await db.commit()
return out
except HTTPException:
await db.rollback()
raise
except Exception as exc:
await db.rollback()
raise HTTPException(status_code=500, detail=f"上传真人视频素材失败: {exc}")
@router.post(
"/private-portrait/projects",
response_model=PrivatePortraitProjectCreateWithValidateOut,
@@ -196,6 +256,7 @@ async def update_private_portrait_project(project_id: str, payload: PrivatePortr
async def delete_private_portrait_project(project_id: str, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
project = await soft_delete_project(db, user_id=current_user.id, project_id=project_id, library_type=PrivatePortraitLibraryType.REAL_PERSON.value)
project_id_snapshot = project.id
pending_upload_resource_ids = list(getattr(project, "_pending_upload_resource_ids", []) or [])
await db.commit()
try:
from app.tasks.private_portrait_asset_tasks import delete_private_portrait_project_remote
@@ -204,6 +265,21 @@ async def delete_private_portrait_project(project_id: str, current_user: User =
_log_task_dispatch_success(task_name="private_portrait.delete_project_remote", user_id=current_user.id, project_id=project_id_snapshot)
except Exception as exc:
_log_task_dispatch_failed(task_name="private_portrait.delete_project_remote", user_id=current_user.id, project_id=project_id_snapshot, exc=exc)
if pending_upload_resource_ids:
try:
await cleanup_upload_resource_files_after_commit(db, resource_ids=pending_upload_resource_ids)
await db.commit()
except Exception as exc:
await db.rollback()
log_operation_error(
domain=DOMAIN,
event_type=PrivatePortraitEventType.ASSET_UPLOAD_RELEASE_FAILED.value,
source=PrivatePortraitEventSource.API.value,
user_id=current_user.id,
project_id=project_id_snapshot,
exc=exc,
detail={"resource_ids": pending_upload_resource_ids},
)
return PrivatePortraitDeleteOut(success=True, remote_delete_status=PrivatePortraitRemoteDeleteStatus.PENDING.value)
@@ -337,6 +413,7 @@ async def delete_private_portrait_asset(asset_id: str, current_user: User = Depe
asset = await soft_delete_asset(db, user_id=current_user.id, asset_id=asset_id, library_type=PrivatePortraitLibraryType.REAL_PERSON.value)
asset_id_snapshot = asset.id
project_id_snapshot = asset.project_id
pending_upload_resource_ids = list(getattr(asset, "_pending_upload_resource_ids", []) or [])
await db.commit()
try:
from app.tasks.private_portrait_asset_tasks import delete_private_portrait_asset_remote
@@ -345,6 +422,22 @@ async def delete_private_portrait_asset(asset_id: str, current_user: User = Depe
_log_task_dispatch_success(task_name="private_portrait.delete_asset_remote", user_id=current_user.id, project_id=project_id_snapshot, asset_id=asset_id_snapshot)
except Exception as exc:
_log_task_dispatch_failed(task_name="private_portrait.delete_asset_remote", user_id=current_user.id, project_id=project_id_snapshot, asset_id=asset_id_snapshot, exc=exc)
if pending_upload_resource_ids:
try:
await cleanup_upload_resource_files_after_commit(db, resource_ids=pending_upload_resource_ids)
await db.commit()
except Exception as exc:
await db.rollback()
log_operation_error(
domain=DOMAIN,
event_type=PrivatePortraitEventType.ASSET_UPLOAD_RELEASE_FAILED.value,
source=PrivatePortraitEventSource.API.value,
user_id=current_user.id,
project_id=project_id_snapshot,
asset_id=asset_id_snapshot,
exc=exc,
detail={"resource_ids": pending_upload_resource_ids},
)
return PrivatePortraitDeleteOut(success=True, remote_delete_status=PrivatePortraitRemoteDeleteStatus.PENDING.value)