Files
hockey_new/docs/ARCHITECTURE.md

100 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Архитектура
## Модули
| Каталог | Назначение |
| --- | --- |
| `hockey_data/` | Stat2TV, PostgreSQL, матч, статистика, роли, mapping и Agent bridge |
| `ui_builder/` | Действующий runtime/редактор UI и активные статические ресурсы |
| `broadcast_settings/` | Переносимые настройки команд и vMix JSON |
| `khl_site/` | Карточки КХЛ, карьера, обновления и локальные данные |
| `agent/` | Windows Agent и исходники Go |
| `settings/local/` | Активная конфигурация UI, таймеров, обновлений и восстановление |
| `tests/` | Регрессии серверной логики и браузерных функций |
| `logs/` | Закрытый постоянный журнал программы в месячных SQLite-файлах |
## Поток данных
Stat2TV XML/JSON разбираются сервисом и сохраняются в нормализованных таблицах
PostgreSQL: турниры, команды, игроки, составы, события и статистика.
Карточки/карьера КХЛ имеют отдельный локальный импорт и синхронизацию.
Рабочее состояние матча и права доступа относятся к матчу; персональный канал,
назначение устройства и mapping определяют получателя данных vMix.
SQL Mapping получает контекст матча/выбора, формирует SetText/SetImage и передаёт
их нужному Agent. Отправка команды и её подтверждение — разные этапы.
Команды действий (например, Toggle/Cut) нельзя повторять без проверки результата.
Восстановление связи должно заново синхронизировать данные, не воспроизводя эфирные действия.
Mapping имеет раздельное ожидаемое и подтверждённое состояние. На Agent ожидается
ACK не более одного пакета Mapping. Agent с явной capability `mapping_title_batch_v1`
получает команды, сгруппированные по Input, до 500 команд и 48 КиБ; прежние версии
получают до шести независимых установок. Новый Agent сохраняет порядок записей
одной цели, выполняет независимые цели ограниченным пулом и выделяет отдельный
обработчик для интерактивных команд. WebSocket reader продолжает читать назначения,
heartbeat и управление во время массовой отправки. Очереди и время обработки ограничены.
Ошибки отдельных полей не прерывают первый проход по остальным; безопасные повторы
выполняются после него. Частичный результат отличается от полной обработки. SQL-каталог ограничен
источниками действующих записей и правил, включая зависимости настроенного счёта.
Поколение соединения/назначения отбрасывает устаревшие подтверждения. Новый WebSocket
ждёт актуальную inventory перед восстановлением. После committed передачи управления
обновляется поколение доставки устройств матча.
Шаблон Mapping и опубликованный снимок устройства разделены. Снимок использует
существующие таблицы профилей/полей и хранится как технический профиль, связанный
с устройством. Сериализация устройства внутри транзакции передаёт её же session
в публикацию Mapping, без вложенного подключения с конкурирующей блокировкой.
Сохранение шаблона атомарно проверяет ожидаемую версию. Общие SQL
источники остаются общими; параметры контекста разрешаются для конкретного матча.
Эксплуатационный журнал независим от доступности PostgreSQL. Он получает
структурированные события приложения, стандартный Python logging, Uvicorn,
HTTP-аудит и браузерные замеры. Неблокирующая очередь ограничена, запись пакетная;
чтение использует короткие подключения и курсорную пагинацию. Секреты маскируются
до очереди записи. Запись startup/shutdown закрывается штатно и через atexit.
Сводка готовности к эфиру читает только назначение и опубликованный Mapping
выбранного устройства, доступного текущему аккаунту. Она сопоставляет цели с
inventory текущего WebSocket и использует подтверждения обработки Mapping
текущего поколения. Просмотр не исполняет SQL источников, не создаёт публикации
и не отправляет команды Agent. Эти подтверждения живут в памяти процесса;
после смены контекста или разрыва связи прежний успех не доказывает готовность.
Stat2TV использует общий ограниченный HTTP-пул, а не новое соединение на каждый
запрос. Авторизация и timeout передаются отдельно для каждого запроса;
при завершении сервера HTTP-клиенты закрываются после фоновых задач.
Состояние WebSocket/Agent и часть кэшей находятся в памяти одного процесса.
Поэтому запуск нескольких workers без общего хранилища назначения/очередей
не является способом масштабирования этой версии.
## Пользовательские данные и артефакты
UI хранится атомарно в `settings/local/` либо внешнем `HOCKEY_UI_DATA_DIR`.
Одинаковые сохранения не создают копий. Миграция прежней истории backup выполняется
однократно; исходные root-настройки сохраняются как bootstrap.
`hockey_data/snapshots/` — диагностические XML, а не база рабочего состояния.
Новые имена включают полный SHA-256 содержимого и тип ресурса. Повторные данные
используют прежний путь без перезаписи. История синхронизаций в БД может ссылаться
на эти файлы, поэтому возрастная очистка XML запрещена без сверки ссылок БД.
Автоматический retention относится только к самостоятельным JSON-отчётам discovery.
Флаги и их LICENSE, шаблоны vMix JSON, исходники Go/Agent.exe, SQL-примеры и
проверки входят в проект по назначению. Совпадение имён или содержимого файлов
default и локальной конфигурации не делает их взаимозаменяемыми.
Ответы браузерных запросов матча привязаны к поколению выбора и сессии. Загрузка
общих данных провайдера сохраняет рабочий контекст выбранного матча. Эффективный
SQL-счёт берётся из текущего Game либо проверенного результата настроенного ключа
для каждой стороны; ручные значения хранятся отдельно.
В BUILD197 браузер связывает загрузку матча с поколением, аккаунтом, сессией,
языком и выбранным устройством. Mapping ждёт полных составов обеих команд.
Изменение упорядоченных данных игроков запускает полный apply выбранного Agent
со свежим разрешением SQL; одинаковые снимки объединяются. Признаки счёта,
времени, статистики и timestamps не входят в отпечаток состава. Ответ старой
отправки не закрывает окно нового матча. Полный apply сохраняет существующую
защиту native Countdown: поля таймеров исключаются до разрешения зависимостей.