Files
ai_game/server/app/models/story.py
liangguodong 4ac47c8474 feat: AI改写功能集成角色数据 + UI优化
- 新增story_characters表和seed_characters.sql种子数据(27个角色)
- AI改写/续写功能注入角色信息(性别/年龄/外貌/性格)
- 首页UI下移避让微信退出按钮
- 个人中心页面布局重构
2026-03-11 18:41:56 +08:00

130 lines
4.9 KiB
Python

"""
故事相关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")
characters = relationship("StoryCharacter", back_populates="story", cascade="all, delete-orphan")
class StoryCharacter(Base):
"""故事角色表"""
__tablename__ = "story_characters"
id = Column(Integer, primary_key=True, autoincrement=True)
story_id = Column(Integer, ForeignKey("stories.id", ondelete="CASCADE"), nullable=False)
name = Column(String(50), nullable=False)
role_type = Column(String(20), default="supporting") # protagonist/antagonist/supporting
gender = Column(String(10), default="")
age_range = Column(String(20), default="")
appearance = Column(Text) # 外貌描述
personality = Column(Text) # 性格描述
background = Column(Text) # 背景故事
avatar_prompt = Column(Text) # AI绘图提示词
avatar_url = Column(String(500), default="")
created_at = Column(TIMESTAMP, server_default=func.now())
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
story = relationship("Story", back_populates="characters")
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")