generated from VLADIMIR/template
up
This commit is contained in:
+104
-4
@@ -145,8 +145,8 @@ MIME и SHA-256, некорректные связи данных и состо
|
||||
сообщения API.
|
||||
|
||||
SHA-256 защищает от повреждения, но не подтверждает автора пакета. Подпись и
|
||||
шифрование не реализованы. Кодек не блокирует игру и не делает импорт; повторные
|
||||
импорты, сохранность QR на площадке и полный перенос проверяются на этапе 6.
|
||||
шифрование не реализованы. Кодек не блокирует игру и не делает импорт. Импортёр этапа 6 использует его
|
||||
проверенный результат; повторные импорты и сохранность QR описаны ниже.
|
||||
|
||||
Проверка: `go test ./internal/services/game_package_service -count=1` и полный
|
||||
`go test ./...`. Тесты покрывают сохранение данных, автономное чтение, загрузку
|
||||
@@ -154,8 +154,8 @@ SHA-256 защищает от повреждения, но не подтверж
|
||||
|
||||
## Экспорт со статусом офлайн-режима: этап 5
|
||||
|
||||
Только для тестовых игр до реализации возврата результатов. Импорт, интерфейс
|
||||
передачи, отмена и возврат результатов реализуются следующими этапами.
|
||||
Только для тестовых игр до реализации возврата результатов. Импорт реализован
|
||||
на этапе 6; интерфейс передачи, отмена и возврат результатов остаются следующими этапами.
|
||||
|
||||
### API
|
||||
|
||||
@@ -246,3 +246,103 @@ maintenance очищает оставшиеся каталоги термина
|
||||
Необязательный `TEST_S3_RESTART_MARKER` позволяет внешнему тестовому harness
|
||||
перезапустить изолированный RustFS: после появления marker нужно дождаться health
|
||||
и создать `<marker>.continue` в течение трёх минут. Это не production-проверка.
|
||||
|
||||
|
||||
## Локальный импорт: этап 6
|
||||
|
||||
### API
|
||||
|
||||
`POST /api/games/import` принимает ZIP непосредственно в теле запроса с
|
||||
`Content-Type: application/zip` и `Authorization: Bearer <JWT>`. Multipart и
|
||||
Base64 не используются. Разрешены роли `admin`, `developer`, `organizer`;
|
||||
операция доступна только в `APP_MODE=venue`.
|
||||
|
||||
Ответ HTTP 200: `{"gameID":100,"packageID":"…","repeated":false}`.
|
||||
Повтор того же пакета возвращает `repeated:true` и прежние идентификаторы,
|
||||
не перезаписывая локальные изменения. Повтор после удаления игры также
|
||||
возвращает прежний результат и не восстанавливает удалённую игру.
|
||||
|
||||
Ошибки: 401/403 — авторизация/роль; 400 — неподходящий режим, повреждённый
|
||||
пакет, версия или данные; 409 — конфликт ID, изменённые байты под прежним ID
|
||||
пакета, другой пакет той же игры или ещё выполняющийся импорт; 413 — лимит
|
||||
размера; 499/504 — отмена/timeout; 500 — безопасная внутренняя ошибка.
|
||||
Ответы имеют `Cache-Control: no-store`.
|
||||
|
||||
Маршрут описан в protobuf как `ImportGame(HttpBody) → GameImportRsp`, но
|
||||
HTTP-поток обслуживает отдельный handler перед grpc-gateway: ZIP размером до
|
||||
1 ГиБ не проходит через gRPC-буфер. Прямой gRPC-вызов использует тот же сервис,
|
||||
однако ограничен существующим лимитом сообщений. В обоих конфигурациях прокси
|
||||
venue только `/api/games/import` получает лимит 1 ГиБ, отключённую буферизацию
|
||||
request body и timeout 30 минут; другие маршруты сохраняют прежние лимиты.
|
||||
|
||||
### Перенос и конфликты
|
||||
|
||||
Импорт сохраняет ID игры, команд и билетов, пароли QR, хеши билетов, их
|
||||
статусы/даты, подтверждения участия, email капитанов и ID доставки регистрации.
|
||||
Занятый локальный ID игры/команды/билета или хеш билета приводят к отказу,
|
||||
не затрагивая существующие записи. Конфликты проверяются до загрузки медиа и
|
||||
повторно в транзакции публикации.
|
||||
|
||||
Сценарий, оба плейлиста и треки создаются отдельными копиями с новыми локальными
|
||||
ID. Все назначения перепривязываются; сценарий принадлежит импортирующему
|
||||
пользователю. `creator_id` команд и `used_by` билетов остаются NULL: совпадение
|
||||
числового ID не связывает аккаунты двух серверов. Исходные связи и соответствия
|
||||
ID сохраняются в закрытых метаданных `game_imports` для будущего возврата.
|
||||
|
||||
Формат и статус игры сохраняются. План экранов и карта переносятся без подключённых
|
||||
устройств; воспроизведение музыки начинает со штатного состояния покоя.
|
||||
Медиа загружаются в локальный RustFS потоково под отдельными именами попытки,
|
||||
с заменой всех ссылок. Одинаковое медиа сценария используется повторно; треки
|
||||
с одинаковыми байтами получают разные имена, если этого требует существующее
|
||||
ограничение уникальности `playlist_tracks.filename`.
|
||||
|
||||
### Атомарность и восстановление
|
||||
|
||||
ZIP и распаковка находятся в приватном временном каталоге с правами 0700/0600.
|
||||
Вся обработка ограничена 30 минутами. До загрузки файлов в БД фиксируются
|
||||
ID попытки, исходный сервер, ID пакета, SHA-256 всего ZIP, оригинальный снимок,
|
||||
манифест и ключи будущих объектов. Частичные уникальные индексы разрешают одну
|
||||
активную попытку на ID пакета и исходный ID игры, включая завершённые импорты.
|
||||
|
||||
После загрузки всех файлов одна транзакция создаёт сценарий, плейлисты,
|
||||
треки, игру, команды, билеты, план, карту и состояние `ready` со связями ID.
|
||||
На время публикации вставки/изменения games/teams/tickets блокируются для
|
||||
повторной проверки конфликтов и безопасного продвижения последовательностей.
|
||||
Последовательности могут оставить пропуски после rollback; исходные ID
|
||||
повторно не выдаются. При ошибке ни одна часть новой игры не видна.
|
||||
|
||||
Heartbeat каждые 30 секунд продлевает аренду на 2 минуты; истёкшая попытка
|
||||
не может публиковать данные. Recovery проверяет БД каждые 5 секунд, переводит
|
||||
прерванные попытки в `failed` и повторяет очистку их объектов, в том числе после
|
||||
перезапуска и позднего завершения старой загрузки. Неоднозначный результат
|
||||
commit не разрешает удаление: очистка допустима только для подтверждённого
|
||||
`failed`. Ключи успешного импорта никогда не используются для этой очистки.
|
||||
Временные каталоги прерванных запросов удаляются после 32 минут и проверки
|
||||
отсутствия активной попытки. Ошибка допускает новую попытку с отдельными ключами.
|
||||
|
||||
### Проверка
|
||||
|
||||
`TEST_DATABASE_URL` включает проверки с реальными миграциями в отдельных схемах:
|
||||
|
||||
```sh
|
||||
go test ./internal/repos/game_imports_repo ./internal/services/game_import_service ./internal/app -count=1
|
||||
go test -race ./internal/repos/game_imports_repo ./internal/services/game_import_service -count=1
|
||||
```
|
||||
|
||||
Проверяются сохранность данных/QR/билетов, локальные ID копий и исходные связи,
|
||||
последовательности, повтор с локальными изменениями, изменённый ZIP под прежним
|
||||
ID, конфликты до/после загрузки, конкурентный импорт, rollback всех записей,
|
||||
ошибки загрузки/очистки, отмена, аренда/timeout и неопределённый ответ commit.
|
||||
HTTP-тесты проверяют роли, ошибки, MIME, лимиты и отсутствие внутренних деталей.
|
||||
|
||||
Для изолированного RustFS дополнительно задать `TEST_S3_ENDPOINT`, `TEST_S3_USER`,
|
||||
`TEST_S3_PASSWORD` и запустить `go test ./internal/services/game_import_service
|
||||
-run TestImportRustFSHTTPAndRestart -count=1 -v`. Smoke проверяет настоящий HTTP
|
||||
handler, файлы в отдельном bucket и повтор импорта. Необязательный
|
||||
`TEST_S3_RESTART_MARKER` позволяет внешнему harness перезапустить только тестовый
|
||||
RustFS и создать `<marker>.continue` в течение минуты. Bucket, файлы и тестовая
|
||||
схема удаляются после проверки. Проверка без WAN проводится в отдельной Docker-сети
|
||||
с `internal: true`; рабочая площадка и production для этого не изменяются.
|
||||
|
||||
Интерфейс передачи остаётся этапом 7; полный билетный и игровой прогон — этапом 8.
|
||||
До готовности возврата результатов используются только тестовые игры.
|
||||
|
||||
Reference in New Issue
Block a user