- realtime: личный поток несёт события жизненного цикла альбома и изменения счётчика фото; добавлены требования о жизненном цикле, получателях, форме payload'а, догоне и потере доступа - albums: photos_count считает только фото вне корзины
9.3 KiB
9.3 KiB
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не выдаёт ошибок.