🎨 飞行侠完善 P1 功能:座位图 + @Agent + 会议纪要

新增功能:
- 座位可视化 - 圆形头像展示参会者
- @Agent 功能 - 定向消息给特定 Agent
- 会议纪要生成 - Web 界面一键生成
- 参会者列表 API

文件变更:
- meetings/views.py: mention_agent() 新接口
- templates/meeting_room.html:
  - 座位图 UI(圆形头像)
  - 生成纪要按钮
  - @Agent 按钮
- test_mention.py: @Agent 测试脚本

测试结果:
 完整功能测试 (7 项)
 会议纪要测试 (JSON + Markdown)
 @Agent 功能测试
This commit is contained in:
2026-04-04 11:43:41 +08:00
parent 53c3ac487a
commit d403583fb8
3 changed files with 298 additions and 2 deletions

View File

@@ -125,6 +125,70 @@ class MeetingViewSet(viewsets.ModelViewSet):
serializer = ParticipantSerializer(participants, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def mention_agent(self, request, pk=None):
"""@Agent 功能 - 发送消息给特定 Agent"""
meeting = self.get_object()
target_agent_id = request.data.get('target_agent_id')
content = request.data.get('content')
sender_agent_id = request.data.get('sender_agent_id')
sender_name = request.data.get('sender_name', 'User')
sender_emoji = request.data.get('sender_emoji', '👤')
if not target_agent_id or not content:
return Response(
{'error': '缺少 target_agent_id 或 content'},
status=status.HTTP_400_BAD_REQUEST
)
# 找到目标 Agent
target_participant = Participant.objects.filter(
meeting=meeting,
agent_id=target_agent_id,
left_at__isnull=True
).first()
if not target_participant:
return Response(
{'error': f'未找到 Agent: {target_agent_id}'},
status=status.HTTP_404_NOT_FOUND
)
# 获取或创建发送者
sender_participant = Participant.objects.filter(
meeting=meeting,
agent_id=sender_agent_id,
left_at__isnull=True
).first()
if not sender_participant and sender_agent_id:
sender_participant = Participant.objects.create(
meeting=meeting,
agent_type='openclaw',
agent_id=sender_agent_id,
agent_name=sender_name,
agent_emoji=sender_emoji,
nickname=f"{sender_emoji} {sender_name}"
)
if not sender_participant:
# 人类发送的,用主持人
sender_participant = Participant.objects.filter(
meeting=meeting,
is_host=True
).first()
# 创建消息,标记为需要回复
message = Message.objects.create(
meeting=meeting,
sender=sender_participant,
content=f"@{target_participant.nickname} {content}",
is_broadcast=False, # 只发给目标 Agent
requires_response=True
)
return Response(MessageSerializer(message).data, status=status.HTTP_201_CREATED)
@action(detail=True, methods=['get'])
def messages(self, request, pk=None):
"""获取消息(人类轮询)"""