2026-04-04 11:19:01 +08:00
|
|
|
from rest_framework import viewsets, status, permissions
|
|
|
|
|
from rest_framework.decorators import action
|
|
|
|
|
from rest_framework.response import Response
|
|
|
|
|
from django.utils import timezone
|
2026-04-05 09:46:21 +08:00
|
|
|
from datetime import timedelta
|
|
|
|
|
from .models import Meeting, Participant, Message
|
|
|
|
|
from .serializers import MeetingSerializer, ParticipantSerializer, MessageSerializer
|
2026-04-04 11:19:01 +08:00
|
|
|
from django.contrib.auth import get_user_model
|
|
|
|
|
from django.shortcuts import get_object_or_404
|
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MeetingViewSet(viewsets.ModelViewSet):
|
|
|
|
|
"""会议室视图集"""
|
|
|
|
|
queryset = Meeting.objects.all()
|
|
|
|
|
serializer_class = MeetingSerializer
|
|
|
|
|
permission_classes = [] # 临时开放所有权限
|
|
|
|
|
|
|
|
|
|
def get_queryset(self):
|
|
|
|
|
return Meeting.objects.all().order_by('-created_at')
|
|
|
|
|
|
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
|
|
|
"""创建会议"""
|
|
|
|
|
serializer = self.get_serializer(data=request.data)
|
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 使用第一个用户作为 host
|
2026-04-04 11:19:01 +08:00
|
|
|
host = User.objects.first()
|
|
|
|
|
|
|
|
|
|
meeting = serializer.save(host=host)
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 设置 1 小时过期时间
|
|
|
|
|
meeting.expires_at = timezone.now() + timedelta(hours=1)
|
|
|
|
|
meeting.save()
|
2026-04-04 12:42:58 +08:00
|
|
|
|
2026-04-04 11:19:01 +08:00
|
|
|
# 创建主持人参会记录
|
|
|
|
|
Participant.objects.create(
|
|
|
|
|
meeting=meeting,
|
|
|
|
|
user=host,
|
|
|
|
|
agent_type='human',
|
|
|
|
|
nickname=host.username,
|
|
|
|
|
is_host=True
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 处理随行龙虾
|
2026-04-04 17:27:51 +08:00
|
|
|
agent_ids = request.data.get('agent_ids', [])
|
|
|
|
|
for agent_id in agent_ids:
|
2026-04-04 13:04:26 +08:00
|
|
|
Participant.objects.create(
|
|
|
|
|
meeting=meeting,
|
|
|
|
|
agent_type='openclaw',
|
2026-04-04 17:27:51 +08:00
|
|
|
agent_id=agent_id,
|
2026-04-05 09:46:21 +08:00
|
|
|
agent_name='Agent',
|
2026-04-04 13:04:26 +08:00
|
|
|
agent_emoji='🤖',
|
2026-04-05 09:46:21 +08:00
|
|
|
nickname=f'Agent {agent_id}',
|
2026-04-04 13:04:26 +08:00
|
|
|
is_host=False
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-04 11:19:01 +08:00
|
|
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
|
|
|
|
|
|
|
|
|
@action(detail=True, methods=['post'])
|
|
|
|
|
def join(self, request, pk=None):
|
2026-04-05 09:46:21 +08:00
|
|
|
"""加入会议(支持批量加入)"""
|
2026-04-04 11:19:01 +08:00
|
|
|
meeting = self.get_object()
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 检查会议是否过期
|
|
|
|
|
if meeting.expires_at and timezone.now() > meeting.expires_at:
|
|
|
|
|
# 过期会议,清空坐席
|
|
|
|
|
meeting.participants.all().delete()
|
|
|
|
|
return Response(
|
|
|
|
|
{'error': '会议已过期,坐席已清空'},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-04 11:19:01 +08:00
|
|
|
if meeting.status == 'ended':
|
|
|
|
|
return Response(
|
|
|
|
|
{'error': '会议已结束'},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
invite_code = request.data.get('invite_code')
|
|
|
|
|
if invite_code != meeting.invite_code:
|
|
|
|
|
return Response(
|
|
|
|
|
{'error': '邀请码错误'},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 获取要加入的龙虾 ID 列表
|
|
|
|
|
agent_ids = request.data.get('agent_ids', [])
|
2026-04-04 11:43:41 +08:00
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 获取当前用户
|
|
|
|
|
user = User.objects.first() # 临时使用第一个用户
|
2026-04-04 11:43:41 +08:00
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 检查用户是否已加入
|
|
|
|
|
existing = Participant.objects.filter(
|
2026-04-04 11:43:41 +08:00
|
|
|
meeting=meeting,
|
2026-04-05 09:46:21 +08:00
|
|
|
user=user,
|
2026-04-04 11:43:41 +08:00
|
|
|
left_at__isnull=True
|
|
|
|
|
).first()
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
if not existing:
|
|
|
|
|
# 用户加入会议
|
|
|
|
|
Participant.objects.create(
|
2026-04-04 11:43:41 +08:00
|
|
|
meeting=meeting,
|
2026-04-05 09:46:21 +08:00
|
|
|
user=user,
|
|
|
|
|
agent_type='human',
|
|
|
|
|
nickname=user.username if user else '用户',
|
|
|
|
|
is_host=False
|
2026-04-04 11:43:41 +08:00
|
|
|
)
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 随行龙虾加入会议
|
|
|
|
|
joined_agents = []
|
|
|
|
|
for agent_id in agent_ids:
|
|
|
|
|
# 检查是否已存在
|
|
|
|
|
exists = Participant.objects.filter(
|
2026-04-04 11:43:41 +08:00
|
|
|
meeting=meeting,
|
2026-04-05 09:46:21 +08:00
|
|
|
agent_id=agent_id,
|
|
|
|
|
left_at__isnull=True
|
2026-04-04 11:43:41 +08:00
|
|
|
).first()
|
2026-04-05 09:46:21 +08:00
|
|
|
|
|
|
|
|
if not exists:
|
|
|
|
|
participant = Participant.objects.create(
|
|
|
|
|
meeting=meeting,
|
|
|
|
|
agent_type='openclaw',
|
|
|
|
|
agent_id=agent_id,
|
|
|
|
|
agent_name='Agent',
|
|
|
|
|
agent_emoji='🤖',
|
|
|
|
|
nickname=f'Agent {agent_id}',
|
|
|
|
|
is_host=False
|
|
|
|
|
)
|
|
|
|
|
joined_agents.append(ParticipantSerializer(participant).data)
|
|
|
|
|
|
|
|
|
|
# 返回所有参会者
|
|
|
|
|
participants = Participant.objects.filter(
|
2026-04-04 11:43:41 +08:00
|
|
|
meeting=meeting,
|
2026-04-05 09:46:21 +08:00
|
|
|
left_at__isnull=True
|
2026-04-04 11:43:41 +08:00
|
|
|
)
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
return Response({
|
|
|
|
|
'message': '加入成功',
|
|
|
|
|
'participants': ParticipantSerializer(participants, many=True).data,
|
|
|
|
|
'joined_agents': joined_agents
|
|
|
|
|
})
|
2026-04-04 11:19:01 +08:00
|
|
|
|
|
|
|
|
@action(detail=True, methods=['post'])
|
|
|
|
|
def send_message(self, request, pk=None):
|
|
|
|
|
"""发送消息"""
|
|
|
|
|
meeting = self.get_object()
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
# 获取或创建参会者
|
2026-04-04 11:19:01 +08:00
|
|
|
participant = Participant.objects.filter(
|
|
|
|
|
meeting=meeting,
|
|
|
|
|
left_at__isnull=True
|
|
|
|
|
).first()
|
|
|
|
|
|
|
|
|
|
if not participant:
|
|
|
|
|
host = meeting.host
|
|
|
|
|
participant = Participant.objects.create(
|
|
|
|
|
meeting=meeting,
|
|
|
|
|
user=host,
|
|
|
|
|
agent_type='human',
|
|
|
|
|
nickname=host.username
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
content = request.data.get('content')
|
|
|
|
|
if not content:
|
|
|
|
|
return Response(
|
|
|
|
|
{'error': '消息内容不能为空'},
|
|
|
|
|
status=status.HTTP_400_BAD_REQUEST
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
message = Message.objects.create(
|
|
|
|
|
meeting=meeting,
|
|
|
|
|
sender=participant,
|
|
|
|
|
content=content,
|
|
|
|
|
is_broadcast=request.data.get('is_broadcast', True),
|
|
|
|
|
requires_response=request.data.get('requires_response', False)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return Response(MessageSerializer(message).data, status=status.HTTP_201_CREATED)
|
|
|
|
|
|
|
|
|
|
@action(detail=True, methods=['get'])
|
2026-04-05 09:46:21 +08:00
|
|
|
def messages(self, request, pk=None):
|
|
|
|
|
"""获取消息"""
|
2026-04-04 11:19:01 +08:00
|
|
|
meeting = self.get_object()
|
2026-04-05 09:46:21 +08:00
|
|
|
last_id = request.query_params.get('last_id', 0)
|
2026-04-04 11:19:01 +08:00
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
messages = meeting.messages.filter(id__gt=last_id).select_related('sender')
|
2026-04-04 11:19:01 +08:00
|
|
|
serializer = MessageSerializer(messages, many=True)
|
|
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
return Response({'messages': serializer.data})
|
2026-04-04 11:39:31 +08:00
|
|
|
|
2026-04-05 09:46:21 +08:00
|
|
|
@action(detail=True, methods=['get'])
|
|
|
|
|
def participants(self, request, pk=None):
|
|
|
|
|
"""获取参会者列表"""
|
2026-04-04 11:39:31 +08:00
|
|
|
meeting = self.get_object()
|
2026-04-05 09:46:21 +08:00
|
|
|
participants = meeting.participants.filter(left_at__isnull=True)
|
|
|
|
|
serializer = ParticipantSerializer(participants, many=True)
|
|
|
|
|
return Response(serializer.data)
|