Refactor chat channel join/leave logic in room API
Removed redundant safe join/leave channel functions and streamlined chat channel management for room creation, user addition, and removal. Channel operations now use direct server calls with improved error handling and logging, reducing complexity and ensuring main room flows are not interrupted by chat channel errors.
This commit is contained in:
@@ -62,6 +62,20 @@ async def _ensure_room_chat_channel(
|
||||
await db.commit()
|
||||
await db.refresh(ch)
|
||||
|
||||
# 2) (可选)把房主加入频道 & 触发在线侧同步
|
||||
# 如果你有 server 并希望立即让房主加入聊天频道,可取消注释以下代码
|
||||
"""
|
||||
try:
|
||||
from app.router.v2.chat import server # 视你的项目实际路径调整
|
||||
host_user = await db.get(User, host_user_id)
|
||||
# server.batch_join_channel 接口签名:([users], channel, session)
|
||||
await server.batch_join_channel([host_user], ch, db)
|
||||
await db.commit()
|
||||
except Exception as e:
|
||||
# 不中断主流程,打日志即可
|
||||
logger.debug(f"Warning: failed to join host {host_user_id} to chat channel {ch.channel_id}: {e}")
|
||||
"""
|
||||
|
||||
return ch
|
||||
|
||||
|
||||
@@ -73,8 +87,6 @@ async def _alloc_channel_id(db: Database) -> int:
|
||||
result = await db.execute(select(func.max(Room.channel_id)))
|
||||
current_max = result.scalar() or 100
|
||||
return int(current_max) + 1
|
||||
|
||||
|
||||
class RoomCreateRequest(BaseModel):
|
||||
"""Request model for creating a multiplayer room."""
|
||||
name: str
|
||||
@@ -480,37 +492,6 @@ async def _transfer_ownership_or_end_room(db: Database, room_id: int, leaving_us
|
||||
# 没有其他参与者,结束房间
|
||||
return await _end_room_if_empty(db, room_id)
|
||||
|
||||
|
||||
async def _safely_join_channel(channel_id: int, user_id: int, max_retries: int = 3) -> bool:
|
||||
"""安全地让用户加入聊天频道,带重试机制"""
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
await server.join_room_channel(int(channel_id), int(user_id))
|
||||
logger.debug(f"Successfully joined user {user_id} to channel {channel_id} on attempt {attempt + 1}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Attempt {attempt + 1} failed to join user {user_id} to channel {channel_id}: {e}")
|
||||
if attempt == max_retries - 1:
|
||||
logger.debug(f"Failed to join user {user_id} to channel {channel_id} after {max_retries} attempts")
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
async def _safely_leave_channel(channel_id: int, user_id: int, max_retries: int = 3) -> bool:
|
||||
"""安全地让用户离开聊天频道,带重试机制"""
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
await server.leave_room_channel(int(channel_id), int(user_id))
|
||||
logger.debug(f"Successfully removed user {user_id} from channel {channel_id} on attempt {attempt + 1}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.debug(f"Attempt {attempt + 1} failed to remove user {user_id} from channel {channel_id}: {e}")
|
||||
if attempt == max_retries - 1:
|
||||
logger.debug(f"Failed to remove user {user_id} from channel {channel_id} after {max_retries} attempts")
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
# ===== API ENDPOINTS =====
|
||||
|
||||
@router.post("/multiplayer/rooms")
|
||||
@@ -541,58 +522,26 @@ async def create_multiplayer_room(
|
||||
room_id = room.id
|
||||
|
||||
try:
|
||||
# 确保聊天频道存在
|
||||
channel = await _ensure_room_chat_channel(db, room, host_user_id)
|
||||
|
||||
# 让房主加入频道
|
||||
host_user = await db.get(User, host_user_id)
|
||||
if host_user:
|
||||
await server.batch_join_channel([host_user], channel, db)
|
||||
# Add playlist items
|
||||
await _add_playlist_items(db, room_id, room_data, host_user_id)
|
||||
|
||||
# 修复:确保房主被添加为参与者
|
||||
await _add_host_as_participant(db, room_id, host_user_id)
|
||||
# Add host as participant
|
||||
#await _add_host_as_participant(db, room_id, host_user_id)
|
||||
|
||||
# 提交数据库更改
|
||||
await db.commit()
|
||||
|
||||
# 房主加入聊天频道(在数据库提交后进行)
|
||||
host_user = await db.get(User, host_user_id)
|
||||
if host_user and channel:
|
||||
try:
|
||||
# 使用批量加入确保房主正确加入频道
|
||||
await server.batch_join_channel([host_user], channel, db)
|
||||
await db.commit() # 提交频道加入状态
|
||||
|
||||
# 额外确保房主在内存频道中注册
|
||||
success = await _safely_join_channel(channel.channel_id, host_user_id)
|
||||
if not success:
|
||||
logger.error(f"Critical: Failed to register host {host_user_id} in channel {channel.channel_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add host {host_user_id} to channel {channel.channel_id}: {e}")
|
||||
# 不中断房间创建流程,但记录严重错误
|
||||
|
||||
return room_id
|
||||
|
||||
except HTTPException:
|
||||
# Clean up room if setup fails
|
||||
await db.rollback()
|
||||
try:
|
||||
await db.delete(room)
|
||||
await db.commit()
|
||||
except:
|
||||
pass
|
||||
# Clean up room if playlist creation fails
|
||||
await db.delete(room)
|
||||
await db.commit()
|
||||
raise
|
||||
except Exception as e:
|
||||
# Clean up on unexpected errors
|
||||
await db.rollback()
|
||||
try:
|
||||
await db.delete(room)
|
||||
await db.commit()
|
||||
except:
|
||||
pass
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to setup room: {str(e)}"
|
||||
)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
raise HTTPException(
|
||||
@@ -602,13 +551,13 @@ async def create_multiplayer_room(
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to create room: {str(e)}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@router.delete("/multiplayer/rooms/{room_id}/users/{user_id}")
|
||||
async def remove_user_from_room(
|
||||
request: Request,
|
||||
@@ -649,9 +598,6 @@ async def remove_user_from_room(
|
||||
# 如果房间已经结束,直接返回
|
||||
if ends_at is not None:
|
||||
logger.debug(f"Room {room_id} is already ended")
|
||||
# 仍然尝试清理频道状态
|
||||
if channel_id:
|
||||
await _safely_leave_channel(int(channel_id), int(user_id))
|
||||
return {"success": True, "room_ended": True}
|
||||
|
||||
# 检查用户是否在房间中
|
||||
@@ -670,14 +616,13 @@ async def remove_user_from_room(
|
||||
room_ended = await _end_room_if_empty(db, room_id)
|
||||
await db.commit()
|
||||
|
||||
# 清理频道状态(即使用户不在参与者列表中)
|
||||
if channel_id:
|
||||
await _safely_leave_channel(int(channel_id), int(user_id))
|
||||
if room_ended:
|
||||
try:
|
||||
try:
|
||||
if channel_id:
|
||||
await server.leave_room_channel(int(channel_id), int(user_id))
|
||||
if room_ended:
|
||||
server.channels.pop(int(channel_id), None)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.debug(f"[warn] failed to leave user {user_id} from channel {channel_id}: {e}")
|
||||
|
||||
return {"success": True, "room_ended": room_ended}
|
||||
|
||||
@@ -702,23 +647,17 @@ async def remove_user_from_room(
|
||||
# 不是房主离开,只需检查房间是否为空
|
||||
room_ended = await _end_room_if_empty(db, room_id)
|
||||
|
||||
# 提交数据库更改
|
||||
await db.commit()
|
||||
logger.debug(f"Successfully removed user {user_id} from room {room_id}, room_ended: {room_ended}")
|
||||
|
||||
# 清理聊天频道状态
|
||||
if channel_id:
|
||||
success = await _safely_leave_channel(int(channel_id), int(user_id))
|
||||
if not success:
|
||||
logger.warning(f"Failed to remove user {user_id} from channel {channel_id}, but continuing")
|
||||
|
||||
if room_ended:
|
||||
try:
|
||||
# 清理内存中的频道数据
|
||||
# ===== 新增:提交后,把用户从聊天频道移除;若房间已结束,清理内存频道 =====
|
||||
try:
|
||||
if channel_id:
|
||||
await server.leave_room_channel(int(channel_id), int(user_id))
|
||||
if room_ended:
|
||||
server.channels.pop(int(channel_id), None)
|
||||
logger.debug(f"Cleaned up channel {channel_id} from memory")
|
||||
except Exception as e:
|
||||
logger.debug(f"Warning: Failed to cleanup channel {channel_id} from memory: {e}")
|
||||
except Exception as e:
|
||||
logger.debug(f"[warn] failed to leave user {user_id} from channel {channel_id}: {e}")
|
||||
|
||||
return {"success": True, "room_ended": room_ended}
|
||||
|
||||
@@ -726,7 +665,7 @@ async def remove_user_from_room(
|
||||
raise
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Error removing user from room: {str(e)}")
|
||||
logger.debug(f"Error removing user from room: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to remove user from room: {str(e)}"
|
||||
@@ -762,81 +701,56 @@ async def add_user_to_room(
|
||||
detail="Invalid request signature"
|
||||
)
|
||||
|
||||
# 检查房间是否已结束
|
||||
room_result = await db.execute(
|
||||
select(Room.id, Room.ends_at, Room.channel_id, Room.host_id)
|
||||
.where(col(Room.id) == room_id)
|
||||
)
|
||||
room_row = room_result.first()
|
||||
if not room_row:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
|
||||
_, ends_at, channel_id, host_user_id = room_row
|
||||
if ends_at is not None:
|
||||
logger.debug(f"User {user_id} attempted to join ended room {room_id}")
|
||||
raise HTTPException(status_code=410, detail="Room has ended and cannot accept new participants")
|
||||
|
||||
# Verify room password
|
||||
provided_password = user_data.get("password") if user_data else None
|
||||
logger.debug(f"Verifying room {room_id} with password: {provided_password}")
|
||||
await _verify_room_password(db, room_id, provided_password)
|
||||
|
||||
# Add or update participant
|
||||
await _add_or_update_participant(db, room_id, user_id)
|
||||
# Update participant count
|
||||
await _update_room_participant_count(db, room_id)
|
||||
|
||||
# 先提交 DB 状态,确保参与关系已生效
|
||||
await db.commit()
|
||||
logger.debug(f"Successfully added user {user_id} to room {room_id}")
|
||||
|
||||
# ===== 新增:确保有聊天频道并把用户加入 =====
|
||||
try:
|
||||
# 检查房间是否已结束
|
||||
room_result = await db.execute(
|
||||
select(Room.id, Room.ends_at, Room.channel_id, Room.host_id)
|
||||
.where(col(Room.id) == room_id)
|
||||
)
|
||||
room_row = room_result.first()
|
||||
if not room_row:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
# 若房间还没分配/创建频道,补建并同步回写
|
||||
if not channel_id:
|
||||
room = await db.get(Room, room_id)
|
||||
if room is None:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
await _ensure_room_chat_channel(db, room, host_user_id)
|
||||
await db.refresh(room)
|
||||
channel_id = room.channel_id
|
||||
|
||||
_, ends_at, channel_id, host_user_id = room_row
|
||||
if ends_at is not None:
|
||||
logger.debug(f"User {user_id} attempted to join ended room {room_id}")
|
||||
raise HTTPException(status_code=410, detail="Room has ended and cannot accept new participants")
|
||||
|
||||
# Verify room password
|
||||
provided_password = user_data.get("password") if user_data else None
|
||||
logger.debug(f"Verifying room {room_id} with password: {provided_password}")
|
||||
await _verify_room_password(db, room_id, provided_password)
|
||||
|
||||
# 验证用户存在
|
||||
user = await _validate_user_exists(db, user_id)
|
||||
|
||||
# Add or update participant
|
||||
await _add_or_update_participant(db, room_id, user_id)
|
||||
# Update participant count
|
||||
await _update_room_participant_count(db, room_id)
|
||||
|
||||
# 先提交 DB 状态,确保参与关系已生效
|
||||
await db.commit()
|
||||
logger.debug(f"Successfully added user {user_id} to room {room_id}")
|
||||
|
||||
# 确保聊天频道存在并让用户加入
|
||||
try:
|
||||
# 若房间还没分配/创建频道,补建并同步回写
|
||||
if not channel_id:
|
||||
room = await db.get(Room, room_id)
|
||||
if room is None:
|
||||
raise HTTPException(status_code=404, detail="Room not found")
|
||||
channel = await _ensure_room_chat_channel(db, room, host_user_id)
|
||||
await db.commit()
|
||||
await db.refresh(room)
|
||||
channel_id = room.channel_id
|
||||
|
||||
if channel_id:
|
||||
# 使用安全的加入频道方法
|
||||
success = await _safely_join_channel(int(channel_id), int(user_id))
|
||||
if success:
|
||||
logger.debug(f"User {user_id} successfully joined channel {channel_id}")
|
||||
else:
|
||||
logger.error(f"Critical: User {user_id} failed to join channel {channel_id}")
|
||||
# 不抛出异常,允许用户继续在房间中,但记录错误
|
||||
else:
|
||||
logger.warning(f"Room {room_id} has no channel_id after ensure")
|
||||
|
||||
except Exception as e:
|
||||
# 频道加入失败不应该影响用户加入房间的主要功能
|
||||
logger.error(f"Failed to join user {user_id} to channel of room {room_id}: {e}")
|
||||
# 返回成功,但标记频道状态异常
|
||||
return {
|
||||
"success": True,
|
||||
"channel_error": f"Failed to join chat channel: {str(e)}"
|
||||
}
|
||||
|
||||
return {"success": True}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
if channel_id:
|
||||
# 加入聊天频道 → 内存注册 + 给在线客户端发 chat.channel.join
|
||||
await server.join_room_channel(int(channel_id), int(user_id))
|
||||
else:
|
||||
# 理论上不会发生;留日志以便排查
|
||||
logger.debug(f"[warn] Room {room_id} has no channel_id after ensure.")
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Error adding user to room: {str(e)}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to add user to room: {str(e)}"
|
||||
)
|
||||
# 不影响加入房间主流程,仅记录
|
||||
logger.debug(f"[warn] failed to join user {user_id} to channel of room {room_id}: {e}")
|
||||
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@router.post("/beatmaps/ensure")
|
||||
|
||||
Reference in New Issue
Block a user