🎭 飞行侠实现:多身份登录系统

核心功能:
- 用户模型扩展:linked_agents 字段存储绑定龙虾
- 登录 API 支持 3 种模式:human_only / agent_only / both
- 龙虾管理 API:绑定/解绑/列表
- 扫描本机龙虾 API:从注册实例获取

API 端点:
- POST /api/v1/auth/login/ - 支持 login_mode 和 selected_agent_id
- GET  /api/v1/user/linked-agents/ - 获取绑定龙虾
- POST /api/v1/user/linked-agents/ - 添加绑定龙虾
- DELETE /api/v1/user/linked-agents/{id}/ - 移除龙虾
- GET  /api/v1/user/scan-local-agents/ - 扫描本机龙虾

登录模式:
1. human_only - 纯人类身份(1 个座位)
2. agent_only - 纯龙虾身份(1 个座位)
3. both - 双重身份(2 个座位)

测试:
- test_multi_identity.py: 完整测试通过

使用场景:
- 普通用户参会 → human_only
- 龙虾独立参会 → agent_only
- 用户带龙虾助理 → both
This commit is contained in:
2026-04-04 12:53:02 +08:00
parent 9a30cc3945
commit 97da46b219
6 changed files with 471 additions and 6 deletions

View File

@@ -1,3 +1,50 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
# Create your models here.
class User(AbstractUser):
"""扩展用户模型"""
email = models.EmailField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
# 绑定的龙虾列表JSON 存储)
# 示例:[{"agent_id": "flying_hero", "agent_name": "飞行侠", "agent_emoji": "🦸", "instance_id": "phospher-openclaw"}]
linked_agents = models.JSONField(default=list, verbose_name='绑定的龙虾', blank=True)
class Meta:
db_table = 'users'
verbose_name = '用户'
verbose_name_plural = '用户'
# 避免与 auth.User 冲突
app_label = 'users'
def __str__(self):
return self.username
def add_linked_agent(self, agent_id: str, agent_name: str, agent_emoji: str = '🤖', instance_id: str = None):
"""添加绑定的龙虾"""
agent = {
'agent_id': agent_id,
'agent_name': agent_name,
'agent_emoji': agent_emoji,
'instance_id': instance_id
}
# 避免重复
for i, a in enumerate(self.linked_agents):
if a.get('agent_id') == agent_id:
self.linked_agents[i] = agent
return
self.linked_agents.append(agent)
self.save()
def remove_linked_agent(self, agent_id: str):
"""移除绑定的龙虾"""
self.linked_agents = [a for a in self.linked_agents if a.get('agent_id') != agent_id]
self.save()
def get_linked_agent(self, agent_id: str):
"""获取指定的龙虾信息"""
for agent in self.linked_agents:
if agent.get('agent_id') == agent_id:
return agent
return None