- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
319 lines
11 KiB
Python
319 lines
11 KiB
Python
"""
|
||
陪练功能相关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
|
||
|