真人素材库授权BUG修复 6
This commit is contained in:
@@ -7,13 +7,31 @@ from fastapi import HTTPException
|
|||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.enums.private_portrait import PRIVATE_PORTRAIT_ASSET_URI_PREFIX, PrivatePortraitAssetStatus, PrivatePortraitEventSource, PrivatePortraitEventStatus, PrivatePortraitEventType, PrivatePortraitReferenceSource
|
from app.enums.private_portrait import (
|
||||||
|
PRIVATE_PORTRAIT_ASSET_URI_PREFIX,
|
||||||
|
PrivatePortraitAssetStatus,
|
||||||
|
PrivatePortraitAssetType,
|
||||||
|
PrivatePortraitEventSource,
|
||||||
|
PrivatePortraitEventStatus,
|
||||||
|
PrivatePortraitEventType,
|
||||||
|
PrivatePortraitReferenceSource,
|
||||||
|
)
|
||||||
from app.models.private_portrait import PrivatePortraitAsset
|
from app.models.private_portrait import PrivatePortraitAsset
|
||||||
from app.services.operation_log_service import log_operation_event
|
from app.services.operation_log_service import log_operation_error, log_operation_event
|
||||||
|
|
||||||
DOMAIN = "private_portrait"
|
DOMAIN = "private_portrait"
|
||||||
|
|
||||||
|
|
||||||
|
_ASSET_TYPE_TO_REFERENCE_TYPE = {
|
||||||
|
PrivatePortraitAssetType.IMAGE.value: "image",
|
||||||
|
PrivatePortraitAssetType.VIDEO.value: "video",
|
||||||
|
PrivatePortraitAssetType.AUDIO.value: "audio",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_SUPPORTED_GEN_TYPES = {"image", "video"}
|
||||||
|
|
||||||
|
|
||||||
def _ref_get(ref: Any, key: str) -> Any:
|
def _ref_get(ref: Any, key: str) -> Any:
|
||||||
if isinstance(ref, dict):
|
if isinstance(ref, dict):
|
||||||
return ref.get(key)
|
return ref.get(key)
|
||||||
@@ -27,33 +45,109 @@ def _ref_set(ref: Any, key: str, value: Any) -> None:
|
|||||||
setattr(ref, key, value)
|
setattr(ref, key, value)
|
||||||
|
|
||||||
|
|
||||||
async def resolve_private_portrait_references(db: AsyncSession, *, user_id: str, media_references: list[Any] | None) -> list[Any] | None:
|
def _normalize_gen_type(gen_type: str | None) -> str | None:
|
||||||
|
value = (gen_type or "").strip().lower()
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
if value not in _SUPPORTED_GEN_TYPES:
|
||||||
|
raise HTTPException(status_code=400, detail="gen_type 仅支持 image 或 video")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_ref_type(value: Any) -> str:
|
||||||
|
return str(value or "").strip().lower()
|
||||||
|
|
||||||
|
|
||||||
|
async def resolve_private_portrait_references(
|
||||||
|
db: AsyncSession,
|
||||||
|
*,
|
||||||
|
user_id: str,
|
||||||
|
media_references: list[Any] | None,
|
||||||
|
gen_type: str | None = None,
|
||||||
|
) -> list[Any] | None:
|
||||||
|
"""Resolve private portrait references before dispatching a generation task.
|
||||||
|
|
||||||
|
generation_ai_service.py 和 generation_task_factory_service.py 都会传 gen_type。
|
||||||
|
这里保留该参数用于兼容调用方,并做基础校验,避免接口因函数签名不一致直接 500。
|
||||||
|
"""
|
||||||
|
normalized_gen_type = _normalize_gen_type(gen_type)
|
||||||
if not media_references:
|
if not media_references:
|
||||||
return media_references
|
return media_references
|
||||||
|
|
||||||
refs = deepcopy(media_references)
|
refs = deepcopy(media_references)
|
||||||
ids = [str(_ref_get(ref, "private_asset_id")) for ref in refs if _ref_get(ref, "source") == PrivatePortraitReferenceSource.PRIVATE_PORTRAIT_ASSET.value and _ref_get(ref, "private_asset_id")]
|
ids = [
|
||||||
|
str(_ref_get(ref, "private_asset_id"))
|
||||||
|
for ref in refs
|
||||||
|
if _ref_get(ref, "source") == PrivatePortraitReferenceSource.PRIVATE_PORTRAIT_ASSET.value
|
||||||
|
and _ref_get(ref, "private_asset_id")
|
||||||
|
]
|
||||||
ids = list(dict.fromkeys(ids))
|
ids = list(dict.fromkeys(ids))
|
||||||
if not ids:
|
if not ids:
|
||||||
return refs
|
return refs
|
||||||
log_operation_event(domain=DOMAIN, event_type=PrivatePortraitEventType.REFERENCE_RESOLVE_START.value, event_status=PrivatePortraitEventStatus.PENDING.value, source=PrivatePortraitEventSource.SERVICE.value, user_id=user_id, detail={"private_asset_ids": ids})
|
|
||||||
rows = await db.execute(select(PrivatePortraitAsset).where(PrivatePortraitAsset.id.in_(ids)))
|
log_operation_event(
|
||||||
asset_map = {asset.id: asset for asset in rows.scalars().all()}
|
domain=DOMAIN,
|
||||||
for ref in refs:
|
event_type=PrivatePortraitEventType.REFERENCE_RESOLVE_START.value,
|
||||||
if _ref_get(ref, "source") != PrivatePortraitReferenceSource.PRIVATE_PORTRAIT_ASSET.value:
|
event_status=PrivatePortraitEventStatus.PENDING.value,
|
||||||
continue
|
source=PrivatePortraitEventSource.SERVICE.value,
|
||||||
asset_id = str(_ref_get(ref, "private_asset_id") or "")
|
user_id=user_id,
|
||||||
asset = asset_map.get(asset_id)
|
detail={"private_asset_ids": ids, "gen_type": normalized_gen_type},
|
||||||
if not asset:
|
)
|
||||||
raise HTTPException(status_code=400, detail="真人素材不存在")
|
|
||||||
if asset.user_id != user_id:
|
try:
|
||||||
raise HTTPException(status_code=403, detail="真人素材不属于当前用户")
|
rows = await db.execute(select(PrivatePortraitAsset).where(PrivatePortraitAsset.id.in_(ids)))
|
||||||
if asset.deleted_at is not None:
|
asset_map = {asset.id: asset for asset in rows.scalars().all()}
|
||||||
raise HTTPException(status_code=400, detail="真人素材已删除")
|
|
||||||
if asset.status != PrivatePortraitAssetStatus.ACTIVE.value:
|
for ref in refs:
|
||||||
raise HTTPException(status_code=400, detail=f"真人素材状态为 {asset.status},Active 后才可用于生成")
|
if _ref_get(ref, "source") != PrivatePortraitReferenceSource.PRIVATE_PORTRAIT_ASSET.value:
|
||||||
if not asset.remote_asset_id:
|
continue
|
||||||
raise HTTPException(status_code=400, detail="真人素材缺少远程 AssetId")
|
|
||||||
_ref_set(ref, "remote_asset_id", asset.remote_asset_id)
|
asset_id = str(_ref_get(ref, "private_asset_id") or "")
|
||||||
_ref_set(ref, "url", f"{PRIVATE_PORTRAIT_ASSET_URI_PREFIX}{asset.remote_asset_id}")
|
if not asset_id:
|
||||||
log_operation_event(domain=DOMAIN, event_type=PrivatePortraitEventType.REFERENCE_RESOLVE_SUCCESS.value, event_status=PrivatePortraitEventStatus.SUCCESS.value, source=PrivatePortraitEventSource.SERVICE.value, user_id=user_id, detail={"count": len(ids)})
|
raise HTTPException(status_code=400, detail="真人素材引用缺少 private_asset_id")
|
||||||
return refs
|
|
||||||
|
asset = asset_map.get(asset_id)
|
||||||
|
if not asset:
|
||||||
|
raise HTTPException(status_code=400, detail="真人素材不存在")
|
||||||
|
if asset.user_id != user_id:
|
||||||
|
raise HTTPException(status_code=403, detail="真人素材不属于当前用户")
|
||||||
|
if asset.deleted_at is not None:
|
||||||
|
raise HTTPException(status_code=400, detail="真人素材已删除")
|
||||||
|
if asset.status != PrivatePortraitAssetStatus.ACTIVE.value:
|
||||||
|
raise HTTPException(status_code=400, detail=f"真人素材状态为 {asset.status},Active 后才可用于生成")
|
||||||
|
if not asset.remote_asset_id:
|
||||||
|
raise HTTPException(status_code=400, detail="真人素材缺少远程 AssetId")
|
||||||
|
|
||||||
|
expected_ref_type = _ASSET_TYPE_TO_REFERENCE_TYPE.get(asset.asset_type)
|
||||||
|
ref_type = _normalize_ref_type(_ref_get(ref, "type"))
|
||||||
|
if expected_ref_type and ref_type and ref_type != expected_ref_type:
|
||||||
|
raise HTTPException(status_code=400, detail=f"真人素材类型不匹配:引用为 {ref_type},素材为 {expected_ref_type}")
|
||||||
|
|
||||||
|
_ref_set(ref, "source", PrivatePortraitReferenceSource.PRIVATE_PORTRAIT_ASSET.value)
|
||||||
|
_ref_set(ref, "private_asset_id", asset.id)
|
||||||
|
_ref_set(ref, "remote_asset_id", asset.remote_asset_id)
|
||||||
|
_ref_set(ref, "url", f"{PRIVATE_PORTRAIT_ASSET_URI_PREFIX}{asset.remote_asset_id}")
|
||||||
|
if expected_ref_type:
|
||||||
|
_ref_set(ref, "type", expected_ref_type)
|
||||||
|
if not _ref_get(ref, "name") and asset.name:
|
||||||
|
_ref_set(ref, "name", asset.name)
|
||||||
|
|
||||||
|
log_operation_event(
|
||||||
|
domain=DOMAIN,
|
||||||
|
event_type=PrivatePortraitEventType.REFERENCE_RESOLVE_SUCCESS.value,
|
||||||
|
event_status=PrivatePortraitEventStatus.SUCCESS.value,
|
||||||
|
source=PrivatePortraitEventSource.SERVICE.value,
|
||||||
|
user_id=user_id,
|
||||||
|
detail={"count": len(ids), "gen_type": normalized_gen_type},
|
||||||
|
)
|
||||||
|
return refs
|
||||||
|
except Exception as exc:
|
||||||
|
log_operation_error(
|
||||||
|
domain=DOMAIN,
|
||||||
|
event_type=PrivatePortraitEventType.REFERENCE_RESOLVE_FAILED.value,
|
||||||
|
source=PrivatePortraitEventSource.SERVICE.value,
|
||||||
|
user_id=user_id,
|
||||||
|
detail={"private_asset_ids": ids, "gen_type": normalized_gen_type},
|
||||||
|
exc=exc,
|
||||||
|
)
|
||||||
|
raise
|
||||||
|
|||||||
Reference in New Issue
Block a user