Files
meeting-room/backend/users/views.py
flying-hero 65000664ef 🎯 重构登录逻辑:以人为核心的三种出战模式
核心变更:
- 单枪匹马 (solo) - 人类单独出战
- 组队团战 (team) - 人类 +N 龙虾
- 独当一面 (agent_only) - 龙虾单独出征

后端:
- users/views.py: 支持多选 agent_ids
- 新增 mode_names 映射
- 错误提示优化

前端:
- 新模式选择 UI(带图标和说明)
- 多选龙虾复选框
- 实时显示已选龙虾数量
- 选中模式高亮显示

测试:
- test_new_login.py: 完整测试三种模式
- 绑定第二只龙虾(龙虾监控 🦞)

结果:
 单枪匹马 - 1 个人类座位
 组队团战 - 1+N 个座位(人类 + 龙虾)
 独当一面 - N 个龙虾座位
2026-04-04 16:41:13 +08:00

239 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from rest_framework import serializers, status, views
from rest_framework.response import Response
from django.contrib.auth import authenticate, get_user_model
User = get_user_model()
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
mode = serializers.ChoiceField(
choices=['solo', 'team', 'agent_only'],
default='solo',
help_text='solo=单枪匹马team=组队团战agent_only=独当一面'
)
agent_ids = serializers.ListField(
child=serializers.CharField(),
required=False,
default=list,
help_text='选择的龙虾 ID 列表team 或 agent_only 模式)'
)
class LoginView(views.APIView):
def post(self, request):
serializer = LoginSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
username = serializer.validated_data['username']
password = serializer.validated_data['password']
mode = serializer.validated_data.get('mode', 'solo')
agent_ids = serializer.validated_data.get('agent_ids', [])
user = authenticate(username=username, password=password)
if not user:
return Response(
{'detail': '用户名或密码错误'},
status=status.HTTP_401_UNAUTHORIZED
)
# 简单 Token生产环境应该用 JWT
import uuid
token = uuid.uuid4().hex
# 构建会话信息
sessions = []
if mode in ['solo', 'team']:
# 人类身份(单枪匹马 或 组队团战)
sessions.append({
'session_type': 'human',
'nickname': user.username,
'emoji': '👤',
'user_id': user.id
})
if mode in ['team', 'agent_only']:
# 龙虾身份(组队团战 或 独当一面)
if not agent_ids:
return Response(
{'error': '组队或独当一面模式需要选择至少一只龙虾'},
status=status.HTTP_400_BAD_REQUEST
)
# 添加所有选择的龙虾
for agent_id in agent_ids:
agent = user.get_linked_agent(agent_id)
if agent:
sessions.append({
'session_type': 'agent',
'agent_id': agent['agent_id'],
'agent_name': agent['agent_name'],
'nickname': agent['agent_name'],
'emoji': agent.get('agent_emoji', '🤖'),
'instance_id': agent.get('instance_id')
})
else:
return Response(
{'error': f'未找到绑定的龙虾:{agent_id}'},
status=status.HTTP_400_BAD_REQUEST
)
# 模式名称映射
mode_names = {
'solo': '单枪匹马',
'team': '组队团战',
'agent_only': '独当一面'
}
return Response({
'token': token,
'user': {
'id': user.id,
'username': user.username,
'email': user.email,
'linked_agents': user.linked_agents
},
'sessions': sessions,
'mode': mode,
'mode_name': mode_names.get(mode, mode)
})
class RegisterSerializer(serializers.Serializer):
username = serializers.CharField()
email = serializers.EmailField()
password = serializers.CharField()
class RegisterView(views.APIView):
def post(self, request):
serializer = RegisterSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.create_user(
username=serializer.validated_data['username'],
email=serializer.validated_data['email'],
password=serializer.validated_data['password']
)
import uuid
token = uuid.uuid4().hex
return Response({
'token': token,
'user': {
'id': user.id,
'username': user.username,
'email': user.email
}
}, status=status.HTTP_201_CREATED)
except Exception as e:
return Response(
{'detail': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
class LinkedAgentsView(views.APIView):
"""
用户绑定的龙虾管理
GET /api/v1/user/linked-agents/?username=xxx - 获取龙虾列表
POST /api/v1/user/linked-agents/ - 添加龙虾
DELETE /api/v1/user/linked-agents/{agent_id}/?username=xxx - 移除龙虾
"""
def get(self, request):
username = request.query_params.get('username')
if not username:
return Response({'error': '缺少 username'}, status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.get(username=username)
return Response({
'linked_agents': user.linked_agents,
'count': len(user.linked_agents)
})
except User.DoesNotExist:
return Response({'error': '用户不存在'}, status=status.HTTP_404_NOT_FOUND)
def post(self, request):
username = request.data.get('username')
agent_id = request.data.get('agent_id')
agent_name = request.data.get('agent_name')
agent_emoji = request.data.get('agent_emoji', '🤖')
instance_id = request.data.get('instance_id')
if not all([username, agent_id, agent_name]):
return Response({'error': '缺少必要参数'}, status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.get(username=username)
user.add_linked_agent(agent_id, agent_name, agent_emoji, instance_id)
return Response({
'status': 'success',
'linked_agents': user.linked_agents
})
except User.DoesNotExist:
return Response({'error': '用户不存在'}, status=status.HTTP_404_NOT_FOUND)
def delete(self, request, agent_id):
username = request.query_params.get('username')
if not username:
return Response({'error': '缺少 username'}, status=status.HTTP_400_BAD_REQUEST)
try:
user = User.objects.get(username=username)
user.remove_linked_agent(agent_id)
return Response({
'status': 'success',
'linked_agents': user.linked_agents
})
except User.DoesNotExist:
return Response({'error': '用户不存在'}, status=status.HTTP_404_NOT_FOUND)
class ScanLocalAgentsView(views.APIView):
"""
扫描本机龙虾列表
GET /api/v1/user/scan-local-agents/?instance_id=xxx - 扫描指定实例
GET /api/v1/user/scan-local-agents/ - 扫描所有实例
"""
def get(self, request):
instance_id = request.query_params.get('instance_id')
if not instance_id:
# 返回所有已注册实例的 Agent
from instances.models import Instance
instances = Instance.objects.filter(is_active=True)
agents = []
for inst in instances:
for agent_id in inst.agent_ids:
agents.append({
'agent_id': agent_id,
'instance_id': inst.instance_id,
'instance_name': inst.instance_name
})
else:
# 返回指定实例的 Agent
from instances.models import Instance
try:
instance = Instance.objects.get(instance_id=instance_id, is_active=True)
agents = [{
'agent_id': agent_id,
'instance_id': instance.instance_id,
'instance_name': instance.instance_name
} for agent_id in instance.agent_ids]
except Instance.DoesNotExist:
return Response({'error': '实例不存在'}, status=status.HTTP_404_NOT_FOUND)
return Response({
'agents': agents,
'count': len(agents)
})