Files
meijiaka-zy/python-api/app/models/avatar.py
T

141 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Avatar 形象克隆模型
==================
存储用户克隆形象的信息,作为本地 localStorage 的云端备份。
"""
from datetime import UTC, datetime
from sqlalchemy import BigInteger, DateTime, ForeignKey, String, Text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from app.db.session import Base
from app.schemas.enums import AvatarCloneStatus
class Avatar(Base):
"""
形象克隆记录表
用于备份用户在本地创建的克隆形象,支持换机恢复和客服排查。
"""
__tablename__ = "avatars"
# 主键:本地生成的唯一标识(与 Kling element_id 无关)
id: Mapped[str] = mapped_column(
String(64),
primary_key=True,
comment="本地形象唯一标识(如 avt_xxx",
)
# 关联用户(外键,对应 users.id)
user_id: Mapped[str] = mapped_column(
UUID(as_uuid=False),
ForeignKey("users.id", ondelete="CASCADE"),
nullable=False,
index=True,
comment="关联用户 ID",
)
# 形象展示名称
name: Mapped[str] = mapped_column(
String(64),
nullable=False,
comment="形象展示名称",
)
# 供应商标识
provider: Mapped[str] = mapped_column(
String(32),
nullable=False,
default="kling",
comment="供应商标识: kling",
)
# Kling 自定义音色 ID(创建成功后回填)
voice_id: Mapped[str | None] = mapped_column(
String(64),
nullable=True,
comment="Kling 自定义音色 ID",
)
# 供应商主体 ID(创建成功后回填,用于调用 omni-video API
provider_element_id: Mapped[int | None] = mapped_column(
BigInteger,
nullable=True,
comment="供应商主体 ID(数字类型,调用 API 时使用)",
)
# 供应商任务 ID(用于客服追溯)
provider_voice_job_id: Mapped[str | None] = mapped_column(
String(128),
nullable=True,
index=True,
comment="供应商自定义音色任务 ID",
)
provider_element_job_id: Mapped[str | None] = mapped_column(
String(128),
nullable=True,
index=True,
comment="供应商主体创建任务 ID",
)
# 资源地址
video_url: Mapped[str] = mapped_column(
Text,
nullable=False,
comment="原始人物视频 URL",
)
trial_url: Mapped[str | None] = mapped_column(
Text,
nullable=True,
comment="音色试听音频 URL",
)
# 状态机
status: Mapped[str] = mapped_column(
String(32),
nullable=False,
default=AvatarCloneStatus.PENDING.value,
comment="状态: pending/voice_processing/voice_failed/element_processing/element_failed/succeed/timeout",
)
# 失败原因(用户可读)
fail_reason: Mapped[str | None] = mapped_column(
Text,
nullable=True,
comment="失败原因(中文可读)",
)
# 软删除标记
deleted_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True),
nullable=True,
comment="软删除时间,NULL 表示未删除",
)
# 时间戳
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(UTC),
nullable=False,
comment="记录创建时间",
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(UTC),
onupdate=lambda: datetime.now(UTC),
nullable=False,
comment="记录更新时间",
)
def to_dict(self) -> dict:
"""转换为字典(用于序列化)"""
return {column.name: getattr(self, column.name) for column in self.__table__.columns}