""" 会议管理 API 路由 接入 MeetingScheduler(栅栏同步)+ MeetingRecorder(会议记录) """ from fastapi import APIRouter, HTTPException from pydantic import BaseModel from typing import List, Optional from dataclasses import asdict from datetime import datetime from ..services.meeting_scheduler import get_meeting_scheduler from ..services.meeting_recorder import get_meeting_recorder router = APIRouter() class MeetingCreate(BaseModel): title: str agenda: Optional[str] = "" meeting_type: Optional[str] = "design_review" attendees: List[str] = [] steps: Optional[List[str]] = None class MeetingWaitRequest(BaseModel): agent_id: str timeout: Optional[int] = 300 class DiscussionRequest(BaseModel): agent_id: str agent_name: Optional[str] = "" content: str step: Optional[str] = "" class ProgressRequest(BaseModel): step: str class FinishRequest(BaseModel): consensus: Optional[str] = "" def _meeting_to_dict(meeting) -> dict: """将 MeetingInfo 转为前端友好的 dict""" return { "meeting_id": meeting.meeting_id, "title": meeting.title, "date": meeting.date, "status": meeting.status, "attendees": meeting.attendees, "steps": [ {"step_id": s.step_id, "label": s.label, "status": s.status} for s in meeting.steps ], "discussions": [ { "agent_id": d.agent_id, "agent_name": d.agent_name, "content": d.content, "timestamp": d.timestamp, "step": d.step } for d in meeting.discussions ], "progress_summary": meeting.progress_summary, "consensus": meeting.consensus, "created_at": meeting.created_at, "ended_at": meeting.ended_at } @router.get("") @router.get("/") async def list_meetings(date: Optional[str] = None): """获取会议列表(默认今天)""" recorder = get_meeting_recorder() meetings = await recorder.list_meetings(date) return {"meetings": [_meeting_to_dict(m) for m in meetings]} @router.get("/today") async def list_today_meetings(): """获取今日会议""" recorder = get_meeting_recorder() meetings = await recorder.list_meetings() return {"meetings": [_meeting_to_dict(m) for m in meetings]} @router.post("/") async def create_meeting(meeting: MeetingCreate): """创建新会议(同时创建调度记录和会议记录)""" recorder = get_meeting_recorder() scheduler = get_meeting_scheduler() meeting_id = f"meeting-{int(datetime.now().timestamp())}" # 在调度器中创建(用于栅栏同步) await scheduler.create_meeting( meeting_id=meeting_id, title=meeting.title, expected_attendees=meeting.attendees ) # 在记录器中创建(用于记录内容) meeting_info = await recorder.create_meeting( meeting_id=meeting_id, title=meeting.title, attendees=meeting.attendees, steps=meeting.steps ) return _meeting_to_dict(meeting_info) @router.post("/create") async def create_meeting_alt(meeting: MeetingCreate): """创建会议 API(前端使用的端点,与 POST / 相同)""" return await create_meeting(meeting) @router.get("/{meeting_id}") async def get_meeting(meeting_id: str, date: Optional[str] = None): """获取会议详情""" recorder = get_meeting_recorder() meeting_info = await recorder.get_meeting(meeting_id, date) if not meeting_info: raise HTTPException(status_code=404, detail="Meeting not found") return _meeting_to_dict(meeting_info) @router.get("/{meeting_id}/queue") async def get_meeting_queue(meeting_id: str): """获取会议等待队列""" scheduler = get_meeting_scheduler() queue = await scheduler.get_queue(meeting_id) if not queue: raise HTTPException(status_code=404, detail="Meeting queue not found") return { "meeting_id": queue.meeting_id, "title": queue.title, "status": queue.status, "expected_attendees": queue.expected_attendees, "arrived_attendees": queue.arrived_attendees, "missing_attendees": queue.missing_attendees, "progress": queue.progress, "is_ready": queue.is_ready } @router.post("/{meeting_id}/wait") async def wait_for_meeting(meeting_id: str, request: MeetingWaitRequest): """栅栏同步等待(阻塞直到所有参会者到齐或超时)""" scheduler = get_meeting_scheduler() status = await scheduler.wait_for_meeting( agent_id=request.agent_id, meeting_id=meeting_id, timeout=request.timeout or 300 ) return {"meeting_id": meeting_id, "status": status} @router.post("/{meeting_id}/end") async def end_meeting(meeting_id: str): """结束会议(调度层)""" scheduler = get_meeting_scheduler() success = await scheduler.end_meeting(meeting_id) if not success: raise HTTPException(status_code=404, detail="Meeting not found") return {"success": True, "meeting_id": meeting_id} @router.post("/{meeting_id}/join") async def join_meeting(meeting_id: str, data: dict): """Agent 加入会议""" agent_id = data.get("agent_id", "") scheduler = get_meeting_scheduler() await scheduler.add_attendee(meeting_id, agent_id) return {"success": True, "meeting_id": meeting_id, "agent_id": agent_id} @router.post("/{meeting_id}/discuss") async def add_discussion(meeting_id: str, data: DiscussionRequest): """添加讨论内容""" recorder = get_meeting_recorder() await recorder.add_discussion( meeting_id=meeting_id, agent_id=data.agent_id, agent_name=data.agent_name or data.agent_id, content=data.content, step=data.step or "" ) return {"success": True, "meeting_id": meeting_id} @router.post("/{meeting_id}/finish") async def finish_meeting(meeting_id: str, data: FinishRequest): """完成会议(记录层 - 保存共识并标记完成)""" recorder = get_meeting_recorder() success = await recorder.end_meeting( meeting_id=meeting_id, consensus=data.consensus or "" ) if not success: raise HTTPException(status_code=404, detail="Meeting not found") # 同时结束调度 scheduler = get_meeting_scheduler() await scheduler.end_meeting(meeting_id) return {"success": True, "meeting_id": meeting_id} @router.post("/{meeting_id}/progress") async def update_progress(meeting_id: str, data: ProgressRequest): """更新会议进度""" recorder = get_meeting_recorder() await recorder.update_progress( meeting_id=meeting_id, step_label=data.step ) return {"success": True, "meeting_id": meeting_id} @router.post("/record/create") async def create_meeting_record(data: dict): """创建会议记录(前端使用的端点)""" recorder = get_meeting_recorder() meeting_id = data.get("meeting_id", f"meeting-{int(datetime.now().timestamp())}") meeting_info = await recorder.create_meeting( meeting_id=meeting_id, title=data.get("title", "未命名会议"), attendees=data.get("attendees", []), steps=data.get("steps", []) ) # 同时在调度器中注册 scheduler = get_meeting_scheduler() await scheduler.create_meeting( meeting_id=meeting_id, title=data.get("title", "未命名会议"), expected_attendees=data.get("attendees", []) ) return _meeting_to_dict(meeting_info) @router.post("/record/{meeting_id}/discussion") async def add_meeting_discussion(meeting_id: str, data: dict): """添加会议讨论(前端使用的端点)""" recorder = get_meeting_recorder() await recorder.add_discussion( meeting_id=meeting_id, agent_id=data.get("agent_id", ""), agent_name=data.get("agent_name", data.get("agent_id", "")), content=data.get("content", ""), step=data.get("step", "") ) return {"success": True, "meeting_id": meeting_id, "discussion": data}