События: общий рейтинг обновляется у всех, а не только в группе партии
Рейтинг с #80 один на всё приложение: завершённая или удалённая партия двигает топ, историю и профили всех, кто играл после неё, и страницы других групп. Событие match по-прежнему идёт группе партии, остальным активным игрокам — ratings без подробностей о партии. Фронт по обоим сбрасывает все рейтинговые витрины (invalidateRatingViews); participant_ids больше не нужен. #88 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
This commit is contained in:
@@ -315,16 +315,15 @@ def delete_match(
|
||||
session: Session = Depends(get_session),
|
||||
admin: User = Depends(get_current_admin),
|
||||
) -> s.OkResponse:
|
||||
group_id = match_service.get_match(session, match_id).group_id # для уведомления
|
||||
# До удаления: каскад унесёт участников вместе с партией.
|
||||
participant_ids = notify.match_participant_ids(session, match_id)
|
||||
match = match_service.get_match(session, match_id)
|
||||
group_id, finished = match.group_id, match.status == "finished" # для уведомления
|
||||
admin_service.delete_match(session, match_id)
|
||||
audit_service.record(
|
||||
session, actor_id=admin.id, action="delete", entity_type="match", entity_id=match_id,
|
||||
ip=client_ip(request),
|
||||
)
|
||||
session.commit()
|
||||
notify.match_removed(session, match_id, group_id, participant_ids)
|
||||
notify.match_removed(session, match_id, group_id, finished=finished)
|
||||
return s.OkResponse()
|
||||
|
||||
|
||||
|
||||
@@ -352,9 +352,7 @@ def delete_match(
|
||||
match_service.assert_can_modify(session, match, user)
|
||||
match_id_val = match.id
|
||||
group_id_val = match.group_id
|
||||
# Участников читаем до удаления: каскад унесёт их строки вместе с партией,
|
||||
# а событию они нужны, чтобы клиент знал, чьи витрины протухли.
|
||||
participant_ids = notify.match_participant_ids(session, match_id_val) # type: ignore[arg-type]
|
||||
finished = match.status == "finished" # после удаления статус уже не прочитать
|
||||
match_service.delete_match(session, match, expected_version=expected_version)
|
||||
audit_service.record(
|
||||
session,
|
||||
@@ -367,5 +365,5 @@ def delete_match(
|
||||
user_agent=request.headers.get("user-agent"),
|
||||
)
|
||||
session.commit()
|
||||
notify.match_removed(session, match_id_val, group_id_val, participant_ids) # type: ignore[arg-type]
|
||||
notify.match_removed(session, match_id_val, group_id_val, finished=finished) # type: ignore[arg-type]
|
||||
return s.OkResponse()
|
||||
|
||||
@@ -8,7 +8,7 @@ from __future__ import annotations
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.core.events import hub
|
||||
from app.models import GroupMember, Match, MatchParticipant, User
|
||||
from app.models import GroupMember, Match, User
|
||||
|
||||
|
||||
def _group_member_ids(session: Session, group_id: int) -> list[int]:
|
||||
@@ -17,30 +17,28 @@ def _group_member_ids(session: Session, group_id: int) -> list[int]:
|
||||
)
|
||||
|
||||
|
||||
def match_participant_ids(session: Session, match_id: int) -> list[int]:
|
||||
"""Кто играл в партии. Нужен в событии, чтобы клиент понимал, чьи витрины
|
||||
(история игр, публичный профиль, личная статистика) реально протухли."""
|
||||
return list(
|
||||
session.exec(
|
||||
select(MatchParticipant.user_id).where(MatchParticipant.match_id == match_id)
|
||||
def _ratings_changed(session: Session, notified: list[int]) -> None:
|
||||
"""Рейтинг общий и считается по всей истории (#80): завершённая партия двигает топ,
|
||||
главную, историю и профили всех, кто играл после неё, и страницы других групп.
|
||||
Игрокам вне группы (notified уже знают) — событие без подробностей о партии."""
|
||||
skip = set(notified)
|
||||
ids = [
|
||||
uid
|
||||
for uid in session.exec(
|
||||
select(User.id).where(User.role == "player", User.is_active.is_(True)) # type: ignore[union-attr]
|
||||
).all()
|
||||
)
|
||||
if uid not in skip
|
||||
]
|
||||
hub.publish(ids, {"type": "ratings"})
|
||||
|
||||
|
||||
def match_changed(session: Session, match: Match) -> None:
|
||||
"""Партия изменилась — уведомить всех участников её группы.
|
||||
|
||||
Адресат — вся группа: списки партий и статистика группы меняются у всех. А вот
|
||||
история и профили протухают только у игравших, поэтому их id едут в событии."""
|
||||
hub.publish(
|
||||
_group_member_ids(session, match.group_id),
|
||||
{
|
||||
"type": "match",
|
||||
"match_id": match.id,
|
||||
"group_id": match.group_id,
|
||||
"participant_ids": match_participant_ids(session, match.id), # type: ignore[arg-type]
|
||||
},
|
||||
)
|
||||
"""Партия изменилась — уведомить всех участников её группы, а если она завершена —
|
||||
и остальных игроков (_ratings_changed)."""
|
||||
members = _group_member_ids(session, match.group_id)
|
||||
hub.publish(members, {"type": "match", "match_id": match.id, "group_id": match.group_id})
|
||||
if match.status == "finished":
|
||||
_ratings_changed(session, members)
|
||||
|
||||
|
||||
def match_draft_changed(session: Session, match: Match, actor_id: int) -> None:
|
||||
@@ -53,22 +51,13 @@ def match_draft_changed(session: Session, match: Match, actor_id: int) -> None:
|
||||
hub.publish(ids, {"type": "match_draft", "match_id": match.id, "group_id": match.group_id})
|
||||
|
||||
|
||||
def match_removed(
|
||||
session: Session, match_id: int, group_id: int, participant_ids: list[int] | None = None
|
||||
) -> None:
|
||||
"""Партия удалена — уведомить участников группы (обновить списки).
|
||||
|
||||
participant_ids передаются снаружи: к этому моменту партии уже нет, а её участники
|
||||
ушли каскадом, и собрать их из базы невозможно."""
|
||||
hub.publish(
|
||||
_group_member_ids(session, group_id),
|
||||
{
|
||||
"type": "match",
|
||||
"match_id": match_id,
|
||||
"group_id": group_id,
|
||||
"participant_ids": participant_ids or [],
|
||||
},
|
||||
)
|
||||
def match_removed(session: Session, match_id: int, group_id: int, *, finished: bool) -> None:
|
||||
"""Партия удалена — уведомить участников группы (обновить списки), а если она была
|
||||
завершена — и остальных игроков. Статус передаётся снаружи: партии уже нет."""
|
||||
members = _group_member_ids(session, group_id)
|
||||
hub.publish(members, {"type": "match", "match_id": match_id, "group_id": group_id})
|
||||
if finished:
|
||||
_ratings_changed(session, members)
|
||||
|
||||
|
||||
def group_changed(session: Session, group_id: int, extra_user_ids: list[int] | None = None) -> None:
|
||||
|
||||
Reference in New Issue
Block a user