""" 故事相关ORM模型 """ from sqlalchemy import Column, Integer, String, Text, Boolean, TIMESTAMP, ForeignKey, Enum, JSON from sqlalchemy.orm import relationship from sqlalchemy.sql import func from app.database import Base import enum class Story(Base): """故事主表""" __tablename__ = "stories" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(100), nullable=False) cover_url = Column(String(255), default="") description = Column(Text) author_id = Column(Integer, default=0) category = Column(String(50), nullable=False) play_count = Column(Integer, default=0) like_count = Column(Integer, default=0) is_featured = Column(Boolean, default=False) status = Column(Integer, default=1) created_at = Column(TIMESTAMP, server_default=func.now()) updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now()) nodes = relationship("StoryNode", back_populates="story", cascade="all, delete-orphan") class StoryNode(Base): """故事节点表""" __tablename__ = "story_nodes" id = Column(Integer, primary_key=True, autoincrement=True) story_id = Column(Integer, ForeignKey("stories.id", ondelete="CASCADE"), nullable=False) node_key = Column(String(50), nullable=False) content = Column(Text, nullable=False) speaker = Column(String(50), default="") background_image = Column(String(255), default="") character_image = Column(String(255), default="") bgm = Column(String(255), default="") is_ending = Column(Boolean, default=False) ending_name = Column(String(100), default="") ending_score = Column(Integer, default=0) ending_type = Column(String(20), default="") sort_order = Column(Integer, default=0) created_at = Column(TIMESTAMP, server_default=func.now()) story = relationship("Story", back_populates="nodes") choices = relationship("StoryChoice", back_populates="node", cascade="all, delete-orphan") class StoryChoice(Base): """故事选项表""" __tablename__ = "story_choices" id = Column(Integer, primary_key=True, autoincrement=True) node_id = Column(Integer, ForeignKey("story_nodes.id", ondelete="CASCADE"), nullable=False) story_id = Column(Integer, nullable=False) text = Column(String(200), nullable=False) next_node_key = Column(String(50), nullable=False) sort_order = Column(Integer, default=0) is_locked = Column(Boolean, default=False) created_at = Column(TIMESTAMP, server_default=func.now()) node = relationship("StoryNode", back_populates="choices") class DraftStatus(enum.Enum): """草稿状态枚举""" pending = "pending" processing = "processing" completed = "completed" failed = "failed" class StoryDraft(Base): """AI改写草稿表""" __tablename__ = "story_drafts" id = Column(Integer, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) story_id = Column(Integer, ForeignKey("stories.id", ondelete="CASCADE"), nullable=False) title = Column(String(100), default="") # 用户输入 path_history = Column(JSON, default=None) # 用户之前的选择路径 current_node_key = Column(String(50), default="") current_content = Column(Text, default="") user_prompt = Column(String(500), nullable=False) # AI生成结果 ai_nodes = Column(JSON, default=None) # AI生成的新节点 entry_node_key = Column(String(50), default="") tokens_used = Column(Integer, default=0) # 状态 status = Column(Enum(DraftStatus), default=DraftStatus.pending) error_message = Column(String(500), default="") is_read = Column(Boolean, default=False) # 用户是否已查看 created_at = Column(TIMESTAMP, server_default=func.now()) completed_at = Column(TIMESTAMP, default=None) # 关联 story = relationship("Story")