Add updated_at and created_at to Playlist model
Introduces created_at and updated_at fields to the Playlist model for tracking creation and modification times. Refactors room user removal logic to handle owner transfer and participant count updates more robustly. Updates room query logic for more accurate filtering by category and status. Adds migration stubs for updated_at column in room playlists.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from app.models.model import UTCBaseModel
|
||||
from app.models.mods import APIMod
|
||||
@@ -60,6 +60,17 @@ class Playlist(PlaylistBase, table=True):
|
||||
}
|
||||
)
|
||||
room: "Room" = Relationship()
|
||||
created_at: Optional[datetime] = Field(
|
||||
default=None,
|
||||
sa_column_kwargs={"server_default": func.now()}
|
||||
)
|
||||
updated_at: Optional[datetime] = Field(
|
||||
default=None,
|
||||
sa_column_kwargs={
|
||||
"server_default": func.now(),
|
||||
"onupdate": func.now()
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_next_id_for_room(cls, room_id: int, session: AsyncSession) -> int:
|
||||
|
||||
@@ -438,51 +438,109 @@ async def remove_user_from_room(
|
||||
timestamp: str = "",
|
||||
) -> Dict[str, Any]:
|
||||
"""Remove a user from a multiplayer room."""
|
||||
""" try: """
|
||||
# Verify request signature
|
||||
body = await request.body()
|
||||
# 直接用内部获取的时间戳
|
||||
now = utcnow()
|
||||
if not verify_request_signature(request, timestamp, body):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid request signature"
|
||||
try:
|
||||
# Verify request signature
|
||||
body = await request.body()
|
||||
now = utcnow()
|
||||
if not verify_request_signature(request, timestamp, body):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid request signature"
|
||||
)
|
||||
|
||||
# 检查房间是否存在
|
||||
room_query = await db.fetch_one(
|
||||
select(Room.owner_id, Room.status, Room.participant_count)
|
||||
.where(col(Room.id) == room_id)
|
||||
)
|
||||
|
||||
if not room_query:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Room not found"
|
||||
)
|
||||
|
||||
room_owner_id = room_query['owner_id']
|
||||
room_status = room_query['status']
|
||||
current_participant_count = room_query['participant_count']
|
||||
|
||||
# 检查用户是否在房间中
|
||||
participant_query = await db.fetch_one(
|
||||
select(RoomParticipatedUser.id)
|
||||
.where(
|
||||
col(RoomParticipatedUser.room_id) == room_id,
|
||||
col(RoomParticipatedUser.user_id) == user_id,
|
||||
col(RoomParticipatedUser.left_at).is_(None)
|
||||
)
|
||||
)
|
||||
|
||||
if not participant_query:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="User is not in this room"
|
||||
)
|
||||
|
||||
# 标记用户离开房间
|
||||
await db.execute(
|
||||
update(RoomParticipatedUser)
|
||||
.where(
|
||||
col(RoomParticipatedUser.room_id) == room_id,
|
||||
col(RoomParticipatedUser.user_id) == user_id,
|
||||
col(RoomParticipatedUser.left_at).is_(None)
|
||||
)
|
||||
.values(left_at=now)
|
||||
)
|
||||
|
||||
# 更新房间结束时间
|
||||
await db.execute(
|
||||
update(Room)
|
||||
.where(col(Room.id) == room_id)
|
||||
.values(ends_at=now, status=RoomStatus.IDLE)
|
||||
)
|
||||
# 检查是否是房主离开
|
||||
if user_id == room_owner_id:
|
||||
# 房主离开,查找其他参与者来转让房主权限
|
||||
remaining_result = await db.execute(
|
||||
select(RoomParticipatedUser.user_id)
|
||||
.where(
|
||||
col(RoomParticipatedUser.room_id) == room_id,
|
||||
col(RoomParticipatedUser.user_id) != user_id,
|
||||
col(RoomParticipatedUser.left_at).is_(None)
|
||||
)
|
||||
.order_by(col(RoomParticipatedUser.joined_at)) # 按加入时间排序,选择最早的
|
||||
)
|
||||
remaining_participants = remaining_result.all()
|
||||
|
||||
if remaining_participants:
|
||||
# 将房主权限转让给最早加入的用户
|
||||
new_owner_id = remaining_participants[0][0] # 获取 user_id
|
||||
await db.execute(
|
||||
update(Room)
|
||||
.where(col(Room.id) == room_id)
|
||||
.values(owner_id=new_owner_id)
|
||||
)
|
||||
|
||||
# 记录房主转让日志(可选)
|
||||
# logger.info(f"Room {room_id} ownership transferred from {user_id} to {new_owner_id}")
|
||||
else:
|
||||
# 没有其他参与者,房间应该被标记为结束
|
||||
await db.execute(
|
||||
update(Room)
|
||||
.where(col(Room.id) == room_id)
|
||||
.values(
|
||||
ends_at=now,
|
||||
status=RoomStatus.IDLE,
|
||||
participant_count=0
|
||||
)
|
||||
)
|
||||
await db.commit()
|
||||
return {"success": True, "room_ended": True}
|
||||
|
||||
# 标记所有参与者已离开
|
||||
await db.execute(
|
||||
update(RoomParticipatedUser)
|
||||
.where(col(RoomParticipatedUser.room_id) == room_id, col(RoomParticipatedUser.left_at).is_(None))
|
||||
.values(left_at=now)
|
||||
)
|
||||
# 更新房间参与者数量
|
||||
await _update_room_participant_count(db, room_id)
|
||||
|
||||
await db.commit()
|
||||
return {"success": True, "room_ended": False}
|
||||
|
||||
# 人数归零
|
||||
await db.execute(
|
||||
update(Room)
|
||||
.where(col(Room.id) == room_id)
|
||||
.values(participant_count=0)
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
|
||||
|
||||
# Update participant count
|
||||
await _update_room_participant_count(db, room_id)
|
||||
|
||||
await db.commit()
|
||||
return {"success": True}
|
||||
|
||||
""" except HTTPException:
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Failed to remove user from room: {str(e)}"
|
||||
) """
|
||||
)
|
||||
@@ -50,14 +50,26 @@ async def get_all_rooms(
|
||||
current_user: User = Security(get_current_user, scopes=["public"]),
|
||||
):
|
||||
resp_list: list[RoomResp] = []
|
||||
where_clauses: list[ColumnElement[bool]] = [col(Room.category) == category]
|
||||
if category == RoomCategory.REALTIME:
|
||||
db_category = RoomCategory.NORMAL # 实际查询 normal
|
||||
else:
|
||||
db_category = category
|
||||
where_clauses: list[ColumnElement[bool]] = [col(Room.category) == db_category]
|
||||
now = utcnow()
|
||||
|
||||
if status is not None:
|
||||
where_clauses.append(col(Room.status) == status)
|
||||
print(mode, category, status, current_user.id)
|
||||
if mode == "open":
|
||||
where_clauses.append((col(Room.ends_at).is_(None)) | (col(Room.ends_at) > now.replace(tzinfo=UTC)))
|
||||
if category == RoomCategory.REALTIME:
|
||||
where_clauses.append(col(Room.id).in_(MultiplayerHubs.rooms.keys()))
|
||||
# 修改为新的查询逻辑:状态为 idle 或 playing,starts_at 不为空,ends_at 为空
|
||||
where_clauses.extend([
|
||||
col(Room.status).in_([RoomStatus.IDLE, RoomStatus.PLAYING]),
|
||||
col(Room.starts_at).is_not(None),
|
||||
col(Room.ends_at).is_(None)
|
||||
])
|
||||
#if category == RoomCategory.REALTIME:
|
||||
# where_clauses.append(col(Room.id).in_(MultiplayerHubs.rooms.keys()))
|
||||
|
||||
if mode == "participated":
|
||||
where_clauses.append(
|
||||
exists().where(
|
||||
@@ -65,11 +77,14 @@ async def get_all_rooms(
|
||||
col(RoomParticipatedUser.user_id) == current_user.id,
|
||||
)
|
||||
)
|
||||
|
||||
if mode == "owned":
|
||||
where_clauses.append(col(Room.host_id) == current_user.id)
|
||||
|
||||
if mode == "ended":
|
||||
where_clauses.append((col(Room.ends_at).is_not(None)) & (col(Room.ends_at) < now.replace(tzinfo=UTC)))
|
||||
|
||||
# 使用 select 指定需要的字段,对应您的 SQL 语句
|
||||
db_rooms = (
|
||||
(
|
||||
await db.exec(
|
||||
@@ -81,7 +96,7 @@ async def get_all_rooms(
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
|
||||
print("Retrieved rooms:", db_rooms)
|
||||
for room in db_rooms:
|
||||
resp = await RoomResp.from_db(room, db)
|
||||
if category == RoomCategory.REALTIME:
|
||||
@@ -409,4 +424,4 @@ async def get_room_events(
|
||||
playlist_items=playlist_items_resps,
|
||||
room=room_resp,
|
||||
user=user_resps,
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
"""add_updated_at_column_to_room_playlists
|
||||
|
||||
Revision ID: 20c6df84813f
|
||||
Revises: 57bacf936413
|
||||
Create Date: 2025-08-24 00:08:14.704724
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "20c6df84813f"
|
||||
down_revision: str | Sequence[str] | None = "57bacf936413"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
pass
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
pass
|
||||
@@ -0,0 +1,26 @@
|
||||
"""add_updated_at_column_to_room_playlists
|
||||
|
||||
Revision ID: 7576ca1e056d
|
||||
Revises: 20c6df84813f
|
||||
Create Date: 2025-08-24 00:08:42.419252
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "7576ca1e056d"
|
||||
down_revision: str | Sequence[str] | None = "20c6df84813f"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
pass
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
pass
|
||||
@@ -0,0 +1,26 @@
|
||||
"""add_updated_at_column_to_room_playlists
|
||||
|
||||
Revision ID: 8d2af11343b9
|
||||
Revises: 7576ca1e056d
|
||||
Create Date: 2025-08-24 00:11:05.064099
|
||||
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "8d2af11343b9"
|
||||
down_revision: str | Sequence[str] | None = "7576ca1e056d"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
pass
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
pass
|
||||
Reference in New Issue
Block a user