# ВЕКТОР МЧС — Система определения приоритетных направлений поиска **Программное средство поддержки принятия решений при поисково-спасательных работах (ПСР) на открытой местности МЧС Республики Беларусь.** ВЕКТОР рассчитывает приоритетные направления поиска пропавшего человека по матмодели: на основании данных о потерявшемся (возраст, диагнозы, физподготовка, транспорт), обстоятельствах потери (время, причина, направление), среде (рельеф, погода, сезон) и точке последней пропажи (ТНП) система строит концентрическую зону вероятного нахождения и подсвечивает приоритетные направления обследования с учётом рельефа из OSM-данных. > **Версия документа:** соответствует состоянию репозитория на 9 сентября 2026 г. > **Репозиторий:** [`Sadmin/vector`](http://192.168.0.106:3000/Sadmin/vector) > **Развёртывание:** см. [SETUP_GUIDE.md](SETUP_GUIDE.md) — пошаговый гайд по развёртыванию на серверах МЧС. --- ## Оглавление 1. [Назначение и контекст](#1-назначение-и-контекст) 2. [Ключевая политика: полностью закрытый контур](#2-ключевая-политика-полностью-закрытый-контур) 3. [Архитектура](#3-архитектура) 4. [Стек технологий](#4-стек-технологий) 5. [Структура репозитория](#5-структура-репозитория) 6. [База данных](#6-база-данных) 7. [Математическая модель расчёта](#7-математическая-модель-расчёта) 8. [OSM-геослой (PostGIS)](#7-osm-геослой-postgis) 9. [Бэкенд (FastAPI) — структура и API](#8-бэкенд-fastapi--структура-и-api) 10. [Фронтенд (React)](#9-фронтенд-react) 11. [Аутентификация, роли и подразделения (RBAC)](#10-аутентификация-роли-и-подразделения-rbac) 12. [Мультипоиск и операции](#11-мультипоиск-и-операции) 13. [Интеграция с КОНТУР (отправка зон)](#12-интеграция-с-контуром-отправка-зон) 14. [Аудит](#13-аудит) 15. [Тестирование](#14-тестирование) 16. [Справочники](#14-справочники) 17. [Что готово и что в планах](#15-что-готово-и-что-в-планах) 18. [Контакты и сопровождение](#16-контакты-и-сопровождение) --- ## 1. Назначение и контекст ### Что делает ВЕКТОР ВЕКТОР — рабочий инструмент РЦУ (расчётно-центрального управления) МЧС Республики Беларусь при организации поиска пропавших людей: - **Карточка поиска** — структурированный ввод данных: субъект (возраст, пол, диагнозы — РАС, эпилепсия, СДВГ, ЗПР, психотип), обстоятельства (причина потери, время, направление движения, надёжность свидетельства), среда (сезон, погода, температура, рельеф), ресурсы (группы, кинолог, транспорт) - **Матрасчёт** — детерминированный расчёт: максимальный радиус по методике ПСР (КМЧ × коэффициенты), приоритетные направления (сектора 45° с весами), прогноз поведения по психотипу, срочность и критические действия - **Карта зон** — Leaflet: концентрические круги возможной зоны (0.5R / 0.75R / R) + подсветка приоритетных направлений; слои воды/болот полигонами и ж/д линий из локальной OSM - **Рекомендации** — тактические рекомендации по профилям случая (проверить водоёмы, обследовать вдоль дорог и т.д.) - **Мультипоиск** — несколько одновременных поисков, каждое ОУМЧС/Г(Р)ОЧС работает со своими операциями - **Интеграция с КОНТУР** — отправка зон в полевую систему координации (ATAK + Meshtastic) - **Архив завершённых поисков** — факт vs прогноз (дистанция, направление, место находки) для калибровки зонального скоринга - **Администрирование** — пользователи по иерархии МЧС, журнал аудита, статистика ### Методическая основа Расчёт радиуса основан на статистических таблицах ПСР (поиск и спасение, экстремум-методика): базовая скорость смещения по среднему лесу (НормС) умножается на коэффициенты возраста, диагноза, рельефа, погоды, времени суток, транспорта. Скоринг зон — взвешенная сумма факторов рельефа (лес, вода, дороги, НП) по секторам вокруг ТНП. ### Домен МЧС осуществляет поиски **только в природных экосистемах** — не в городах. Город учтён в коэффициентах рельефа, но приоритетные зоны строятся для природной среды. --- ## 2. Ключевая политика: полностью закрытый контур **Все данные о поисках (включая ПДн детей) не покидают контур организации.** - **ИИ/LLM удалён из проекта полностью** (решение руководства): анализ детерминированный, `backend/services/claude_service.py` и любые облачные API исключены. В `docker-compose` и `.env` НЕТ никаких API-ключей внешних сервисов - **Расчёт локальный**: вся математика в `services/*.py` (Python), выполняется на сервере за миллисекунды - **Карты — из локальной OSM**: дамп Geofabrik (беларусь-latest.osm.pbf, ~333 МБ) импортирован в PostGIS внутри организации; бэкенд не ходит в Overpass/интернет (fallback на Overpass остаётся только на случай пустых OSM-таблиц) - **Фронтенд** тянет OSM-тайлы и иконки Leaflet с CDN — для полностью offline-контура требуется локальный tile-сервер (в планах) Тест-страж: `backend/tests/test_rules_analysis.py::TestNoLLM` — падает, если в код возвращается внешний ИИ. --- ## 3. Архитектура ``` ┌───────────────────────────────────────────────────┐ │ Браузер пользователя │ │ (Chrome / Firefox / Edge — любая ОС) │ └──────────────────────┬────────────────────────────┘ │ HTTP ┌──────────────────────▼────────────────────────────┐ │ vector-frontend (React 18, CRA, порт 3000) │ │ Форма карточки · карта Leaflet · дашборд · админка│ └──────────────────────┬────────────────────────────┘ │ REST JSON :8000 ┌──────────────────────▼────────────────────────────┐ │ vector-backend (FastAPI, порт 8000) │ │ ┌──────────────────────────────────────────────┐ │ │ │ Роутеры: auth, cases, analyze, operations, │ │ │ │ admin, admin_users, closed_cases, contour, │ │ │ │ water, geocode, stats, health │ │ │ └──────────────────────────────────────────────┘ │ │ ┌──────────────────────────────────────────────┐ │ │ │ Services (математика, без БД/HTTP): │ │ │ │ search_engine (модель), distance_service │ │ │ │ (радиус), scoring_service (сектора), │ │ │ │ rules_analysis (правила), psychotype, │ │ │ │ geo_service → osm_local (PostGIS) │ │ │ └──────────────────────────────────────────────┘ │ └──────────────────────┬────────────────────────────┘ │ SQLAlchemy / psycopg2 ┌──────────────────────▼────────────────────────────┐ │ vector-postgres (PostgreSQL 16 + PostGIS 3.6) │ │ БД vector_mchs: кейсы, операции, юниты, аудит, │ │ OSM (planet_osm_*: 1 млн дорог, 192 тыс. лесов) │ └───────────────────────────────────────────────────┘ │ (опционально, outbound) ┌──────────────────────▼────────────────────────────┐ │ КОНТУР (CT130, полевая координация ATAK) │ │ POST /api/v1/vector/zones — приоритетные зоны │ └───────────────────────────────────────────────────┘ ``` ### Принципы архитектуры - **Чистая математика**: `services/*.py` не знают ни о БД, ни об HTTP — только входные словари на выходе модели. Тонкие роутеры-обёртки в `backend/routers/` - **Контракт /analyze**: `zone.forest_pct` — доля 0..1 (не проценты!), дистанции — км (в PostGIS SRID 3857 раздувание мерка: real = mercator × cos(lat)) - **Анализ воспроизводим**: тот же вход — тот же выход; прогон модели сохраняется в `analysis_log` кейса и сравнивается с фактом в архиве --- ## 4. Стек технологий | Слой | Технология | Версия | Назначение | |------|-----------|--------|-----------| | Бэкенд | FastAPI | 0.115 | REST API | | ORM | SQLAlchemy 2.0 + psycopg2 | | БД + миграции Alembic 1.14 | | БД | PostgreSQL 16 + PostGIS 3.6 | | Данные + гео (PostGIS) | | OSM | osm2pgsql 2.1 | | Импорт дамп OSM → planet_osm_* | | Auth | python-jose + passlib | | JWT + bcrypt, server-side сессии | | Фронт | React 18 (CRA) + react-leaflet | | SPA | | Карта | Leaflet 1.9 + OSM-тайлы | | Отображение зон | | Контейнеры | Docker Compose | | 4 сервиса | Полный список зависимостей — `backend/requirements.txt` и `frontend/package.json`. --- ## 5. Структура репозитория ``` vector/ ├── backend/ # FastAPI-приложение │ ├── main.py # Сборка приложения (12 роутеров) │ ├── models.py # 21 таблица (SQLAlchemy) │ ├── schemas.py # Pydantic-схемы │ ├── database.py # Репозиторий кейсов + статистика │ ├── audit.py # Журнал аудита + гео-скоуп │ ├── rbac.py # Роли/права/политика безопасности │ ├── alembic/ # Миграции (006…009) │ ├── seed_users.py # Сид первых пользователей │ ├── seed_units.py # Сид подразделений (РЦУ РЧС → ОУМЧС → Г(Р)ОЧС, 142 юнита) │ ├── routers/ # 12 модулей API (см. раздел 8) │ ├── services/ # Адаптеры расчёта │ └── tests/ # 231 тест (pytest) ├── services/ # Математика (чистые функции) │ ├── search_engine.py # SearchInput/SearchModel — ядро расчёта │ ├── distance_service.py # Максимальный радиус (КМЧ × коэффициенты) │ ├── scoring_service.py # Взвешенный скоринг секторов │ ├── rules_analysis.py # Правила: срочность, действия, поведение │ ├── psychotype_service.py # Психотипирование (опрос) │ ├── geo_service.py # Геоданные секторов (лес/вода/дороги/НП) │ ├── osm_local.py # Локальный PostGIS-провайдер OSM │ └── recommendation_service.py ├── frontend/ # React SPA │ └── src/ │ ├── App.js # Роутинг (/dashboard, /desktop, /users, /admin) │ ├── constants.js # Единые справочники (место находки, кто нашёл, румбы) │ ├── api/client.js # apiFetch/apiJson — Bearer + 401-перехват │ ├── components/ # CaseForm (5 шагов), SearchMap, ProtectedRoute │ └── pages/ # Dashboard, DesktopSARPage, analysis/, AdminDashboard, │ # UsersAdmin, ClosedCases, Login ├── scripts/ # Развёртывание и живые проверки │ ├── b16-import.sh # Импорт OSM-дампа в PostGIS │ ├── e2e-*.sh # e2e-сценарии │ ├── eN-live-check.sh # Живые проверки этапов E1–E4 │ └── prod-stamp-backfill.sql ├── docker-compose.yml # 4 сервиса: postgres, adminer, backend, frontend ├── .env.example # Шаблон окружения (без внешних ключей) ├── SETUP_GUIDE.md # Развёртывание на серверах МЧС ├── vector_tasks.md # Живой план работ (B1…B21+) └── README.md # Этот документ ``` --- ## 6. База данных **PostgreSQL 16 + PostGIS 3.6**, БД `vector_mchs`. Схема управления — Alembic (`backend/alembic/versions/`), актуальная ревизия **009_e3_operations**. ### Основные таблицы | Таблица | Назначение | |---------|-----------| | `cases` | Карточки поисков: субъект, обстоятельства, среда, ТНП, исход | | `analysis_log` | JSONB-лог прогонов анализа (аудит расчётов) | | `search_models` | Слой 4: сохранённые матмодели по кейсам (GeoJSON-зоны) | | `search_teams`, `field_observations`, `areas_checked`, `found_events` | Оперативные слои (состав групп, наблюдения, проверенные квадраты, находки) | | `reference_priors` | Слой 1: справочные приоритеты | | `users`, `mchs_units` | Пользователи; иерархия РЦУ РЧС → 6 ОУМЧС → 135 Г(Р)ОЧС | | `roles`, `permissions`, `role_permissions`, `user_roles` | RBAC: 4 роли, 9 прав, матрица связей | | `sessions`, `auth_events` | Server-side сессии (sha256-токены), журнал входов | | `security_settings` | Политика безопасности (10 параметров) | | `search_operations` | Поисковые операции (статусная машина, привязка к кейсу и КОНТУРу) | | `audit_events`, `audit_changes` | Неизменяемый журнал аудита, field-level изменения (ретенция 90 дней) | | `planet_osm_*` | Импорт OSM Беларуси (osm2pgsql, SRID 4326/3857): дороги, леса, вода, НП | ### Политика безопасности (`security_settings`, меняется в админке) | Параметр | Значение | |----------|----------| | Минимальная длина пароля | 8 | | Сложность пароля | требуются буквы+цифры | | Срок действия пароля | 90 дней | | История паролей | 5 (нельзя переиспользовать) | | Блокировка после неудачных входов | 5 (на 15 минут) | | Тайм-аут сессии / неактивности | 24 ч / 60 мин | | Макс. параллельных сессий | 3 (старые отзываются) | | Ретенция журнала аудита | 90 дней | --- ## 7. Математическая модель расчёта Ядро — `services/search_engine.py`: `SearchInput` → `SearchModel` (чистая функция, без БД/HTTP). ### Состав расчёта 1. **Максимальный радиус** (`distance_service.py`): НормС (скорость смещения по среднему лесу, км/ч) × коэффициенты возраста / диагноза / рельефа / погоды / времени суток / транспорта, с учётом усталости. Контрольные точки: подросток 14 л, 3 ч, лес = 5.1 км; велосипедист 8 л, 2 ч = 10.8 км 2. **Коэффициенты рельефа** (`terrain_map`): лес/поле/луг/город/неизвестно = 1.0; густой лес = 0.5; болото = 0.4; горы/овраги = 0.6; дорога/тропа = 0.8 3. **Сектора направлений** (`scoring_service.py`): 8 секторов по 45° вокруг ТНП; каждый сектор оценивается по составу рельефа из локальной OSM (лес — доля 0..1, вода/НП — расстояние в км), приоритетные подсвечиваются на карте 4. **Психотип** (`psychotype_service.py`): опрос из формы → доминантный / гармоничный / тревожный / интроверт → модификаторы поведения и рекомендаций 5. **Правила** (`rules_analysis.py`): срочность (critical/high/elevated/normal), критические действия, прогноз поведения по профилям (не_умеет_плавать ×3.0 к водоёмам и т.п.), тактические рекомендации («проверить водоёмы», не «перекрыть») 6. **Профили, не покрытые моделью** (ДЦП, слабое зрение/слух) — честно помечаются `unmodeled_profiles`, не искажают расчёт **Коэффициенты меняются только по явному решению руководства** — молча таблицы не трогать. --- ## 7. OSM-геослой (PostGIS) - Импорт: дамп [Geofabrik](https://download.geofabrik.de/europe/belarus-latest.osm.pbf) (~333 МБ) → `scripts/b16-import.sh` (osm2pgsql --slim, ~4,5 мин, RAM 2 ГБ) - Содержимое: 1 010 507 дорог, 192 374 леса, 81 772 воды, 2 731 НП - Провайдер `services/osm_local.py` читает `planet_osm_*`; `geo_service.py` переключается на Overpass только если таблицы пусты - Полный `/analyze` на локальных данных: ~10 мс (см. `scripts/e2e-b16-timing.sh`) - Обновление дампа — по решению (раз в квартал / заморозить) --- ## 8. Бэкенд (FastAPI) — структура и API `backend/main.py` собирает 12 роутеров. Авторизация — Bearer JWT (form-urlencoded логин, OAuth2PasswordRequestForm). | Префикс | Модуль | Назначение | |---------|--------|-----------| | `/api/v1/health` | health.py | Liveness | | `/api/v1/auth` | auth.py | Логин/логаут/me/смена пароля, блокировки | | `/api/v1/cases` | cases.py | CRUD карточек поисков | | `/api/v1/analyze` | analyze.py | Матрасчёт зон (по payload или case_id) | | `/api/v1/operations` | operations.py | Операции мультипоиска (CRUD, статусная машина, /summary), завершение поиска | | `/api/v1/operations/{id}/send-to-contour` | contour.py | Отправка зон в КОНТУР; GET areas-checked (прокси) | | `/api/v1/admin` | admin.py | Список/редактирование кейсов, дашборд-метрики | | `/api/v1/admin/users` | admin_users.py | Пользователи (создание/роли/юниты/блокировка/сброс пароля) + журнал аудита | | `/api/v1/closed-cases` | closed_cases.py | Архив завершённых поисков (ручной ввод + прогон модели) | | `/api/v1/water/{case_id}` | water.py | Вода/болота (полигоны), ж/д (линии) вокруг ТНП | | `/api/v1/geocode` | geocode.py | Прокси Nominatim (формат «пункт, район, адрес») | | `/api/v1/stats` | stats.py | Тепловая карта, агрегаты | Swagger: `http://<сервер>:8000/docs`. **Синхронизация закрытия**: кейс закрывается через ЛЮБОЙ из путей (`PATCH /admin/cases` или `POST /operations/{id}/complete`) — активная операция кейса автоматически завершается (`completed` + `closed_at`). --- ## 9. Фронтенд (React) SPA на react-scripts (CRA). Роутинг: | Путь | Страница | |------|----------| | `/dashboard` | Главная: «Активные поиски» (счётчики, карточки операций, «+ Новый поиск», «Пользователи») | | `/desktop` | Рабочее пространство: форма карточки (5 шагов) + карта + результат анализа | | `/analysis/:case_id` | Результат анализа: карта зон, КОНТУР-панель, «Завершить поиск» | | `/users` | Админка пользователей + вкладка «Аудит» | | `/admin` | Администрирование: Кейсы / Операции / Завершённые поиски | Компоненты: - **CaseForm** — 5 шагов: субъект → обстоятельства → среда → координаты (ТНП + геокодирование) → ресурсы; после сохранения автоматически создаётся поисковая операция - **SearchMap** — концентрические круги (0.5R/0.75R/R) + подсветка приоритетных направлений 45°-секторами (палитра ЦОУ: #ff4d00 / #ff9f1a / #ffd166) - **Dashboard / UsersAdmin / AdminDashboard** — VCL-density, тёмная тема (#0d1117 / #161b22 / #30363d / #e6edf3) Единые справочники исхода — `frontend/src/constants.js` (тип места находки, кто нашёл, 8 румбов) — используются во всех точках ввода исхода. --- ## 10. Аутентификация, роли и подразделения (RBAC) ### Иерархия подразделений РЦУ РЧС (республиканский) → 6 областных ОУМЧС → 135 Г(Р)ОЧС/ПАСО. Справочник — идемпотентный сид `backend/seed_units.py` (источник: справочник подразделений РВС). ### Роли | Роль | Права | Скоуп видимости | |------|-------|----------------| | **admin** (РЦУ РЧС) | Все 9 прав | Все операции и пользователи республики | | **coordinator** (ОУМЧС) | view/create/update/export/manage_users/view_audit | Своё ОУМЧС + подчинённые Г(Р)ОЧС | | **operator** (Г(Р)ОЧС) | view/create/update | Свои поиски | | **observer** | view | Только просмотр | - Управление пользователями (`manage_users`): РЦУ РЧС — все; ОУМЧС — только своё поддерево (кросс-областные операции отбиваются 403 на бэкенде) - Созданный пользователь обязан сменить пароль при первом входе (`must_change_password`) - Сессии server-side: JWT несёт идентификатор, в БД — sha256-хэш; сброс пароля отзывает все сессии --- ## 11. Мультипоиск и операции `search_operations` — надстройка над кейсами для одновременной работы с несколькими поисками: - Одна операция на карточку (case_id unique) - **Статусная машина**: planned → active ⇄ paused → completed → archived (недопустимые переходы → 400) - Авто-создание операции при сохранении карточки; дашборд показывает активные - `POST /operations/{id}/complete` — завершение поиска: исход (найден живым/погибшим, дистанция, направление, кто нашёл) → кейс в архив + детерминированный прогон матмодели → сравнение «прогноз vs факт» (accuracy_note) - `GET /operations/summary` — счётчики для дашборда (активные / всего / завершено за 24 ч) --- ## 12. Интеграция с КОНТУР (отправка зон) КОНТУР — система полевой координации (ATAK + Meshtastic). ВЕКТОР отдаёт приоритетные зоны (outbound): 1. В операции задаётся `contour_operation_id` (UUID операции в КОНТУРе) 2. `POST /api/v1/operations/{id}/send-to-contour` строит `SearchZones[]` (GeoJSON-полигоны секторов ±22.5°, `zone_id = vec:<НАПР>:<дист>m`, probability по приоритету) и отправляет на `{CONTOUR_API_URL}/api/v1/vector/zones?operation_id=…` с `Bearer CONTOUR_TOKEN` 3. Каждый шаг аудируется (`contour_send_ok` / `contour_send_failed`); ошибка КОНТУРа → 502 4. `GET /api/v1/operations/{id}/areas-checked` — прокси проверенных полевых квадратов (inbound, B17) Переменные окружения: `CONTOUR_API_URL` (по умолчанию `http://192.168.0.130:8000`), `CONTOUR_TOKEN`. --- ## 13. Аудит Неизменяемый журнал (append-only): - `audit_events`: кто, что, когда, IP, user-agent, тип события (login/lockout/user_*/operation_*/contour_send_*/case_closed) - `audit_changes`: field-level `старое → новое` по каждому изменённому полю - Ретенция 90 дней (фильтруется при чтении из `security_settings`) - Доступ: право `view_audit` — вкладка «Аудит» в админке `/users` с фильтром по типу и пагинацией --- ## 15. Что готово и что в планах ### Реализовано - Форма карточки поиска (5 шагов), справочники, автоподсчёт «прошло времени» - Детерминированный матрасчёт: радиус, сектора, поведение, срочность, рекомендации — без ИИ - Локальный OSM-геослой в PostGIS (Беларусь), /analyze ~10 мс, без внешних вызовов - Карта: концентрические круги + подсветка направлений (дизайн согласованный) - РБАК: иерархия РЦУ РЧС → ОУМЧС → Г(Р)ОЧС (142 юнита), 4 роли / 9 прав, блокировки, сессии - Мультипоиск: операции, статусная машина, дашборд «Активные поиски» - Завершение поиска с внесением исхода, архив завершённых с «прогноз vs факт» - Аудит 90 дней с field-level деталями - Интеграция с КОНТУР: отправка зон (контракт B20) - Журнал аудита пользователей: входы, изменения, отправки - 231 автотест (pytest), 5 skipped ### В планах - **E5 (B17)** — приём полевых данных от КОНТУРа: треки групп, проверенные квадраты (areas-checked) на карте операции - **Живая интеграция с КОНТУР** (CT130) — настройка токена и реальная отправка зон - **Локальный tile-сервер** — для полностью offline-фронта (сейчас тайлы OSM с CDN в браузере) - **Калибровка settlement_score** — вкладка населённых пунктов в скоринге секторов (открытый методический вопрос) - **Калибровка зонального скоринга** по накопленному архиву «прогноз vs факт» --- ## 16. Контакты и сопровождение - Развёртывание и эксплуатация: см. [SETUP_GUIDE.md](SETUP_GUIDE.md) - План работ и статус этапов: `vector_tasks.md` - API-документация: Swagger `http://<сервер>:8000/docs` - Репозиторий: [`Sadmin/vector`](http://192.168.0.106:3000/Sadmin/vector) Смежные системы МЧС: **РВС** («Регистрация входящих сообщений», [`Sadmin/rvs-web`](http://192.168.0.106:3000/Sadmin/rvs-web)), **КОНТУР** (полевая координация).