真人/虚拟人像库BUG修复 | app build

This commit is contained in:
2026-07-07 13:55:30 +08:00
parent 64714c6d42
commit 01b520ac49
9 changed files with 884 additions and 125 deletions
@@ -1,10 +1,32 @@
from __future__ import annotations
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.private_portrait import PrivatePortraitProject
from app.services.private_portrait.project_service import list_projects, project_to_out, refresh_project_counters
async def _reload_projects_by_ids(db: AsyncSession, project_ids: list[str]) -> list[PrivatePortraitProject]:
"""Reload projects after counter refresh to avoid async expired-attribute lazy load.
refresh_project_counters() uses bulk UPDATE. In SQLAlchemy async ORM, previously
loaded ORM instances may become expired after a bulk update. Accessing expired
scalar attributes outside greenlet context triggers MissingGreenlet. Reloading
with populate_existing=True refreshes the identity-map objects during the awaited
execute call, and keeps the original pagination order.
"""
if not project_ids:
return []
result = await db.execute(
select(PrivatePortraitProject)
.where(PrivatePortraitProject.id.in_(project_ids))
.execution_options(populate_existing=True)
)
project_map = {project.id: project for project in result.scalars().all()}
return [project_map[project_id] for project_id in project_ids if project_id in project_map]
async def admin_list_projects(
db: AsyncSession,
*,
@@ -15,6 +37,18 @@ async def admin_list_projects(
page: int = 1,
page_size: int = 20,
):
items, total = await list_projects(db, user_id=user_id, page=page, page_size=page_size, keyword=keyword, status=status, library_type=library_type)
await refresh_project_counters(db, [item.id for item in items])
items, total = await list_projects(
db,
user_id=user_id,
page=page,
page_size=page_size,
keyword=keyword,
status=status,
library_type=library_type,
)
project_ids = [item.id for item in items]
await refresh_project_counters(db, project_ids)
# Bulk UPDATE may expire loaded ORM instances. Re-query before DTO conversion.
items = await _reload_projects_by_ids(db, project_ids)
return [project_to_out(item, include_user=True) for item in items], total
@@ -251,6 +251,7 @@ async def refresh_project_counters(db: AsyncSession, project_ids: list[str]) ->
active_image_asset_count=active_image_total,
active_video_asset_count=active_video_total,
)
.execution_options(synchronize_session=False)
)