Событие о партии рассылается всей группе, и клиент по нему инвалидировал историю игр, публичные профили и личную статистику получателя. В группе из шести человек любая партия двоих заставляла остальные четыре вкладки перезапрашивать свою историю и открытый профиль, хотя у них ничего не изменилось. Теперь событие несёт participant_ids. Общие витрины (карточка партии, списки и статистика группы, топ и главная) обновляются у всех — рейтинг глобальный, чужая партия действительно двигает топ. История, публичный профиль и личная статистика обновляются только у тех, кто играл, и у зрителей их профилей. При удалении партии участники собираются ДО удаления: каскад уносит их строки вместе с партией, и собранный после список всегда был бы пустым. Это же поведение закреплено тестом. Событие без participant_ids (вкладка открыта до обновления сервера) обрабатывается по-старому, широко: обновление бэкенда не ломает уже открытые страницы. #33 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BoiJK9ux8peeyjLb8TYjFf
90 lines
4.3 KiB
Python
90 lines
4.3 KiB
Python
"""Публикация SSE-событий «данные изменились» нужным пользователям.
|
|
|
|
Тонкая прослойка над core.events.hub — держит роутеры чистыми. Событие несёт лишь тип и id,
|
|
клиент по нему точечно перезапрашивает данные.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from app.core.events import hub
|
|
from app.models import GroupMember, Match, MatchParticipant
|
|
|
|
|
|
def _group_member_ids(session: Session, group_id: int) -> list[int]:
|
|
return list(
|
|
session.exec(select(GroupMember.user_id).where(GroupMember.group_id == group_id)).all()
|
|
)
|
|
|
|
|
|
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)
|
|
).all()
|
|
)
|
|
|
|
|
|
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]
|
|
},
|
|
)
|
|
|
|
|
|
def match_draft_changed(session: Session, match: Match, actor_id: int) -> None:
|
|
"""Черновик формы завершения изменился — остальным заполняющим из группы.
|
|
|
|
Отдельный тип события: черновик меняется на каждое движение тайла, и гнать по нему
|
|
полную инвалидацию (лидерборд, история, профили) было бы расточительно. Автору
|
|
правки событие не шлём — у него уже актуальное состояние."""
|
|
ids = [uid for uid in _group_member_ids(session, match.group_id) if uid != actor_id]
|
|
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 group_changed(session: Session, group_id: int, extra_user_ids: list[int] | None = None) -> None:
|
|
"""Состав/данные группы изменились — участникам (+ доп. адресатам, напр. удалённому)."""
|
|
ids = _group_member_ids(session, group_id)
|
|
if extra_user_ids:
|
|
ids = ids + extra_user_ids
|
|
hub.publish(ids, {"type": "group", "group_id": group_id})
|
|
|
|
|
|
def invitations_changed(user_id: int) -> None:
|
|
"""У пользователя изменился список приглашений."""
|
|
hub.publish([user_id], {"type": "invitations"})
|
|
|
|
|
|
def notifications_changed(user_id: int) -> None:
|
|
"""У пользователя появилось/изменилось уведомление — пусть подтянет список."""
|
|
hub.publish([user_id], {"type": "notifications"})
|