🎯 重构登录逻辑:以人为核心的三种出战模式

核心变更:
- 单枪匹马 (solo) - 人类单独出战
- 组队团战 (team) - 人类 +N 龙虾
- 独当一面 (agent_only) - 龙虾单独出征

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

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

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

结果:
 单枪匹马 - 1 个人类座位
 组队团战 - 1+N 个座位(人类 + 龙虾)
 独当一面 - N 个龙虾座位
This commit is contained in:
2026-04-04 16:41:13 +08:00
parent 97e4a6fecb
commit 65000664ef
3 changed files with 191 additions and 66 deletions

92
backend/test_new_login.py Normal file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python3
"""
测试新登录逻辑(单枪匹马/组队团战/独当一面)
"""
import requests
API_BASE = 'http://localhost:8000/api/v1'
def test_new_login():
print("="*60)
print("🎯 测试新登录逻辑")
print("="*60)
# 1. 单枪匹马
print("\n🥷 测试 1: 单枪匹马(仅人类)")
res = requests.post(f'{API_BASE}/auth/login/', json={
'username': 'test',
'password': 'test123',
'mode': 'solo'
})
if res.status_code == 200:
data = res.json()
print(f"✅ 登录成功")
print(f" 模式:{data['mode_name']}")
print(f" 会话数:{len(data['sessions'])}")
for s in data['sessions']:
print(f" - {s['session_type']}: {s['nickname']} ({s['emoji']})")
else:
print(f"❌ 登录失败:{res.json()}")
# 2. 组队团战
print("\n🛡️ 测试 2: 组队团战(人类 +2 龙虾)")
res = requests.post(f'{API_BASE}/auth/login/', json={
'username': 'test',
'password': 'test123',
'mode': 'team',
'agent_ids': ['flying_hero', 'lobster_monitor']
})
if res.status_code == 200:
data = res.json()
print(f"✅ 登录成功")
print(f" 模式:{data['mode_name']}")
print(f" 会话数:{len(data['sessions'])}")
for s in data['sessions']:
emoji = s.get('emoji', '🤖')
print(f" - {s['session_type']}: {s['nickname']} ({emoji})")
else:
print(f"❌ 登录失败:{res.json()}")
# 3. 独当一面
print("\n⚔️ 测试 3: 独当一面(仅龙虾)")
res = requests.post(f'{API_BASE}/auth/login/', json={
'username': 'test',
'password': 'test123',
'mode': 'agent_only',
'agent_ids': ['flying_hero']
})
if res.status_code == 200:
data = res.json()
print(f"✅ 登录成功")
print(f" 模式:{data['mode_name']}")
print(f" 会话数:{len(data['sessions'])}")
for s in data['sessions']:
emoji = s.get('emoji', '🤖')
print(f" - {s['session_type']}: {s['nickname']} ({emoji})")
else:
print(f"❌ 登录失败:{res.json()}")
# 4. 错误测试 - 组队但没选龙虾
print("\n❌ 测试 4: 组队但没选龙虾(应该失败)")
res = requests.post(f'{API_BASE}/auth/login/', json={
'username': 'test',
'password': 'test123',
'mode': 'team',
'agent_ids': []
})
if res.status_code == 400:
print(f"✅ 正确失败:{res.json()['error']}")
else:
print(f"❌ 应该失败但成功了:{res.json()}")
print("\n" + "="*60)
print("✅ 新登录逻辑测试完成!")
print("="*60)
print("\n📊 三种模式:")
print("1. 🥷 单枪匹马 - 人类单独出战")
print("2. 🛡️ 组队团战 - 人类 +N 龙虾")
print("3. ⚔️ 独当一面 - 龙虾单独出征")
if __name__ == '__main__':
test_new_login()

View File

@@ -8,11 +8,17 @@ User = get_user_model()
class LoginSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()
login_mode = serializers.ChoiceField(
choices=['human_only', 'agent_only', 'both'],
default='human_only'
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 模式)'
)
selected_agent_id = serializers.CharField(required=False, allow_blank=True)
class LoginView(views.APIView):
@@ -23,8 +29,8 @@ class LoginView(views.APIView):
username = serializer.validated_data['username']
password = serializer.validated_data['password']
login_mode = serializer.validated_data.get('login_mode', 'human_only')
selected_agent_id = serializer.validated_data.get('selected_agent_id')
mode = serializer.validated_data.get('mode', 'solo')
agent_ids = serializer.validated_data.get('agent_ids', [])
user = authenticate(username=username, password=password)
if not user:
@@ -40,8 +46,8 @@ class LoginView(views.APIView):
# 构建会话信息
sessions = []
if login_mode in ['human_only', 'both']:
# 人类身份
if mode in ['solo', 'team']:
# 人类身份(单枪匹马 或 组队团战)
sessions.append({
'session_type': 'human',
'nickname': user.username,
@@ -49,10 +55,17 @@ class LoginView(views.APIView):
'user_id': user.id
})
if login_mode in ['agent_only', 'both']:
# 龙虾身份
if selected_agent_id:
agent = user.get_linked_agent(selected_agent_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',
@@ -64,10 +77,17 @@ class LoginView(views.APIView):
})
else:
return Response(
{'error': f'未找到绑定的龙虾:{selected_agent_id}'},
{'error': f'未找到绑定的龙虾:{agent_id}'},
status=status.HTTP_400_BAD_REQUEST
)
# 模式名称映射
mode_names = {
'solo': '单枪匹马',
'team': '组队团战',
'agent_only': '独当一面'
}
return Response({
'token': token,
'user': {
@@ -77,7 +97,8 @@ class LoginView(views.APIView):
'linked_agents': user.linked_agents
},
'sessions': sessions,
'login_mode': login_mode
'mode': mode,
'mode_name': mode_names.get(mode, mode)
})