feat: 初始化考培练系统项目
- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
318
backend/app/schemas/practice.py
Normal file
318
backend/app/schemas/practice.py
Normal file
@@ -0,0 +1,318 @@
|
||||
"""
|
||||
陪练功能相关Schema定义
|
||||
"""
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
# ==================== 枚举类型 ====================
|
||||
|
||||
class SceneType:
|
||||
"""场景类型枚举"""
|
||||
PHONE = "phone" # 电话销售
|
||||
FACE = "face" # 面对面销售
|
||||
COMPLAINT = "complaint" # 客户投诉
|
||||
AFTER_SALES = "after-sales" # 售后服务
|
||||
PRODUCT_INTRO = "product-intro" # 产品介绍
|
||||
|
||||
|
||||
class Difficulty:
|
||||
"""难度等级枚举"""
|
||||
BEGINNER = "beginner" # 入门
|
||||
JUNIOR = "junior" # 初级
|
||||
INTERMEDIATE = "intermediate" # 中级
|
||||
SENIOR = "senior" # 高级
|
||||
EXPERT = "expert" # 专家
|
||||
|
||||
|
||||
class SceneStatus:
|
||||
"""场景状态枚举"""
|
||||
ACTIVE = "active" # 启用
|
||||
INACTIVE = "inactive" # 禁用
|
||||
|
||||
|
||||
# ==================== 场景Schema ====================
|
||||
|
||||
class PracticeSceneBase(BaseModel):
|
||||
"""陪练场景基础Schema"""
|
||||
name: str = Field(..., max_length=200, description="场景名称")
|
||||
description: Optional[str] = Field(None, description="场景描述")
|
||||
type: str = Field(..., description="场景类型: phone/face/complaint/after-sales/product-intro")
|
||||
difficulty: str = Field(..., description="难度等级: beginner/junior/intermediate/senior/expert")
|
||||
status: str = Field(default="active", description="状态: active/inactive")
|
||||
background: str = Field(..., description="场景背景设定")
|
||||
ai_role: str = Field(..., description="AI角色描述")
|
||||
objectives: List[str] = Field(..., description="练习目标数组")
|
||||
keywords: Optional[List[str]] = Field(default=None, description="关键词数组")
|
||||
duration: int = Field(default=10, ge=1, le=120, description="预计时长(分钟)")
|
||||
|
||||
@field_validator('type')
|
||||
@classmethod
|
||||
def validate_type(cls, v):
|
||||
"""验证场景类型"""
|
||||
valid_types = ['phone', 'face', 'complaint', 'after-sales', 'product-intro']
|
||||
if v not in valid_types:
|
||||
raise ValueError(f"场景类型必须是: {', '.join(valid_types)}")
|
||||
return v
|
||||
|
||||
@field_validator('difficulty')
|
||||
@classmethod
|
||||
def validate_difficulty(cls, v):
|
||||
"""验证难度等级"""
|
||||
valid_difficulties = ['beginner', 'junior', 'intermediate', 'senior', 'expert']
|
||||
if v not in valid_difficulties:
|
||||
raise ValueError(f"难度等级必须是: {', '.join(valid_difficulties)}")
|
||||
return v
|
||||
|
||||
@field_validator('status')
|
||||
@classmethod
|
||||
def validate_status(cls, v):
|
||||
"""验证状态"""
|
||||
valid_statuses = ['active', 'inactive']
|
||||
if v not in valid_statuses:
|
||||
raise ValueError(f"状态必须是: {', '.join(valid_statuses)}")
|
||||
return v
|
||||
|
||||
@field_validator('objectives')
|
||||
@classmethod
|
||||
def validate_objectives(cls, v):
|
||||
"""验证练习目标"""
|
||||
if not v or len(v) < 1:
|
||||
raise ValueError("至少需要1个练习目标")
|
||||
if len(v) > 10:
|
||||
raise ValueError("练习目标不能超过10个")
|
||||
return v
|
||||
|
||||
|
||||
class PracticeSceneCreate(PracticeSceneBase):
|
||||
"""创建陪练场景Schema"""
|
||||
pass
|
||||
|
||||
|
||||
class PracticeSceneUpdate(BaseModel):
|
||||
"""更新陪练场景Schema(所有字段可选)"""
|
||||
name: Optional[str] = Field(None, max_length=200, description="场景名称")
|
||||
description: Optional[str] = Field(None, description="场景描述")
|
||||
type: Optional[str] = Field(None, description="场景类型")
|
||||
difficulty: Optional[str] = Field(None, description="难度等级")
|
||||
status: Optional[str] = Field(None, description="状态")
|
||||
background: Optional[str] = Field(None, description="场景背景设定")
|
||||
ai_role: Optional[str] = Field(None, description="AI角色描述")
|
||||
objectives: Optional[List[str]] = Field(None, description="练习目标数组")
|
||||
keywords: Optional[List[str]] = Field(None, description="关键词数组")
|
||||
duration: Optional[int] = Field(None, ge=1, le=120, description="预计时长(分钟)")
|
||||
|
||||
|
||||
class PracticeSceneResponse(PracticeSceneBase):
|
||||
"""陪练场景响应Schema"""
|
||||
id: int
|
||||
usage_count: int
|
||||
rating: float
|
||||
created_by: Optional[int] = None
|
||||
updated_by: Optional[int] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ==================== 对话Schema ====================
|
||||
|
||||
class StartPracticeRequest(BaseModel):
|
||||
"""开始陪练对话请求Schema"""
|
||||
# 场景信息(首次消息必填,后续消息可选)
|
||||
scene_id: Optional[int] = Field(None, description="场景ID(可选)")
|
||||
scene_name: Optional[str] = Field(None, description="场景名称")
|
||||
scene_description: Optional[str] = Field(None, description="场景描述")
|
||||
scene_background: Optional[str] = Field(None, description="场景背景")
|
||||
scene_ai_role: Optional[str] = Field(None, description="AI角色")
|
||||
scene_objectives: Optional[List[str]] = Field(None, description="练习目标")
|
||||
scene_keywords: Optional[List[str]] = Field(None, description="关键词")
|
||||
|
||||
# 对话信息
|
||||
user_message: str = Field(..., description="用户消息")
|
||||
conversation_id: Optional[str] = Field(None, description="对话ID(续接对话时必填)")
|
||||
is_first: bool = Field(..., description="是否首次消息")
|
||||
|
||||
@field_validator('scene_name')
|
||||
@classmethod
|
||||
def validate_scene_name_for_first(cls, v, info):
|
||||
"""首次消息时场景名称必填"""
|
||||
if info.data.get('is_first') and not v:
|
||||
raise ValueError("首次消息时场景名称必填")
|
||||
return v
|
||||
|
||||
@field_validator('scene_background')
|
||||
@classmethod
|
||||
def validate_scene_background_for_first(cls, v, info):
|
||||
"""首次消息时场景背景必填"""
|
||||
if info.data.get('is_first') and not v:
|
||||
raise ValueError("首次消息时场景背景必填")
|
||||
return v
|
||||
|
||||
@field_validator('scene_ai_role')
|
||||
@classmethod
|
||||
def validate_scene_ai_role_for_first(cls, v, info):
|
||||
"""首次消息时AI角色必填"""
|
||||
if info.data.get('is_first') and not v:
|
||||
raise ValueError("首次消息时AI角色必填")
|
||||
return v
|
||||
|
||||
@field_validator('scene_objectives')
|
||||
@classmethod
|
||||
def validate_scene_objectives_for_first(cls, v, info):
|
||||
"""首次消息时练习目标必填"""
|
||||
if info.data.get('is_first') and (not v or len(v) == 0):
|
||||
raise ValueError("首次消息时练习目标必填")
|
||||
return v
|
||||
|
||||
|
||||
class InterruptPracticeRequest(BaseModel):
|
||||
"""中断对话请求Schema"""
|
||||
conversation_id: str = Field(..., description="对话ID")
|
||||
chat_id: str = Field(..., description="聊天ID")
|
||||
|
||||
|
||||
class ConversationInfo(BaseModel):
|
||||
"""对话信息Schema"""
|
||||
id: str = Field(..., description="对话ID")
|
||||
name: str = Field(..., description="对话名称")
|
||||
created_at: int = Field(..., description="创建时间(时间戳)")
|
||||
|
||||
|
||||
class ConversationsResponse(BaseModel):
|
||||
"""对话列表响应Schema"""
|
||||
items: List[ConversationInfo]
|
||||
has_more: bool
|
||||
page: int
|
||||
size: int
|
||||
|
||||
|
||||
# ==================== 场景提取Schema ====================
|
||||
|
||||
class ExtractSceneRequest(BaseModel):
|
||||
"""提取场景请求Schema"""
|
||||
course_id: int = Field(..., description="课程ID")
|
||||
|
||||
|
||||
class ExtractedSceneData(BaseModel):
|
||||
"""提取的场景数据Schema"""
|
||||
name: str = Field(..., description="场景名称")
|
||||
description: str = Field(..., description="场景描述")
|
||||
type: str = Field(..., description="场景类型")
|
||||
difficulty: str = Field(..., description="难度等级")
|
||||
background: str = Field(..., description="场景背景")
|
||||
ai_role: str = Field(..., description="AI角色描述")
|
||||
objectives: List[str] = Field(..., description="练习目标数组")
|
||||
keywords: Optional[List[str]] = Field(default=[], description="关键词数组")
|
||||
|
||||
|
||||
class ExtractSceneResponse(BaseModel):
|
||||
"""提取场景响应Schema"""
|
||||
scene: ExtractedSceneData = Field(..., description="场景数据")
|
||||
workflow_run_id: str = Field(..., description="工作流运行ID")
|
||||
task_id: str = Field(..., description="任务ID")
|
||||
|
||||
|
||||
# ==================== 陪练会话Schema ====================
|
||||
|
||||
class PracticeSessionCreate(BaseModel):
|
||||
"""创建陪练会话请求Schema"""
|
||||
scene_id: Optional[int] = Field(None, description="场景ID")
|
||||
scene_name: str = Field(..., description="场景名称")
|
||||
scene_type: Optional[str] = Field(None, description="场景类型")
|
||||
conversation_id: Optional[str] = Field(None, description="Coze对话ID")
|
||||
|
||||
|
||||
class PracticeSessionResponse(BaseModel):
|
||||
"""陪练会话响应Schema"""
|
||||
id: int
|
||||
session_id: str
|
||||
user_id: int
|
||||
scene_id: Optional[int]
|
||||
scene_name: str
|
||||
scene_type: Optional[str]
|
||||
conversation_id: Optional[str]
|
||||
start_time: datetime
|
||||
end_time: Optional[datetime]
|
||||
duration_seconds: int
|
||||
turns: int
|
||||
status: str
|
||||
created_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SaveDialogueRequest(BaseModel):
|
||||
"""保存对话记录请求Schema"""
|
||||
session_id: str = Field(..., description="会话ID")
|
||||
speaker: str = Field(..., description="说话人: user/ai")
|
||||
content: str = Field(..., description="对话内容")
|
||||
sequence: int = Field(..., ge=1, description="顺序号(从1开始)")
|
||||
|
||||
|
||||
class PracticeDialogueResponse(BaseModel):
|
||||
"""对话记录响应Schema"""
|
||||
id: int
|
||||
session_id: str
|
||||
speaker: str
|
||||
content: str
|
||||
timestamp: datetime
|
||||
sequence: int
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# ==================== 分析报告Schema ====================
|
||||
|
||||
class ScoreBreakdownItem(BaseModel):
|
||||
"""分数细分项"""
|
||||
name: str
|
||||
score: int = Field(..., ge=0, le=100)
|
||||
description: str
|
||||
|
||||
|
||||
class AbilityDimensionItem(BaseModel):
|
||||
"""能力维度项"""
|
||||
name: str
|
||||
score: int = Field(..., ge=0, le=100)
|
||||
feedback: str
|
||||
|
||||
|
||||
class DialogueReviewItem(BaseModel):
|
||||
"""对话复盘项"""
|
||||
speaker: str
|
||||
time: str
|
||||
content: str
|
||||
tags: List[str] = Field(default_factory=list)
|
||||
comment: str = Field(default="")
|
||||
|
||||
|
||||
class SuggestionItem(BaseModel):
|
||||
"""改进建议项"""
|
||||
title: str
|
||||
content: str
|
||||
example: Optional[str] = None
|
||||
|
||||
|
||||
class PracticeAnalysisResult(BaseModel):
|
||||
"""陪练分析结果Schema"""
|
||||
total_score: int = Field(..., ge=0, le=100, description="综合得分")
|
||||
score_breakdown: List[ScoreBreakdownItem] = Field(..., description="分数细分")
|
||||
ability_dimensions: List[AbilityDimensionItem] = Field(..., description="能力维度")
|
||||
dialogue_review: List[DialogueReviewItem] = Field(..., description="对话复盘")
|
||||
suggestions: List[SuggestionItem] = Field(..., description="改进建议")
|
||||
|
||||
|
||||
class PracticeReportResponse(BaseModel):
|
||||
"""陪练报告响应Schema"""
|
||||
session_info: PracticeSessionResponse
|
||||
analysis: PracticeAnalysisResult
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
Reference in New Issue
Block a user