真人素材库本地完成

This commit is contained in:
2026-07-06 15:13:41 +08:00
parent 75d1f7a428
commit ab67bdf19e
52 changed files with 4131 additions and 137 deletions
+2
View File
@@ -58,6 +58,7 @@ class AdminUserOut(BaseModel):
last_login_at: NaiveDatetimeOptional = None
allowed_menus: list | None = None
resource_capacity: ResourceCapacityUsageOut | None = None
private_portrait_image_limit: int = 5
model_config = {"from_attributes": True}
@@ -71,6 +72,7 @@ class CreateUserRequest(BaseModel):
user_type: str = Field(default="frontend", pattern="^(frontend|admin)$")
frontend_user_kind: str = Field(default="external", pattern="^(internal|external)$")
allowed_menus: list | None = None
private_portrait_image_limit: int = Field(default=5, ge=0, le=9999)
class UpdateFrontendUserKindRequest(BaseModel):
@@ -41,6 +41,21 @@ class GenerationAIReference(BaseModel):
description="参考素材时长(秒)。type=video/audio 时使用,用于视频/音频素材时长校验",
examples=[5.0],
)
source: str | None = Field(
None,
description="参考素材来源。private_portrait_asset=真人素材库;为空表示普通上传文件",
examples=["private_portrait_asset"],
)
private_asset_id: str | None = Field(
None,
description="真人素材库本地素材ID。source=private_portrait_asset 时必填,后端据此解析 remote_asset_id",
examples=["0019fxxx"],
)
remote_asset_id: str | None = Field(
None,
description="后端回填的火山 Asset ID。前端传入时不可信,创建任务时以后端查库为准",
examples=["asset-20260318071009-xxxxx"],
)
class GenerationAITaskCreate(BaseModel):
@@ -0,0 +1,172 @@
from __future__ import annotations
from typing import Any
from pydantic import BaseModel, Field, field_validator
from app.enums.private_portrait import PrivatePortraitAssetType
from app.schemas.common import NaiveDatetimeOptional
class PrivatePortraitConfigOut(BaseModel):
enabled: bool
image_limit: int
used_image_count: int
remaining_image_count: int
class PrivatePortraitAdminConfigUpdate(BaseModel):
private_portrait_image_limit: int = Field(..., ge=0, le=9999, description="0 表示关闭真人素材模块;>0 表示启用并限制图片总量")
class PrivatePortraitProjectCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=128)
description: str | None = Field(None, max_length=2000)
class PrivatePortraitProjectUpdate(BaseModel):
name: str | None = Field(None, min_length=1, max_length=128)
description: str | None = Field(None, max_length=2000)
status: str | None = None
class PrivatePortraitProjectOut(BaseModel):
id: str
user_id: str | None = None
name: str
name_slug: str | None = None
remote_project_name: str | None = None
description: str | None = None
status: str
asset_group_count: int = 0
asset_count: int = 0
active_asset_count: int = 0
last_used_at: NaiveDatetimeOptional = None
created_at: NaiveDatetimeOptional = None
updated_at: NaiveDatetimeOptional = None
model_config = {"from_attributes": True}
class PrivatePortraitProjectListOut(BaseModel):
items: list[PrivatePortraitProjectOut]
total: int
page: int
page_size: int
class PrivatePortraitValidateSessionCreate(BaseModel):
callback_redirect_url: str | None = Field(None, description="认证完成后前端要跳转的页面。为空时使用后端默认回调页。")
class PrivatePortraitValidateSessionOut(BaseModel):
id: str
user_id: str | None = None
project_id: str
byted_token: str | None = None
h5_link: str | None = None
callback_url: str | None = None
result_code: str | None = None
algorithm_base_resp_code: str | None = None
verify_type: str | None = None
status: str
remote_group_id: str | None = None
remote_project_name: str | None = None
expired_at: NaiveDatetimeOptional = None
error_message: str | None = None
created_at: NaiveDatetimeOptional = None
updated_at: NaiveDatetimeOptional = None
model_config = {"from_attributes": True}
class PrivatePortraitAssetGroupOut(BaseModel):
id: str
user_id: str | None = None
project_id: str
remote_group_id: str
remote_group_name: str | None = None
remote_project_name: str
group_type: str
status: str
remote_delete_status: str
remote_deleted_at: NaiveDatetimeOptional = None
remote_delete_error: str | None = None
created_at: NaiveDatetimeOptional = None
updated_at: NaiveDatetimeOptional = None
model_config = {"from_attributes": True}
class PrivatePortraitAssetCreate(BaseModel):
url: str = Field(..., min_length=1, description="已上传到本系统且可公网访问的素材 URL")
asset_type: str = Field(default=PrivatePortraitAssetType.IMAGE.value)
name: str | None = Field(None, max_length=256)
@field_validator("asset_type")
@classmethod
def validate_asset_type(cls, v: str) -> str:
value = v or PrivatePortraitAssetType.IMAGE.value
if value not in {item.value for item in PrivatePortraitAssetType}:
raise ValueError("asset_type 仅支持 Image/Video/Audio")
return value
class PrivatePortraitAssetOut(BaseModel):
id: str
user_id: str | None = None
project_id: str
project_name: str | None = None
group_id: str
remote_group_id: str
remote_asset_id: str | None = None
remote_project_name: str | None = None
asset_type: str
name: str | None = None
source_url: str
preview_url: str | None = None
remote_url: str | None = None
remote_url_expired_at: NaiveDatetimeOptional = None
status: str
moderation: dict[str, Any] | None = None
last_poll_at: NaiveDatetimeOptional = None
next_poll_at: NaiveDatetimeOptional = None
poll_count: int = 0
remote_delete_status: str
remote_deleted_at: NaiveDatetimeOptional = None
remote_delete_error: str | None = None
error_message: str | None = None
created_at: NaiveDatetimeOptional = None
updated_at: NaiveDatetimeOptional = None
model_config = {"from_attributes": True}
class PrivatePortraitAssetListOut(BaseModel):
items: list[PrivatePortraitAssetOut]
total: int
page: int
page_size: int
class PrivatePortraitSelectableAssetOut(BaseModel):
id: str
project_id: str
project_name: str
name: str | None = None
asset_type: str
preview_url: str | None = None
status: str
created_at: NaiveDatetimeOptional = None
class PrivatePortraitSelectableAssetListOut(BaseModel):
items: list[PrivatePortraitSelectableAssetOut]
total: int
page: int
page_size: int
class PrivatePortraitDeleteOut(BaseModel):
success: bool = True
remote_delete_status: str | None = None
+1
View File
@@ -15,5 +15,6 @@ class UserOut(BaseModel):
allowed_menus: list | None = None
must_set_password: bool = False
resource_capacity: ResourceCapacityUsageOut | None = None
private_portrait_image_limit: int = 5
model_config = {"from_attributes": True}