""" 双人对练房间模型 功能: - 房间管理(创建、加入、状态) - 参与者管理 - 实时消息同步 """ from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, JSON from sqlalchemy.sql import func from sqlalchemy.orm import relationship from app.models.base import Base class PracticeRoom(Base): """双人对练房间模型""" __tablename__ = "practice_rooms" id = Column(Integer, primary_key=True, index=True, comment="房间ID") room_code = Column(String(10), unique=True, nullable=False, index=True, comment="6位邀请码") room_name = Column(String(200), comment="房间名称") # 场景信息 scene_id = Column(Integer, ForeignKey("practice_scenes.id", ondelete="SET NULL"), comment="关联场景ID") scene_name = Column(String(200), comment="场景名称") scene_type = Column(String(50), comment="场景类型") scene_background = Column(Text, comment="场景背景") # 角色设置 role_a_name = Column(String(50), default="角色A", comment="角色A名称(如销售顾问)") role_b_name = Column(String(50), default="角色B", comment="角色B名称(如顾客)") role_a_description = Column(Text, comment="角色A描述") role_b_description = Column(Text, comment="角色B描述") # 参与者信息 host_user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, comment="房主用户ID") guest_user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), comment="加入者用户ID") host_role = Column(String(10), default="A", comment="房主选择的角色(A/B)") max_participants = Column(Integer, default=2, comment="最大参与人数") # 状态和时间 status = Column(String(20), default="waiting", index=True, comment="状态: waiting/ready/practicing/completed/canceled") created_at = Column(DateTime, server_default=func.now(), comment="创建时间") started_at = Column(DateTime, comment="开始时间") ended_at = Column(DateTime, comment="结束时间") duration_seconds = Column(Integer, default=0, comment="对练时长(秒)") # 对话统计 total_turns = Column(Integer, default=0, comment="总对话轮次") role_a_turns = Column(Integer, default=0, comment="角色A发言次数") role_b_turns = Column(Integer, default=0, comment="角色B发言次数") # 软删除 is_deleted = Column(Boolean, default=False, comment="是否删除") deleted_at = Column(DateTime, comment="删除时间") def __repr__(self): return f"" @property def is_full(self) -> bool: """房间是否已满""" return self.guest_user_id is not None @property def participant_count(self) -> int: """当前参与人数""" count = 1 # 房主 if self.guest_user_id: count += 1 return count def get_user_role(self, user_id: int) -> str: """获取用户在房间中的角色""" if user_id == self.host_user_id: return self.host_role elif user_id == self.guest_user_id: return "B" if self.host_role == "A" else "A" return None def get_role_name(self, role: str) -> str: """获取角色名称""" if role == "A": return self.role_a_name elif role == "B": return self.role_b_name return None def get_user_role_name(self, user_id: int) -> str: """获取用户的角色名称""" role = self.get_user_role(user_id) return self.get_role_name(role) if role else None class PracticeRoomMessage(Base): """房间实时消息模型""" __tablename__ = "practice_room_messages" id = Column(Integer, primary_key=True, index=True, comment="消息ID") room_id = Column(Integer, ForeignKey("practice_rooms.id", ondelete="CASCADE"), nullable=False, index=True, comment="房间ID") user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), comment="发送者用户ID") message_type = Column(String(20), nullable=False, comment="消息类型: chat/system/join/leave/start/end") content = Column(Text, comment="消息内容") role_name = Column(String(50), comment="角色名称") sequence = Column(Integer, nullable=False, comment="消息序号") created_at = Column(DateTime(3), server_default=func.now(3), comment="创建时间") def __repr__(self): return f"" def to_dict(self) -> dict: """转换为字典(用于SSE推送)""" return { "id": self.id, "room_id": self.room_id, "user_id": self.user_id, "message_type": self.message_type, "content": self.content, "role_name": self.role_name, "sequence": self.sequence, "created_at": self.created_at.isoformat() if self.created_at else None }