"""形仪域表模型:真形锚点 / 主仪化仪(候选定相)/ 声音档案。 设计依据 core-domains.md §5 与 PRD §8: - 真形锚点记录稳定辨识特征、年龄表现、许可、禁忌与可变范围; - 生成内容先为 candidate,用户定相(adopt)后才成为正式主仪或化仪; - 声音三来源(合成预设/原创设计/授权克隆);克隆授权按身份、用途、期限与 传播范围治理,可撤回,撤回立即生效。 """ from datetime import datetime from sqlalchemy import DateTime, ForeignKey, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.core.ids import new_id from app.core.time import utc_now from app.db.base import Base class FormAnchor(Base): """真形锚点:不可被生成内容静默覆盖的稳定辨识约束。""" __tablename__ = "form_anchors" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=lambda: new_id("anchor")) character_id: Mapped[str] = mapped_column( String(80), ForeignKey("characters.id"), nullable=False, index=True ) # 稳定辨识特征(发色/瞳色/身形/标志性印记等) features_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") # 年龄表现约束(成人角色恒定成人表现;不得生成未成年敏感形态) age_presentation: Mapped[str] = mapped_column(String(40), nullable=False, default="adult") # 许可与禁忌(允许的画风/场景;禁止的表现) permissions_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") # 可变范围(服装/表情/姿态可变;核心特征不可变) mutable_scope_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=utc_now, onupdate=utc_now ) class Form(Base): """主仪/化仪:一魂多仪。candidate → adopted(定相)/ discarded;同一时刻仅一主仪。""" __tablename__ = "forms" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=lambda: new_id("form")) character_id: Mapped[str] = mapped_column( String(80), ForeignKey("characters.id"), nullable=False, index=True ) # primary 主仪 / costume 化仪 / text 文字形仪(启镜最小形态) kind: Mapped[str] = mapped_column(String(20), nullable=False, default="costume", index=True) # candidate(候选,未定相不转正)/ adopted / discarded / retired status: Mapped[str] = mapped_column(String(20), nullable=False, default="candidate", index=True) # 来源:image_gen(生成)/ upload(上传)/ live2d(模型导入)/ text source: Mapped[str] = mapped_column(String(20), nullable=False, default="upload") # 文字描述(所有形态必填——文字形仪或图像的文字说明) description: Mapped[str] = mapped_column(Text, nullable=False, default="") # 媒体引用(图片文件路径或 Live2D 模型包引用;文字形仪为空) media_ref: Mapped[str | None] = mapped_column(String(500)) # Live2D 动作映射(模型 motions/expressions 索引,JSON) live2d_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now) adopted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) class VoiceProfile(Base): """声音档案:合成预设 / 原创设计 / 授权克隆。""" __tablename__ = "voice_profiles" id: Mapped[str] = mapped_column(String(80), primary_key=True, default=lambda: new_id("voice")) character_id: Mapped[str] = mapped_column( String(80), ForeignKey("characters.id"), nullable=False, index=True ) # synthesis_preset / original_design / authorized_clone source: Mapped[str] = mapped_column(String(30), nullable=False, default="synthesis_preset") name: Mapped[str] = mapped_column(String(120), nullable=False) # 合成参数(音色/语速/音高;预设名或设计参数) params_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}") # 克隆授权(authorized_clone 必填):身份、用途、期限、传播范围;可撤回 grant_purpose: Mapped[str | None] = mapped_column(String(200)) grant_expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) grant_distribution: Mapped[str | None] = mapped_column(String(200)) grant_revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) is_active: Mapped[bool] = mapped_column(default=True, nullable=False, index=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)