- 后端:扩展 SSE 支持 WebRTC 信令消息转发 - 前端:创建 WebRTC 连接管理模块 (webrtc.ts) - 前端:创建 useVoiceCall 组合式函数 - 前端:在对练房间添加语音通话 UI - 集成 Web Speech API 实现语音转文字
This commit is contained in:
@@ -48,6 +48,13 @@ class JoinRoomRequest(BaseModel):
|
||||
class SendMessageRequest(BaseModel):
|
||||
"""发送消息请求"""
|
||||
content: str = Field(..., description="消息内容")
|
||||
source: Optional[str] = Field("text", description="消息来源: text/voice")
|
||||
|
||||
|
||||
class WebRTCSignalRequest(BaseModel):
|
||||
"""WebRTC 信令请求"""
|
||||
signal_type: str = Field(..., description="信令类型: voice_offer/voice_answer/ice_candidate/voice_start/voice_end")
|
||||
payload: dict = Field(..., description="信令数据(SDP/ICE候选等)")
|
||||
|
||||
|
||||
class RoomResponse(BaseModel):
|
||||
@@ -399,6 +406,60 @@ async def send_message(
|
||||
}
|
||||
|
||||
|
||||
@router.post("/{room_code}/signal", summary="发送WebRTC信令")
|
||||
async def send_signal(
|
||||
room_code: str,
|
||||
request: WebRTCSignalRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
发送 WebRTC 信令消息
|
||||
|
||||
信令类型:
|
||||
- voice_start: 发起语音通话
|
||||
- voice_offer: SDP Offer
|
||||
- voice_answer: SDP Answer
|
||||
- ice_candidate: ICE 候选
|
||||
- voice_end: 结束语音通话
|
||||
"""
|
||||
service = PracticeRoomService(db)
|
||||
|
||||
# 获取房间
|
||||
room = await service.get_room_by_code(room_code.upper())
|
||||
if not room:
|
||||
raise HTTPException(status_code=404, detail="房间不存在")
|
||||
|
||||
# 检查用户是否在房间中
|
||||
user_role = room.get_user_role(current_user.id)
|
||||
if not user_role:
|
||||
raise HTTPException(status_code=403, detail="您不是房间参与者")
|
||||
|
||||
# 验证信令类型
|
||||
valid_signal_types = ["voice_start", "voice_offer", "voice_answer", "ice_candidate", "voice_end"]
|
||||
if request.signal_type not in valid_signal_types:
|
||||
raise HTTPException(status_code=400, detail=f"无效的信令类型,必须是: {', '.join(valid_signal_types)}")
|
||||
|
||||
# 发送信令消息(作为系统消息存储,用于 SSE 推送)
|
||||
message = await service.send_message(
|
||||
room_id=room.id,
|
||||
user_id=current_user.id,
|
||||
content=None, # 信令消息不需要文本内容
|
||||
role_name=None,
|
||||
message_type=request.signal_type,
|
||||
extra_data=request.payload
|
||||
)
|
||||
|
||||
return {
|
||||
"code": 200,
|
||||
"message": "信令发送成功",
|
||||
"data": {
|
||||
"signal_type": request.signal_type,
|
||||
"sequence": message.sequence
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@router.get("/{room_code}/messages", summary="获取消息列表")
|
||||
async def get_messages(
|
||||
room_code: str,
|
||||
|
||||
@@ -271,44 +271,56 @@ class PracticeRoomService:
|
||||
self,
|
||||
room_id: int,
|
||||
user_id: int,
|
||||
content: str,
|
||||
role_name: Optional[str] = None
|
||||
content: Optional[str],
|
||||
role_name: Optional[str] = None,
|
||||
message_type: Optional[str] = None,
|
||||
extra_data: Optional[dict] = None
|
||||
) -> PracticeRoomMessage:
|
||||
"""
|
||||
发送聊天消息
|
||||
发送聊天消息或信令消息
|
||||
|
||||
Args:
|
||||
room_id: 房间ID
|
||||
user_id: 发送者ID
|
||||
content: 消息内容
|
||||
role_name: 角色名称
|
||||
message_type: 消息类型(默认为 chat)
|
||||
extra_data: 额外数据(用于 WebRTC 信令等)
|
||||
|
||||
Returns:
|
||||
PracticeRoomMessage: 消息对象
|
||||
"""
|
||||
import json
|
||||
|
||||
# 获取当前消息序号
|
||||
sequence = await self._get_next_sequence(room_id)
|
||||
|
||||
# 如果是信令消息,将 extra_data 序列化到 content 中
|
||||
actual_content = content
|
||||
if extra_data and not content:
|
||||
actual_content = json.dumps(extra_data)
|
||||
|
||||
message = PracticeRoomMessage(
|
||||
room_id=room_id,
|
||||
user_id=user_id,
|
||||
message_type=self.MSG_TYPE_CHAT,
|
||||
content=content,
|
||||
message_type=message_type or self.MSG_TYPE_CHAT,
|
||||
content=actual_content,
|
||||
role_name=role_name,
|
||||
sequence=sequence
|
||||
)
|
||||
|
||||
self.db.add(message)
|
||||
|
||||
# 更新房间统计
|
||||
room = await self.get_room_by_id(room_id)
|
||||
if room:
|
||||
room.total_turns += 1
|
||||
user_role = room.get_user_role(user_id)
|
||||
if user_role == "A":
|
||||
room.role_a_turns += 1
|
||||
elif user_role == "B":
|
||||
room.role_b_turns += 1
|
||||
# 只有聊天消息才更新房间统计
|
||||
if (message_type or self.MSG_TYPE_CHAT) == self.MSG_TYPE_CHAT:
|
||||
room = await self.get_room_by_id(room_id)
|
||||
if room:
|
||||
room.total_turns += 1
|
||||
user_role = room.get_user_role(user_id)
|
||||
if user_role == "A":
|
||||
room.role_a_turns += 1
|
||||
elif user_role == "B":
|
||||
room.role_b_turns += 1
|
||||
|
||||
await self.db.commit()
|
||||
await self.db.refresh(message)
|
||||
|
||||
Reference in New Issue
Block a user