"""陪练模块数据模型""" from datetime import datetime from typing import Optional from enum import Enum from sqlalchemy import ( Column, String, Integer, ForeignKey, Text, JSON, Enum as SQLEnum, Float, Boolean, DateTime, func, ) from sqlalchemy.orm import relationship, Mapped, mapped_column from app.models.base import BaseModel, SoftDeleteMixin, AuditMixin class TrainingSceneStatus(str, Enum): """陪练场景状态枚举""" DRAFT = "draft" # 草稿 ACTIVE = "active" # 已激活 INACTIVE = "inactive" # 已停用 class TrainingSessionStatus(str, Enum): """陪练会话状态枚举""" CREATED = "created" # 已创建 IN_PROGRESS = "in_progress" # 进行中 COMPLETED = "completed" # 已完成 CANCELLED = "cancelled" # 已取消 ERROR = "error" # 异常结束 class MessageType(str, Enum): """消息类型枚举""" TEXT = "text" # 文本消息 VOICE = "voice" # 语音消息 SYSTEM = "system" # 系统消息 class MessageRole(str, Enum): """消息角色枚举""" USER = "user" # 用户 ASSISTANT = "assistant" # AI助手 SYSTEM = "system" # 系统 class TrainingScene(BaseModel, SoftDeleteMixin, AuditMixin): """ 陪练场景模型 定义不同的陪练场景,如面试训练、演讲训练等 """ __tablename__ = "training_scenes" __allow_unmapped__ = True # 基础信息 name: Mapped[str] = mapped_column(String(100), nullable=False, comment="场景名称") description: Mapped[Optional[str]] = mapped_column( Text, nullable=True, comment="场景描述" ) category: Mapped[str] = mapped_column(String(50), nullable=False, comment="场景分类") # 配置信息 ai_config: Mapped[Optional[dict]] = mapped_column( JSON, nullable=True, comment="AI配置(如Coze Bot ID等)" ) prompt_template: Mapped[Optional[str]] = mapped_column( Text, nullable=True, comment="提示词模板" ) evaluation_criteria: Mapped[Optional[dict]] = mapped_column( JSON, nullable=True, comment="评估标准" ) # 状态和权限 status: Mapped[TrainingSceneStatus] = mapped_column( SQLEnum(TrainingSceneStatus), default=TrainingSceneStatus.DRAFT, nullable=False, comment="场景状态", ) is_public: Mapped[bool] = mapped_column( Boolean, default=True, nullable=False, comment="是否公开" ) required_level: Mapped[Optional[int]] = mapped_column( Integer, nullable=True, comment="所需用户等级" ) # 关联 sessions: Mapped[list["TrainingSession"]] = relationship( "TrainingSession", back_populates="scene", cascade="all, delete-orphan" ) class TrainingSession(BaseModel, AuditMixin): """ 陪练会话模型 记录每次陪练会话的信息 """ __tablename__ = "training_sessions" __allow_unmapped__ = True # 基础信息 user_id: Mapped[int] = mapped_column( Integer, nullable=False, index=True, comment="用户ID" ) scene_id: Mapped[int] = mapped_column( Integer, ForeignKey("training_scenes.id"), nullable=False, comment="场景ID" ) # 会话信息 coze_conversation_id: Mapped[Optional[str]] = mapped_column( String(100), nullable=True, comment="Coze会话ID" ) start_time: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), nullable=False, comment="开始时间(北京时间)" ) end_time: Mapped[Optional[datetime]] = mapped_column( DateTime, nullable=True, comment="结束时间(北京时间)" ) duration_seconds: Mapped[Optional[int]] = mapped_column( Integer, nullable=True, comment="持续时长(秒)" ) # 状态和配置 status: Mapped[TrainingSessionStatus] = mapped_column( SQLEnum(TrainingSessionStatus), default=TrainingSessionStatus.CREATED, nullable=False, comment="会话状态", ) session_config: Mapped[Optional[dict]] = mapped_column( JSON, nullable=True, comment="会话配置" ) # 评估信息 total_score: Mapped[Optional[float]] = mapped_column( Float, nullable=True, comment="总分" ) evaluation_result: Mapped[Optional[dict]] = mapped_column( JSON, nullable=True, comment="评估结果详情" ) # 关联 scene: Mapped["TrainingScene"] = relationship( "TrainingScene", back_populates="sessions" ) messages: Mapped[list["TrainingMessage"]] = relationship( "TrainingMessage", back_populates="session", cascade="all, delete-orphan", order_by="TrainingMessage.created_at", ) report: Mapped[Optional["TrainingReport"]] = relationship( "TrainingReport", back_populates="session", uselist=False ) class TrainingMessage(BaseModel): """ 陪练消息模型 记录会话中的每条消息 """ __tablename__ = "training_messages" __allow_unmapped__ = True # 基础信息 session_id: Mapped[int] = mapped_column( Integer, ForeignKey("training_sessions.id"), nullable=False, comment="会话ID" ) # 消息内容 role: Mapped[MessageRole] = mapped_column( SQLEnum(MessageRole), nullable=False, comment="消息角色" ) type: Mapped[MessageType] = mapped_column( SQLEnum(MessageType), nullable=False, comment="消息类型" ) content: Mapped[str] = mapped_column(Text, nullable=False, comment="消息内容") # 语音消息相关 voice_url: Mapped[Optional[str]] = mapped_column( String(500), nullable=True, comment="语音文件URL" ) voice_duration: Mapped[Optional[float]] = mapped_column( Float, nullable=True, comment="语音时长(秒)" ) # 元数据 message_metadata: Mapped[Optional[dict]] = mapped_column( JSON, nullable=True, comment="消息元数据" ) coze_message_id: Mapped[Optional[str]] = mapped_column( String(100), nullable=True, comment="Coze消息ID" ) # 关联 session: Mapped["TrainingSession"] = relationship( "TrainingSession", back_populates="messages" ) class TrainingReport(BaseModel, AuditMixin): """ 陪练报告模型 存储陪练会话的分析报告 """ __tablename__ = "training_reports" __allow_unmapped__ = True # 基础信息 session_id: Mapped[int] = mapped_column( Integer, ForeignKey("training_sessions.id"), unique=True, nullable=False, comment="会话ID", ) user_id: Mapped[int] = mapped_column( Integer, nullable=False, index=True, comment="用户ID" ) # 评分信息 overall_score: Mapped[float] = mapped_column(Float, nullable=False, comment="总体得分") dimension_scores: Mapped[dict] = mapped_column( JSON, nullable=False, comment="各维度得分" ) # 分析内容 strengths: Mapped[list[str]] = mapped_column(JSON, nullable=False, comment="优势点") weaknesses: Mapped[list[str]] = mapped_column(JSON, nullable=False, comment="待改进点") suggestions: Mapped[list[str]] = mapped_column(JSON, nullable=False, comment="改进建议") # 详细内容 detailed_analysis: Mapped[Optional[str]] = mapped_column( Text, nullable=True, comment="详细分析" ) transcript: Mapped[Optional[str]] = mapped_column( Text, nullable=True, comment="对话文本记录" ) # 统计信息 statistics: Mapped[Optional[dict]] = mapped_column( JSON, nullable=True, comment="统计数据" ) # 关联 session: Mapped["TrainingSession"] = relationship( "TrainingSession", back_populates="report" )