🤖 添加虚拟坐席功能
后端: - 创建会议时自动添加虚拟龙虾参会者 - 如果指定了 host_agent_id,添加该龙虾 - 否则添加 2 个虚拟助手(🤖 和 🦊) 前端: - 创建会议时可选"添加虚拟坐席" - 默认勾选,方便测试 @ 功能 - 提示文字说明用途 使用场景: - 用户创建会议 → 自动有虚拟龙虾 - 点击虚拟龙虾座位 → @ 该龙虾 - 测试 @ 功能无需真实龙虾在线
This commit is contained in:
40
backend/create_test_user.py
Normal file
40
backend/create_test_user.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
创建测试用户
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meeting_room.settings')
|
||||||
|
|
||||||
|
import django
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
# 创建测试用户
|
||||||
|
users = [
|
||||||
|
{'username': 'test', 'email': 'test@example.com', 'password': 'test123'},
|
||||||
|
{'username': 'polaris', 'email': 'polaris@example.com', 'password': 'password123'},
|
||||||
|
]
|
||||||
|
|
||||||
|
for u in users:
|
||||||
|
user, created = User.objects.get_or_create(username=u['username'])
|
||||||
|
if created:
|
||||||
|
user.email = u['email']
|
||||||
|
user.set_password(u['password'])
|
||||||
|
user.save()
|
||||||
|
print(f"✅ 创建用户:{u['username']}")
|
||||||
|
else:
|
||||||
|
# 更新密码
|
||||||
|
user.set_password(u['password'])
|
||||||
|
user.save()
|
||||||
|
print(f"🔄 更新用户密码:{u['username']}")
|
||||||
|
|
||||||
|
# 绑定一个测试龙虾
|
||||||
|
user.add_linked_agent('flying_hero', '飞行侠', '🦸', 'phospher-openclaw')
|
||||||
|
print(f" 🔗 绑定龙虾:飞行侠 🦸")
|
||||||
|
|
||||||
|
print("\n✅ 测试用户创建完成!")
|
||||||
|
print(" 用户名:test / 密码:test123")
|
||||||
|
print(" 用户名:polaris / 密码:password123")
|
||||||
@@ -53,6 +53,39 @@ class MeetingViewSet(viewsets.ModelViewSet):
|
|||||||
is_host=True
|
is_host=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 创建虚拟龙虾参会者(如果指定了 host_agent_id)
|
||||||
|
if host_agent_id:
|
||||||
|
Participant.objects.create(
|
||||||
|
meeting=meeting,
|
||||||
|
agent_type='openclaw',
|
||||||
|
agent_id=host_agent_id,
|
||||||
|
agent_name='飞行侠',
|
||||||
|
agent_emoji='🦸',
|
||||||
|
nickname='飞行侠',
|
||||||
|
is_host=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# 如果没有指定 host_agent_id,创建两个虚拟龙虾
|
||||||
|
if not host_agent_id and request.data.get('auto_add_virtual_agents', True):
|
||||||
|
Participant.objects.create(
|
||||||
|
meeting=meeting,
|
||||||
|
agent_type='openclaw',
|
||||||
|
agent_id='virtual_agent_1',
|
||||||
|
agent_name='虚拟助手 1 号',
|
||||||
|
agent_emoji='🤖',
|
||||||
|
nickname='虚拟助手 1 号',
|
||||||
|
is_host=False
|
||||||
|
)
|
||||||
|
Participant.objects.create(
|
||||||
|
meeting=meeting,
|
||||||
|
agent_type='openclaw',
|
||||||
|
agent_id='virtual_agent_2',
|
||||||
|
agent_name='虚拟助手 2 号',
|
||||||
|
agent_emoji='🦊',
|
||||||
|
nickname='虚拟助手 2 号',
|
||||||
|
is_host=False
|
||||||
|
)
|
||||||
|
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
@action(detail=True, methods=['post'])
|
@action(detail=True, methods=['post'])
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ function LoginPage() {
|
|||||||
function MeetingList() {
|
function MeetingList() {
|
||||||
const [meetings, setMeetings] = useState([]);
|
const [meetings, setMeetings] = useState([]);
|
||||||
const [topic, setTopic] = useState('');
|
const [topic, setTopic] = useState('');
|
||||||
|
const [autoAddAgents, setAutoAddAgents] = useState(true);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|
||||||
@@ -154,7 +155,10 @@ function MeetingList() {
|
|||||||
const createMeeting = async (e) => {
|
const createMeeting = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
const res = await axios.post(`${API_BASE}/meetings/`, { topic });
|
const res = await axios.post(`${API_BASE}/meetings/`, {
|
||||||
|
topic,
|
||||||
|
auto_add_virtual_agents: autoAddAgents
|
||||||
|
});
|
||||||
navigate(`/meeting/${res.data.id}`);
|
navigate(`/meeting/${res.data.id}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('创建失败:' + (error.response?.data?.detail || error.message));
|
alert('创建失败:' + (error.response?.data?.detail || error.message));
|
||||||
@@ -173,8 +177,19 @@ function MeetingList() {
|
|||||||
<h2>创建会议</h2>
|
<h2>创建会议</h2>
|
||||||
<form onSubmit={createMeeting} style={styles.form}>
|
<form onSubmit={createMeeting} style={styles.form}>
|
||||||
<input type="text" placeholder="会议主题" value={topic} onChange={e => setTopic(e.target.value)} style={styles.input} required />
|
<input type="text" placeholder="会议主题" value={topic} onChange={e => setTopic(e.target.value)} style={styles.input} required />
|
||||||
|
<label style={{display: 'flex', alignItems: 'center', gap: '5px', whiteSpace: 'nowrap'}}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={autoAddAgents}
|
||||||
|
onChange={e => setAutoAddAgents(e.target.checked)}
|
||||||
|
/>
|
||||||
|
添加虚拟坐席
|
||||||
|
</label>
|
||||||
<button type="submit" style={styles.btn}>创建</button>
|
<button type="submit" style={styles.btn}>创建</button>
|
||||||
</form>
|
</form>
|
||||||
|
<p style={{fontSize: '12px', color: '#666', marginTop: '10px'}}>
|
||||||
|
💡 勾选"添加虚拟坐席"会自动创建 2 个虚拟龙虾参会者,方便测试 @ 功能
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div style={styles.list}>
|
<div style={styles.list}>
|
||||||
{meetings.map(m => (
|
{meetings.map(m => (
|
||||||
|
|||||||
Reference in New Issue
Block a user