"""会话路由""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.dependencies import get_db, get_current_user from app.models.user import User from app.schemas.conversation import ( ConversationCreate, ConversationRead, ConversationDetail, GroupCreate, GroupUpdate, MemberAdd, RoleUpdate, ) from app.services.conversation_service import ConversationService from app.services.draft_service import DraftService from app.websocket.events import EventType from app.websocket.manager import manager from pydantic import BaseModel router = APIRouter() class ConvPrefs(BaseModel): is_pinned: bool | None = None is_muted: bool | None = None @router.get("/", response_model=list[dict]) async def list_conversations( user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """获取会话列表""" service = ConversationService(db) return await service.get_user_conversations(user.id) @router.post("/", response_model=dict) async def create_conversation( req: ConversationCreate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """创建会话(私聊或群聊)""" service = ConversationService(db) if req.type == "private": if len(req.member_ids) != 1: raise HTTPException(status_code=400, detail="私聊只能选择一个用户") conv = await service.get_or_create_private(user.id, req.member_ids[0]) else: conv = await service.create_group(user.id, req.name or "群聊", req.member_ids) detail = await service.get_conversation_detail(conv.id, user.id) return detail @router.post("/group", response_model=dict) async def create_group( req: GroupCreate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """创建群聊""" service = ConversationService(db) conv = await service.create_group(user.id, req.name, req.member_ids, req.description) detail = await service.get_conversation_detail(conv.id, user.id) return detail @router.get("/{conversation_id}", response_model=dict) async def get_conversation( conversation_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """获取会话详情""" service = ConversationService(db) detail = await service.get_conversation_detail(conversation_id, user.id) if not detail: raise HTTPException(status_code=404, detail="会话不存在或无权访问") return detail @router.put("/{conversation_id}") async def update_group( conversation_id: str, req: GroupUpdate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """更新群聊信息(群主/管理员)""" service = ConversationService(db) try: await service.update_group(conversation_id, user.id, **req.model_dump(exclude_none=True)) return {"success": True, "message": "群信息已更新"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.post("/{conversation_id}/members") async def add_members( conversation_id: str, req: MemberAdd, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """添加群成员(群主/管理员)""" service = ConversationService(db) try: await service.add_members(conversation_id, user.id, req.user_ids) # 通知新成员被加入群聊 detail = await service.get_conversation_detail(conversation_id, user.id) group_name = detail.get("name", "群聊") if detail else "群聊" for mid in req.user_ids: await manager.send_to_user(mid, EventType.CONVERSATION_MEMBER_ADDED, { "conversation_id": conversation_id, "group_name": group_name, "added_by_user_id": user.id, "added_by_username": user.username, }) return {"success": True, "message": "成员已添加"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.delete("/{conversation_id}/members/{target_user_id}") async def remove_member( conversation_id: str, target_user_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """移除群成员(群主/管理员)""" service = ConversationService(db) try: await service.remove_member(conversation_id, user.id, target_user_id) # 通知被移除的用户 await manager.send_to_user(target_user_id, EventType.CONVERSATION_MEMBER_REMOVED, { "conversation_id": conversation_id, "removed_by_user_id": user.id, "removed_by_username": user.username, }) return {"success": True, "message": "成员已移除"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.post("/{conversation_id}/leave") async def leave_group( conversation_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """退出群聊""" service = ConversationService(db) try: await service.leave_group(conversation_id, user.id) return {"success": True, "message": "已退出群聊"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.post("/{conversation_id}/dissolve") async def dissolve_group( conversation_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """解散群聊(仅群主)""" service = ConversationService(db) try: await service.dissolve_group(conversation_id, user.id) return {"success": True, "message": "群聊已解散"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.put("/{conversation_id}/members/{target_user_id}/role") async def update_member_role( conversation_id: str, target_user_id: str, req: RoleUpdate, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """修改群成员角色(群主)""" service = ConversationService(db) try: await service.update_member_role(conversation_id, user.id, target_user_id, req.role) return {"success": True, "message": "角色已更新"} except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.put("/{conversation_id}/prefs") async def update_prefs( conversation_id: str, req: ConvPrefs, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """更新会话个人偏好(置顶/免打扰)""" service = ConversationService(db) try: return await service.update_prefs(conversation_id, user.id, req.is_pinned, req.is_muted) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.get("/{conversation_id}/draft") async def get_draft( conversation_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """获取草稿""" service = DraftService() return {"draft": await service.get(user.id, conversation_id)} @router.put("/{conversation_id}/draft") async def save_draft( conversation_id: str, body: dict, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """保存草稿""" service = DraftService() await service.set(user.id, conversation_id, body.get("draft", "")) return {"success": True} @router.get("/{conversation_id}/announcement") async def get_announcement( conversation_id: str, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """获取群公告""" from app.services.announcement_service import AnnouncementService service = AnnouncementService(db) return await service.get(conversation_id) @router.post("/{conversation_id}/announcement") async def set_announcement( conversation_id: str, body: dict, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """设置群公告(管理员)""" from app.services.announcement_service import AnnouncementService service = AnnouncementService(db) try: return await service.upsert(conversation_id, user.id, body.get("content", "")) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @router.put("/{conversation_id}/mute-all") async def toggle_mute_all( conversation_id: str, body: dict, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """全员禁言开关(群主/管理员)""" service = ConversationService(db) try: await service.update_group(conversation_id, user.id, mute_all=body.get("mute_all")) return {"success": True, "mute_all": body.get("mute_all")} except ValueError as e: raise HTTPException(status_code=400, detail=str(e))