from __future__ import annotations import json from typing import Any from fastapi import APIRouter, Depends, Query from sqlalchemy.ext.asyncio import AsyncSession from app.dependencies import get_admin_user, get_db from app.models.user import User from app.schemas.private_portrait import ( PrivatePortraitAdminConfigUpdate, PrivatePortraitAssetListOut, PrivatePortraitConfigOut, PrivatePortraitProjectListOut, ) from app.services.operation_log import log_operation from app.services.private_portrait.asset_service import ( asset_to_out, get_user_private_portrait_config, list_assets, set_user_private_portrait_limit, ) from app.services.private_portrait.project_service import list_projects, project_to_out router = APIRouter(prefix="/admin/private-portrait", tags=["admin-private-portrait"]) def _json_detail(data: dict[str, Any]) -> str: return json.dumps(data, ensure_ascii=False, default=str) @router.get("/users/{user_id}/config", response_model=PrivatePortraitConfigOut) async def admin_get_private_portrait_config( user_id: str, admin: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): config = await get_user_private_portrait_config(db, user_id=user_id) await log_operation( db, admin.id, admin.username, "查看真人素材库配置", "GET", f"/admin/private-portrait/users/{user_id}/config", detail=_json_detail({"target_user_id": user_id, "config": config.model_dump(mode="json")}), ) return config @router.put("/users/{user_id}/config", response_model=PrivatePortraitConfigOut) async def admin_update_private_portrait_config( user_id: str, payload: PrivatePortraitAdminConfigUpdate, admin: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): before = await get_user_private_portrait_config(db, user_id=user_id) await set_user_private_portrait_limit(db, user_id=user_id, limit=payload.private_portrait_image_limit) after = await get_user_private_portrait_config(db, user_id=user_id) await log_operation( db, admin.id, admin.username, f"设置真人素材库数量限制:{before.image_limit} -> {after.image_limit}", "PUT", f"/admin/private-portrait/users/{user_id}/config", detail=_json_detail( { "target_user_id": user_id, "before": before.model_dump(mode="json"), "after": after.model_dump(mode="json"), } ), ) return after @router.get("/projects", response_model=PrivatePortraitProjectListOut) async def admin_list_private_portrait_projects( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), user_id: str | None = Query(None), keyword: str | None = Query(None), status: str | None = Query(None), admin: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): items, total = await list_projects(db, user_id=user_id, page=page, page_size=page_size, keyword=keyword, status=status) await log_operation( db, admin.id, admin.username, "查看真人素材项目列表", "GET", "/admin/private-portrait/projects", detail=_json_detail( { "filters": {"user_id": user_id, "keyword": keyword, "status": status, "page": page, "page_size": page_size}, "total": total, "returned_count": len(items), } ), ) return PrivatePortraitProjectListOut(items=[project_to_out(item, include_user=True) for item in items], total=total, page=page, page_size=page_size) @router.get("/assets", response_model=PrivatePortraitAssetListOut) async def admin_list_private_portrait_assets( page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), user_id: str | None = Query(None), project_id: str | None = Query(None), keyword: str | None = Query(None), status: str | None = Query(None), admin: User = Depends(get_admin_user), db: AsyncSession = Depends(get_db), ): assets, total, project_name_map = await list_assets(db, user_id=user_id, project_id=project_id, status=status, keyword=keyword, page=page, page_size=page_size) await log_operation( db, admin.id, admin.username, "查看真人素材列表", "GET", "/admin/private-portrait/assets", detail=_json_detail( { "filters": {"user_id": user_id, "project_id": project_id, "keyword": keyword, "status": status, "page": page, "page_size": page_size}, "total": total, "returned_count": len(assets), } ), ) return PrivatePortraitAssetListOut(items=[asset_to_out(asset, project_name=project_name_map.get(asset.project_id), include_user=True) for asset in assets], total=total, page=page, page_size=page_size)