Files
iu 6e68242ec2 chore(openspec): архивировать album-live-sync и синхронизировать основные спеки
- realtime: личный поток несёт события жизненного цикла альбома и изменения
  счётчика фото; добавлены требования о жизненном цикле, получателях, форме
  payload'а, догоне и потере доступа
- albums: photos_count считает только фото вне корзины
2026-10-15 23:25:07 +03:00

9.3 KiB
Raw Permalink Blame History

Tasks

1. Аудитория альбома и доставка в личный поток

  • 1.1 Добавить в members.Repository метод, возвращающий id пользователей альбома (владелец + участники) без join с users; реализовать в sqlite- и postgres-сторах; проверить новым тестом репозитория: cd backend && go test ./internal/members/
  • 1.2 Добавить в albums.Service публикацию события альбома в личные каналы получателей: перечитать альбом через Store.GetByID и обогатить attachCover, разослать по аудитории через Hub.PublishUser; закрыть доступ к резолверу аудитории интерфейсом для members.Service; проверить тестом с подпиской Hub.SubscribeUser, что получатель видит событие с тем же набором полей, что элемент GET /api/albums
  • 1.3 Проверить, что публикация в личный поток безопасна при отсутствии подписчиков и не затрагивает пер-альбомный канал: тест на хаб, где событие уходит пользователю без подписки, не паникует и не создаёт подписку, а подписчик канала альбома событие не получает; cd backend && go test ./internal/sse/ ./internal/albums/

2. Создание, метаданные и удаление альбома

  • 2.1 Публиковать album.created владельцу при создании альбома; проверить тестом, что владелец получает событие, а объект перечитан и содержит photos_count и members_count
  • 2.2 Публиковать album.updated владельцу и участникам при изменении названия, описания и обложки, сохранив существующую публикацию в канал альбома; проверить тестом, что обе подписки получают событие и payload совпадает по полям с ответом GET /api/albums/:id
  • 2.3 Публиковать album.deleted с идентификатором владельцу и участникам, вычисляя аудиторию до удаления альбома (состав удаляется каскадом); проверить тестом, что событие получено владельцем и участником, а после удаления members.List для альбома пуст

3. Состав участников

  • 3.1 При принятии приглашения публиковать album.created приглашённому и album.updated владельцу и остальным участникам; проверить тестом, что приглашённый получает ровно одно событие с объектом альбома, а владелец — обновлённый members_count
  • 3.2 При смене роли участника публиковать album.updated владельцу и участникам; проверить тестом, что событие получает и пользователь, чью роль изменили
  • 3.3 При исключении участника публиковать album.updated оставшимся и album.deleted с идентификатором исключённому; проверить тестом, что исключённый получает ровно одно событие и в нём нет объекта альбома
  • 3.4 Обновить документацию контракта в project.md (§11 «Real-time (SSE)» и §12 «SSE — детали реализации»): состав событий личного потока, форма payload'а, догон списка альбомов при переподключении; проверить, что перечисленные типы событий совпадают с реализованными публикациями, а описанные поля — с ответом GET /api/albums

4. Web-клиент

  • 4.1 Подписаться на /api/sse/me один раз на уровне layout'а (app): album.created и album.updated применять через albums.upsert, album.deleted — через albums.remove, на onReconnect вызывать albums.load(); проверить cd frontend && npm run check и сценарием в двух вкладках: переименование и смена обложки отражаются без перезагрузки, порядок списка не меняется
  • 4.2 Обрабатывать потерю доступа на странице альбома: при событии удаления/исключения страница перестаёт отображать альбом и уходит на главную; проверить сценарием: владелец удаляет альбом, у участника с открытой страницей происходит переход на главную, а альбом исчезает из рейла
  • 4.3 Проверить жизненный цикл подписки: соединение создаётся один раз за сессию, не пересоздаётся при навигации между страницами и закрывается при выходе из аккаунта; проверить в DevTools (одно соединение /api/sse/me на сессию) и сценарием logout/login

5. Интеграционная проверка

  • 5.1 Сквозной сценарий на двух аккаунтах (два профиля браузера) на локальном сервере: создание альбома, приглашение, смена роли, исключение, удаление — список и открытая страница у второго пользователя обновляются без перезагрузки; проверить вручную и приложить результат (какие шаги и что наблюдалось)
  • 5.2 Проверить догон состояния: остановить сервер, изменить и удалить альбом, поднять сервер и убедиться, что после восстановления потока список на клиенте совпадает с GET /api/albums (удалённый альбом не возвращается); проверить сценарием и приложить вывод
  • 5.3 Прогнать на финальной ревизии make test, cd frontend && npm run check и cd frontend && npm run build; зафиксировать, что все три проходят без ошибок

6. Живое обновление состава фото

  • 6.1 В albums.Service добавить рассылку album.updated получателям личного потока без публикации в канал альбома (перечитать через GetByID + attachCover); закрыть метод интерфейсом для photos.Service; проверить тестом, что после вызова получатель видит событие, а подписчик канала альбома — нет
  • 6.2 Публиковать photo.created при загрузке фото и album.updated получателям при загрузке, копировании, перемещении, восстановлении, переводе в корзину и окончательном удалении; проверить тестом, что photos_count меняется у владельца и участника, а подписчик канала альбома получает photo.created
  • 6.3 Считать photos_count только по фото вне корзины: добавить deleted_at IS NULL в подзапросы счётчика в sqlite- и postgres-сторах; проверить тестом, что перевод в корзину уменьшает, а восстановление увеличивает счётчик
  • 6.4 Обновить project.md (§11/§12): состав фото в личном потоке, photo.created при загрузке, семантика photos_count
  • 6.5 Прогнать make test, cd frontend && npm test, cd frontend && npm run check и cd frontend && npm run build

Workflow follow-up

  • Заархивировать change после прохождения ревью проекта (openspec archive album-live-sync).
  • Проверить, что после архивации дельта вошла в openspec/specs/realtime/spec.md, а openspec validate --strict album-live-sync не выдаёт ошибок.