Версия: 1.8 (упрощённый тестовый MVP)
Дата: 03.11.2026
Назначение: SaaS для мониторинга автопарков независимых компаний и собственного автомобиля частного водителя; GPS с телефонов поступает из собственного мобильного приложения платформы.
Статус: спецификация собственного тестового MVP, не отчёт о реализации. Приоритет — простой мониторинг и управление текущими перевозками. Документы, дорожные слои, накопление GPS-точек без сети и удаление аккаунта через интерфейс в MVP не входят. История на сервере формируется только из полученных online-точек; внешний GPS-клиент не используется.
Система обслуживает несколько независимых компаний и частных перевозчиков. Компании отслеживают автомобили на карте в реальном времени, управляют рейсами и сотрудниками. Администраторы приглашают водителей и видят только данные своей организации. Часть административных функций доступна в мобильном приложении.
Предусмотрены два самостоятельных сценария регистрации:
Для частника основной интерфейс — «Мой автомобиль», состояние трекера и «Поделиться постоянной ссылкой». Рейсы и остальные функции MVP доступны по необходимости. Компания и частник могут передать логисту постоянную ссылку на карту автомобиля: логист видит, где находится машина, подбирает заказы с учётом её положения и может переслать эту ссылку заказчику. Получателям достаточно браузера, регистрация не требуется.
Владелец компании и администратор всегда имеют доступ ко всему своему автопарку: на общей live-карте видны все активные машины с последней позицией, временем точки и связью независимо от рейса. Активные машины без координат остаются в списке как «Нет данных». Архивные машины доступны отдельно с карточками и сохранённой историей, но не показываются активными маркерами.
Референсы: https://qsgroup.eu/monitoring-gps-pojazdow/ и https://www.kornilov.de/ . Референсы являются ориентирами по назначению продукта; функциональные требования ниже самостоятельные.
| Компонент | Технологии |
|---|---|
| Веб-дашборд | Vue 3, TypeScript, Vite, Pinia, Vue Router |
| Мобильное приложение платформы | Собственный клиент: Tauri 2 + Vue 3, Android/iOS с первого релиза; GPS, рейсы, быстрая связь с диспетчером, push, состояние подключения |
| Нативный GPS | Собственный Tauri plugin: Kotlin / Android location API и foreground service; Swift / Core Location на iOS |
| Передача GPS | Нативный отправитель независимо от Vue/WebView; только актуальная позиция, без накопления offline-истории |
| Backend | ASP.NET Core (.NET), C# |
| Realtime | ASP.NET Core SignalR |
| База данных | PostgreSQL + PostGIS |
| Кэш / масштабирование | Redis по необходимости |
| Frontend tooling | Bun |
| Локализация | Vue I18n (vue-i18n), Composition API; общие словари для веба, мобильного интерфейса и страниц мониторинга |
| Карта | MapLibre GL JS + картографические данные OpenStreetMap; общий компонент для веба, мобильного WebView и страниц постоянных ссылок |
| Картографическая подложка | Сменяемый провайдер совместимых векторных тайлов и стиля MapLibre либо собственный хостинг; адреса сервисов, атрибуция, ключи и квоты задаются конфигурацией |
| Push | FCM / APNs, интеграция с мобильным приложением |
| Деплой | Docker, reverse proxy, CI/CD |
| Логи | Serilog, OpenTelemetry |
Технический риск: собственный нативный GPS-модуль входит в MVP. На Android предусматривается foreground service с видимым уведомлением, на iOS — Core Location с поддержкой фоновой геолокации. Разрешения, энергосбережение и завершение приложения ОС могут прерывать сбор и передачу. Нужен ранний прототип на реальных Android/iOS, включая заблокированный экран, стоянки, перезапуск и восстановление связи. JavaScript-таймеры и открытый WebView не должны быть условием работы трекера. Источники: Android: фоновая геолокация, Apple: фоновые обновления координат.
tile.openstreetmap.org не являются запланированной инфраструктурой SaaS: их ёмкость ограничена и действует политика использования тайлов OSM. Свободные данные OSM не означают бесплатный неограниченный хостинг карт.| Роль | Права |
|---|---|
| PlatformOwner | Управление организациями и системой, служебный доступ к данным клиентов для поддержки с аудитом |
| OrganizationOwner | Все ресурсы своей организации, администраторы и водители |
| IndividualOwner | Свой личный кабинет, одна машина, свой трекер, рейсы и выдача/отзыв доступа к отслеживанию |
| Admin | Весь автопарк своей компании, постоянный мониторинг всех машин, создание водителей, автомобили, рейсы и выдача ссылок |
| Dispatcher | Мониторинг, управление рейсами и назначениями в их рамках, отчёты, выдача/отзыв ссылок; чтение документов после появления модуля в будущем |
| Driver | Управление рабочими статусами своих рейсов и точек; принятие/отклонение, выезд/отмена выезда, прибытие/завершение; меню связи, профиль, передача GPS |
| Viewer | Только конкретно разрешённая машина, её маршрут, статус и данные; без доступа ко всему автопарку и операциям изменения |
| Получатель ссылки | Постоянный просмотр разрешённых данных автомобиля до отзыва ссылки; для временной ссылки — также до окончания её срока/рейса |
Все данные кабинета принадлежат tenant_id; глобальные учётные записи users и системный справочник разрешений могут не иметь tenant. Backend проверяет активное членство и разрешения на каждом запросе, включая связанные ресурсы; пользователь не может назначить себе чужой tenant_id. Администратор не может выдать роль выше собственной. Служебный доступ PlatformOwner к данным клиентов разрешён для поддержки: отдельный служебный контекст с выбором клиента, причиной обращения и аудитом просмотров/изменений. Tenant-проверки не отключаются глобально. Viewer получает явное разрешение на конкретную машину, проверяемое также для истории и SignalR; это авторизованная роль, отдельная от получателя публичной ссылки.
Личный кабинет частника технически тоже является tenant с типом individual; корпоративный tenant имеет тип organization. Это внутренняя изоляция данных, не требование оформлять юридическое лицо. IndividualOwner совмещает владельца и водителя и управляет трекингом своей машины. Корпоративная роль Driver не получает права управления политикой трекинга. Для личного кабинета допускается одна активная машина; её замена сохраняет историю привязок. Переход частника в организацию — будущее расширение, не сценарий MVP.
assigned, accepted, rejected, en_route, completed, cancelled. Рабочие статусы меняет водитель, прибытие/завершение отмечается для конкретной точки; завершение последней обязательной точки автоматически завершает рейс. Диспетчер/администратор отслеживает прогресс и не имеет отдельного ручного переключателя рабочих статусов.Рейсы, точки доставки, круглые геозоны, push и отчёты входят в MVP. Документооборот целиком перенесён в будущие расширения: экранов, загрузки/заполнения документов и файлового хранилища в первой версии нет. Будущее право диспетчера читать документы не добавляет модуль в текущий объём.
pending, arrived, completed. Не требуется отдельное подтверждение статуса администратором.ordinal — порядок отображения. Закрытие последней оставшейся обязательной точки автоматически ставит рейсу completed; отдельной кнопки завершения всего рейса не нужно. GPS/постоянная ссылка работают без рейса. Вариант списка грузов и удобство кнопок уточняются на первом UX-прототипе без отдельного сложного процесса согласования.Начальная архитектура — модульный монолит: Identity, Tenants, Fleet, Tracking, Sharing, Trips, Geofences, Notifications, Alerts, Reports, Audit. Tracking принимает текущую позицию авторизованного устройства. Нативный модуль собирает/передаёт координату и применяет политику независимо от UI, без устойчивой offline-очереди. Документы и внешние автомобильные трекеры — будущие расширения. Для тестового проекта выделение микросервисов не требуется.
Карты получают первоначальный снимок разрешённых машин/позиций через HTTP и затем обновления SignalR. При переподключении повторно проверяются права и загружается согласованный свежий снимок с учётом обновлений, пришедших во время загрузки; доставка событий SignalR сама по себе не гарантирует восстановление пропущенных изменений. Обновления содержат идентификатор машины и порядок/версию, достаточные для исключения отката к старой позиции. Удалённые права и отозванные ссылки проверяются для уже открытых соединений.
Изменения справочников/рейсов сохраняются транзакционно. Повтор запроса не создаёт повторных действий и уведомлений. Push дополняет сохранённые события в приложении; конкретный механизм повторов выбирается при реализации без отдельной инфраструктуры для MVP.
/api/v1/tracking/position с авторизацией устройства.Трекинг непрерывный, включая стоянки, без автоматического отключения по окончании смены или рейса. Realtime означает отображение новой принятой точки без ручного обновления; это не гарантия ежесекундной передачи с телефона. На карте всегда видны возраст точки и состояние связи; отсутствие новых данных не обозначается как подтверждённая стоянка.
Основные настройки — интервал GPS в секундах и точность: наивысшая / высокая / средняя / низкая (highest/high/medium/low). Их меняет владелец/администратор, у частника — сам владелец; корпоративный водитель не получает права обходить политику. Целевой интервал задаёт частоту получения/записи свежего замера, а не гарантированный таймер ОС. Стартовый технический диапазон для испытаний — 1–300 секунд, значение по умолчанию — 15 секунд; границы корректируются по батарее/нагрузке. Дистанционный фильтр опционален и по умолчанию отключён, чтобы не подменять настройку времени. Один телефон применяет один текущий профиль; разные устройства могут использовать разные профили.
Предлагаемые стартовые профили собственного клиента для испытаний:
| Профиль | Обновления в движении | Heartbeat на стоянке | Назначение |
|---|---|---|---|
| Сбалансированный, по умолчанию | 15 с, высокая точность | Определяется порогом связи ниже | Обычный рейс, приоритет умеренного расхода батареи |
| Подробный, пример пресета | 5 с, наивысшая точность | Определяется порогом связи ниже | Детальный маршрут, желательно при зарядке |
| Экономичный, пример пресета | 60 с, средняя точность | Определяется порогом связи ниже | Длительная автономная работа с менее точным маршрутом |
Пресеты — начальные предложения для испытаний, не ограничение ручной настройки. Низкая точность доступна независимо от пресетов. Названия уровней отражают запрос к ОС, а не гарантированный радиус в метрах; accuracy_m показывает фактическую оценку замера. Нельзя выдавать среднюю/низкую точность за точную GPS-позицию. Адаптивное снижение частоты на стоянке допустимо только как явная настройка, не как скрытая замена заданного интервала. Точность и частота влияют на батарею: Apple: эффективное использование геолокации.
Порог задаёт время с последнего подтверждённого сервером контакта устройства, а не с GPS-замера. Свежая точка или heartbeat подтверждают связь; heartbeat без координаты не подтверждает положение/стоянку. По умолчанию для испытаний предлагается 60 секунд. Для выбранного порога T клиент планирует контакт не реже T/3: примерно 1,5 / 5 / 10 / 20 секунд соответственно; очередная отправка GPS может заменить heartbeat. Сервер использует своё время приёма, отдельно показывает возраст GPS и создаёт одно уведомление при превышении порога.
Это целевой режим, а не обещание точного фонового таймера телефона. ОС, сеть и энергосбережение могут задерживать heartbeat, особенно на iOS; 5 секунд повышают расход сети/батареи и число ложных тревог. При ограничениях ОС UI показывает, что выбранный режим не подтверждён испытаниями/недостижим в текущих условиях; порог самовольно не увеличивается. Постоянное HTTP-подключение/частые heartbeat не превращают стороннюю ОС в гарантированный канал реального времени. Серверная проверка порога и задержка доставки push измеряются отдельно. При административном выключении трекера/архивировании вместо тревоги «потеря связи» показывается соответствующее управляемое состояние.
Низкий расход батареи — приоритет, числовой бюджет пока не задан. В испытаниях фиксируются расход %/час, модель телефона, версия ОС/клиента, профиль, движение/стоянка, зарядка, сеть и пропуски точек за полный рабочий день. Профиль принимается по этим результатам. Платформа хранит назначенный и подтверждённый устройством профиль; собственный клиент получает настройки с сервера и применяет их без переустановки.
При отсутствии сети водитель продолжает поездку, приложение не сохраняет неотправленные GPS-точки для последующей загрузки. Диспетчер/администратор получает уведомление по заданному порогу, карта сохраняет последнюю принятую точку с её временем и состоянием «Нет связи». После восстановления передаётся актуальная позиция, состояние обновляется, событие потери связи закрывается. Серверная история содержит только фактически полученные координаты.
Пробег, движение и остановки за пропуск GPS неизвестны; линия между последней и новой точкой не считается фактическим маршрутом и не включается автоматически в пробег. Геозона не создаёт задним числом события о пути, которого сервер не видел. Это осознанное упрощение тестового проекта. Кэш списка рейсов допустим и не является GPS-очередью; изменения статусов пока подтверждаются при доступной сети.
В компании включение/выключение сбора, профиль и разрешение приёма данных меняет владелец/уполномоченный администратор; корпоративный водитель таких прав в UI/API не имеет. Частник делает это сам как IndividualOwner. Изменения аудируются. Политика раздельно задаёт collection_enabled (сбор на телефоне) и ingest_enabled (приём на сервере). Нативный модуль получает политику через авторизованный API, применяет её и подтверждает результат по версии. Получение команды не считается успешным запуском GPS: отдельно сообщаются разрешения, фактическое состояние сбора и ошибка/ожидание ОС. Старое подтверждение не перезаписывает состояние более новой команды. SignalR/push могут ускорять синхронизацию, но после запуска и восстановления сети клиент обязательно запрашивает актуальную политику.
Без связи изменение политики ожидает применения, клиент использует последнюю успешно применённую политику. Серверный запрет приёма действует сразу; после разрешения принимается свежая позиция, досылки накопленных точек нет. Heartbeat/политика доступны при выключенном GPS, если устройство не отозвано; их выполнение ограничено возможностями ОС.
Собственный клиент не отменяет ограничений ОС: человек может отозвать разрешения, выключить телефон или остановить приложение системными средствами. Удалённый запуск фонового сбора не гарантируется, если ОС не разрешает его; администратор видит последнее сообщённое состояние, время сообщения и известную причину, а при необходимости пользователь получает запрос открыть приложение/выдать разрешение. При отсутствии heartbeat точная причина недоступности неизвестна: нельзя утверждать, что телефон выключен или разрешение отозвано. Поведение проверяется на целевых версиях Android/iOS. Источники: Android: ограничения запуска foreground service, Apple: фоновые обновления координат.
Поля точки: id, device_id, recorded_at, received_at, latitude, longitude, accuracy_m, speed_mps?, heading_deg?, altitude_m?, battery_pct?, sequence_no, trip_id?. received_at устанавливает сервер; recorded_at — время GPS-замера, а не отправки. Во внешнем JSON используются имена camelCase, в схеме БД — snake_case.
driver_id, vehicle_id и tenant_id по зарегистрированному источнику и привязке на момент записи точки; клиент не может выбрать чужую организацию.(device_id, point_id): UUID текущего замера сохраняется при повторе после потери ответа. Старый/переставленный сетевой запрос не откатывает последнюю позицию. При реконнекте клиент получает новый замер вместо replay старых точек.| Таблица | Ключевые поля |
|---|---|
tenants |
id, type (organization/individual), owner_user_id, name, status, settings_json |
users |
id, email, email_verified_at?, phone?, password_hash, status, disabled_at?, preferred_locale |
memberships |
tenant_id, user_id, role_id, status |
vehicle_access_grants |
tenant_id, user_id, vehicle_id, permissions, revoked_at? |
roles, permissions |
роли и разрешения |
devices |
id, tenant_id, driver_id, source_type, external_id?, credential_hash, platform, app_version, tracking_profile_id, last_seen_at, revoked_at |
tracking_profiles |
id, tenant_id, version, name, interval_s, min_interval_s, distance_m?, stationary_interval_s?, accuracy (highest/high/medium/low), connection_timeout_s (5/15/30/60) |
tracking_policies |
id, tenant_id, device_id, version, profile_id, profile_version, collection_enabled, ingest_enabled, effective_at, actor_id |
device_tracking_states |
tenant_id, device_id, applied_policy_version, applied_at, collection_state, permission_state, last_fix_at, last_heartbeat_at, error_code? |
vehicles |
id, tenant_id, plate_number, vin?, status, archived_at? |
vehicle_assignments |
tenant_id, driver_id, vehicle_id, starts_at, ends_at |
device_vehicle_assignments |
id, tenant_id, device_id, driver_id, vehicle_id, starts_at, ends_at, is_primary |
trips |
id, tenant_id, driver_id, dispatcher_user_id?, vehicle_id, status, dates, version |
trip_events |
id, tenant_id, trip_id, trip_stop_id?, actor_id, action, previous_status, new_status, created_at |
trip_stops |
id, tenant_id, trip_id, type (pickup/delivery), ordinal, address, location, status |
trip_cargo_items |
id, tenant_id, trip_id, name, description?, pickup_stop_id, delivery_stop_id |
geofences |
id, tenant_id, name, center, radius_m, rules_json |
geofence_assignments |
tenant_id, geofence_id, vehicle_id, trip_stop_id?, starts_at, ends_at? |
geofence_events |
id, tenant_id, geofence_id, vehicle_id, type, recorded_at |
tracking_shares |
id, tenant_id, created_by, token_hash, token_ciphertext, recipient_label?, vehicle_id, mode (permanent/temporary), trip_id?, permissions_json, history_from?, history_to?, expires_at?, revoked_at |
push_subscriptions |
id, tenant_id, user_id, platform, token, revoked_at |
gps_points |
id, tenant_id, device_id, driver_id, vehicle_id, assignment_id, trip_id?, sequence_no, recorded_at, received_at, location, accuracy_m, speed_mps?, heading_deg?, altitude_m?, battery_pct? |
last_positions |
tenant_id, vehicle_id, device_id, point_id, recorded_at, location, position_version |
alerts |
tenant_id, type, entity_id, created_at, resolved_at |
notifications |
id, tenant_id, recipient_user_id, event_id, type, payload, created_at, read_at?, push_state |
trip_summaries |
tenant_id, trip_id, driver_id, vehicle_id, distance_m, moving_s, stopped_s, unknown_s, calculated_at |
activity_summaries |
tenant_id, vehicle_id, driver_id, period_start, period_end, distance_m, moving_s, stopped_s, unknown_s |
audit_logs |
tenant_id, actor_id, action, target_id, created_at |
Индексы: (tenant_id, recorded_at DESC), (device_id, recorded_at DESC), GiST для координат, уникальность (device_id, id). При росте объёма — партиционирование GPS по времени.
Схема концептуальная: технические таблицы сессий/refresh-токенов, приглашений, восстановления доступа и outbox проектируются при детализации Identity/Notifications. Email обязателен, нормализуется и подтверждается; телефон необязателен для связи. Проверяются уникальность email, одно активное членство пользователя в обычном кабинете, принадлежность связанных сущностей одному tenant. Служебный контекст PlatformOwner не создаёт ему членство во всех клиентах. Ограничения одной активной машины individual, одного действующего телефона машины и одной машины на устройство обеспечиваются транзакционно. История привязок сохраняется.
driver_id обозначает пользователя в контексте проверенного членства в данном tenant, а не самостоятельный глобальный доступ к машине. Политики ссылаются на неизменяемую версию GPS-профиля; редактирование профиля не меняет исторически подтверждённые настройки. История статусов рейсов и состояние обработчиков круглых геозон проектируются в своих модулях; таблицы документов не входят в текущую схему MVP.
last_positions содержит одну позицию на машину, а не маркер на каждый телефон. Архивные машины сохраняют снимок внутри кабинета без live-маркера/внешнего доступа. Сводки сохраняют данные рейсов и активность машины/водителя, включая работу без рейса, после очистки точных координат. Они рассчитываются только по полученным данным; пробел связи сохраняется как неизвестный интервал, без дорисовки маршрута/пробега. Это не бессрочная история точного GPS-трека.
Секреты устройств и ссылок не хранятся открытым текстом в БД/логах. Для постоянной ссылки сохраняются хеш проверки и защищённо зашифрованный токен с ключом вне БД: уполномоченный владелец/администратор/диспетчер может повторно скопировать тот же URL после входа. Ротация ключей шифрования не меняет URL; восстановление ключей учитывается в резервном копировании. Только явный сброс генерирует новый токен и прекращает доступ по старому URL; удаление/отзыв закрывает существующий. Получатель может переслать уже имеющийся URL без права сброса.
Для постоянной ссылки expires_at = null, привязки к ограничивающему доступ рейсу нет. Для временной ссылки задаётся срок и/или конкретный рейс. Проверка доступа различает эти режимы: завершение рейса не отзывает постоянную ссылку на автомобиль.
Базовый URL: /api/v1. HTTPS, JSON, UTC ISO-8601, пагинация, стандартные ответы об ошибках.
Ниже — каталог операций, а не окончательный OpenAPI-контракт. Для каждого ресурса уточняются схемы, доступные методы, фильтры, ограничения и коды ошибок. Пользовательская авторизация и авторизация GPS-устройства имеют разные области доступа.
| Метод | Endpoint | Назначение |
|---|---|---|
| POST | /auth/register/company |
Регистрация владельца и организации |
| POST | /auth/register/individual |
Регистрация частного водителя и личного кабинета |
| POST | /auth/login |
Вход |
| POST | /auth/refresh |
Обновление токенов |
| POST | /auth/logout |
Отзыв только UI-сессии, без остановки и отзыва трекера |
| POST | /auth/contacts:verify, /auth/invitations:accept, /auth/password:reset-request, /auth/password:reset |
Проверка контакта, активация приглашения и восстановление доступа; отдельные операции |
| GET | /me |
Профиль, единственный активный кабинет, тип и разрешения |
| PATCH | /me |
Профиль и предпочитаемая локаль |
| GET/PATCH | /tenants/{tenantId} |
Кабинет и его разрешённые настройки |
| GET/POST/PATCH | /tenants/{tenantId}/memberships[/{id}] |
Приглашения/членство, роли, блокировка с серверной проверкой полномочий |
| GET/POST/DELETE | /tenants/{tenantId}/vehicle-access-grants[/{id}] |
Выдача/отзыв доступа Viewer к конкретной машине |
| GET/POST | /tenants/{tenantId}/drivers |
Водители |
| PATCH | /tenants/{tenantId}/drivers/{id} |
Изменение водителя |
| GET/POST | /tenants/{tenantId}/vehicles |
Автомобили |
| PATCH | /tenants/{tenantId}/vehicles/{id} |
Изменение/архивирование машины с сохранением истории |
| GET/POST/PATCH | /tenants/{tenantId}/vehicle-assignments[/{id}] |
Назначения водителей и периоды действия |
| GET/POST/PATCH | /tenants/{tenantId}/device-vehicle-assignments[/{id}] |
Основной GPS-источник машины и история переключений |
| GET/POST | /tenants/{tenantId}/trips |
Рейсы |
| PATCH | /tenants/{tenantId}/trips/{id} |
Данные/назначение рейса для администратора/диспетчера; не ручное изменение рабочего статуса |
| PATCH | /tenants/{tenantId}/trips/{id}/status |
Рабочее действие назначенного водителя; completed вычисляет сервер |
| GET/POST/PATCH | /tenants/{tenantId}/trips/{tripId}/stops[/{id}] |
Погрузки/разгрузки; состав редактирует диспетчер, рабочий статус — водитель |
| GET/POST/PATCH | /tenants/{tenantId}/trips/{tripId}/cargo-items[/{id}] |
Список грузов и их точки погрузки/разгрузки |
| GET/POST/PATCH/DELETE | /tenants/{tenantId}/geofences[/{id}] |
Геозоны и правила событий |
| GET/POST/PATCH | /tenants/{tenantId}/geofence-assignments[/{id}] |
Привязки геозон к машинам/точкам доставки |
| POST | /tenants/{tenantId}/devices |
Регистрация телефона/трекера с проверкой прав и выдача учётных данных устройства |
| GET | /tenants/{tenantId}/devices[/{id}] |
Устройства, фактическое состояние и применённая политика |
| POST | /tenants/{tenantId}/devices/{id}:revoke |
Отзыв GPS-устройства |
| POST | /tracking/auth:refresh |
Безопасное обновление учётных данных активного устройства |
| GET/POST/PATCH | /tenants/{tenantId}/tracking-profiles[/{id}] |
GPS-профили с версионированием настроек |
| PATCH | /tenants/{tenantId}/devices/{id}/tracking-policy |
Управление сбором, приёмом GPS и назначенным профилем |
| GET | /tracking/config |
Актуальная политика и GPS-профиль авторизованного устройства |
| POST | /tracking/config:ack |
Подтверждение применения версии политики или сообщение об ошибке |
| POST | /tracking/heartbeat |
Текущее состояние устройства, разрешений и время GPS-замера |
| POST | /tracking/position |
Актуальная позиция собственного клиента, один замер без offline-пакетов |
| GET/POST | /tenants/{tenantId}/tracking-shares |
Список и создание постоянных (по умолчанию) или временных ссылок |
| PATCH/DELETE | /tenants/{tenantId}/tracking-shares/{id} |
Изменение или отзыв ссылки |
| POST | /tenants/{tenantId}/tracking-shares/{id}:copy |
Получить тот же URL для повторного копирования с проверкой прав |
| POST | /tenants/{tenantId}/tracking-shares/{id}:reset |
Явно сменить токен, прекратить HTTP/realtime-доступ по старому URL |
| GET | /shared/tracking/{token} |
Только разрешённые данные по действующей ссылке |
| POST/DELETE | /me/push-subscriptions[/{id}] |
Регистрация/отзыв push-устройства |
| GET | /tenants/{tenantId}/tracking/last |
Последние позиции |
| GET | /tenants/{tenantId}/tracking/history |
История |
| GET | /tenants/{tenantId}/reports/activity |
Пробег, движение, остановки и время; driver/vehicle/trip, период, timezone и units |
| GET | /tenants/{tenantId}/alerts |
События |
| GET | /me/notifications |
Центр уведомлений пользователя |
| PATCH | /me/notifications/{id} |
Отметка прочтения собственного уведомления |
| GET | /tenants/{tenantId}/audit-logs |
Журнал действий в пределах прав |
| GET | /hubs/tracking |
SignalR |
Обозначение [/{id}] в таблице сокращённо объединяет коллекцию и отдельный ресурс, но не разрешает все перечисленные методы на обоих URL автоматически. Для каждой операции проверяются разрешения. Только /tracking/* использует область авторизации устройства; остальные закрытые операции требуют пользовательского контекста с проверкой роли, а /shared/* — действующей ссылки. deviceId сверяется с авторизованным устройством и не является секретом. Учётные данные хранятся в защищённом хранилище ОС и могут быть отозваны. Корпоративный водитель может зарегистрировать только свой разрешённый телефон и не меняет политику трекинга. Частник выполняет разрешённые операции со своей машиной как владелец.
GPS-профиль ссылается на версию настроек. /tracking/position принимает один актуальный замер; новая позиция заменяет неотправленную старую. Offline-batch API не требуется. Повтор UUID не создаёт вторую запись, включая потерю ответа сервера. Для рейсов отдельно проверяются права редактировать данные/назначение и права водителя менять рабочий статус; закрытие последней обязательной точки и завершение рейса сохраняются одной операцией.
tracking/last возвращает все разрешённые активные машины, включая записи без GPS; архив запрашивается отдельно с явным фильтром. tracking/history требует машину и ограниченный период. История фильтруется по сохранённой машине/привязке, а не по текущему владельцу телефона. Retention и пагинация применяются также к публичной истории; отсутствие старых сырых точек показывается как недоступная детализация, а не нулевой пробег.
Пример запроса собственного клиента к /tracking/position:
{
"deviceId": "device-uuid",
"position": {
"id": "point-uuid",
"sequenceNo": 1842,
"recordedAt": "2026-10-26T10:00:00Z",
"latitude": 52.2297,
"longitude": 21.0122,
"accuracyM": 8.5,
"speedMps": 14.2,
"headingDeg": 92
}
}
Ответ: pointId, status (accepted / duplicate); отказ содержит понятный код причины. SignalR-события: tracking.position.updated, trip.status.changed, alert.created; доступ проверяется на сервере. Отозванная при архивировании ссылка не получает ни архивной карточки, ни событий машины.
Для ссылок используются отдельные подписки только на разрешённый объект; токен не даёт членства в tenant-группе. Отзыв/сброс закрывает текущую подписку; истечение срока/окончание рейса применяется только к временной ссылке. Постоянная ссылка доступна между рейсами; её разрешения проверяются для открытых HTTP/realtime-контекстов. Дополнительные события: geofence.entered, geofence.exited, vehicle.archived, notification.created; доставка push повторяется с ключами идемпотентности, но получение ровно один раз на телефоне не гарантируется.
Web: Login → Overview → Live Map → History → Drivers → Vehicles → Trips → Alerts → Reports → Users & Roles → Settings → Audit.
Mobile Driver: Login → GPS Status → My Trips → Trip Details → Profile.
Mobile Admin: Overview → Map → Drivers → Trips → Alerts → Settings.
Регистрация: Выбор «Компания» / «Частный водитель» → Контакты и подтверждение → Организация и приглашения / Моя машина → Подключение телефона в собственном приложении → Разрешения геолокации и диагностика.
Частник (Web/Mobile): Мой автомобиль → Состояние трекера → История → Поделиться постоянной ссылкой → Активные ссылки; Рейсы по необходимости → Архив машин.
Компания: Карта активного автопарка → Автомобиль → Поделиться постоянной ссылкой → Копировать/отправить ссылку → Управление ссылками (в том числе явный сброс) → Архив машин.
Получатель ссылки: Карта автомобиля → Последнее обновление и связь → Копировать/переслать ссылку → Разрешённые сведения о рейсе; отдельные состояния «Доступ отозван» и, только для временных ссылок, «Ссылка истекла».
Дополнительные экраны MVP: Грузы и погрузки/разгрузки рейса → Меню связи → Круглые геозоны → Центр уведомлений и настройки push → Внутренний архив машин → Настройки GPS/связи → Часовой пояс и единицы. Удаления аккаунта в интерфейсе нет.
Общие требования: адаптивность, локализация на десять языков с первого релиза, включая страницы ссылок и уведомления; обработка ошибок, фильтры, пустые состояния.
| Язык | Код локали |
|---|---|
| Русский | ru |
| Английский | en |
| Польский | pl |
| Немецкий | de |
| Французский | fr |
| Румынский | ro |
| Испанский | es |
| Итальянский | it |
| Нидерландский | nl |
| Чешский | cs |
vue-i18n) с Composition API. Общие словари хранятся в packages/i18n, разбиваются по локали и функциональному разделу; пользовательские строки задаются ключами переводов. Документация: Vue I18n: Composition API.en). Выбор аккаунта сохраняется в профиле, выбор получателя постоянной ссылки — локально в его браузере и не меняет язык владельца машины.Размещение, инфраструктуру и организационно-правовые вопросы ведёт администратор заказчика. Команда разработки предоставляет продукт, инструкции развёртывания и настройки хранения/доступа; выбор страны размещения не входит в текущую задачу. Технические механизмы защиты и информирования остаются требованиями продукта.
| Показатель | Предварительная цель |
|---|---|
| Задержка realtime | p95 ≤ 5 секунд от получения точки сервером до UI при активном соединении |
| API последних позиций | p95 ≤ 500 мс при согласованной тестовой нагрузке |
| Доступность | Цель 99,5%/месяц для MVP, уточнить SLA |
| GPS без сети | Не накапливать точки; уведомить о потере связи, после восстановления передавать актуальную позицию |
| Масштаб | Начало с 2 водителей; без фиксированного продуктового потолка для компаний, рост подтверждается нагрузочными тестами |
| Хранение GPS | Тяжёлая сырая история — ориентир один месяц; техническое стартовое значение 30 суток, настраиваемое |
| Лёгкие данные | Карточки, рейсы, последние позиции, сводки и аудит — без заданного продуктового срока/тарифного лимита в MVP; фактический размер контролируется |
| Энергопотребление | Умеренный расход; бюджет в %/час определяется по испытаниям собственного клиента |
| Восстановление | Целевые RPO/RTO определить перед production |
Realtime измеряется от приёма сервером, а не от движения. Проверяются заданный GPS-интервал, связь, доставка на карту и push; для первого теста достаточно своих двух водителей. Большую нагрузку и эксплуатационные цели уточняют по результатам, не усложняя первую реализацию. Постоянные ссылки сохраняют ограничения частоты/объёма запросов.
Последняя позиция хранится отдельным снимком и не удаляется каскадно с gps_points. Принято сохранять карточки авто, рейсы, статусы и сводки без заданного срока; точный GPS-маршрут бессрочно не нужен. Сырые полученные точки хранятся 30 суток, затем остаются сводные показатели. Архивирование не стирает данные рейсов/авто; точный старый трек не восстанавливается. Периоды без связи не имеют точных координат и полного GPS-пробега даже в пределах этих 30 суток.
Оценка непрерывной записи с фиксированным интервалом: 500 устройств × 1 точка/15 секунд × 24 часа ≈ 2 880 000 точек/день. Это ориентир при 15 секундах, а не верхняя граница для настройки 1 секунда и не обещание производительности.
При 15 с за 30 суток одна машина даёт 172 800 точек: при расчётных 512–1024 байт/точку это примерно 84–169 МиБ, а 500 машин — примерно 41–82 ГиБ до резервных копий, репликации и временного запаса. При 1 с объём в 15 раз больше. Это основания считать сырые GPS-точки тяжёлыми; размеры БД/индексов измеряются в тестах. При фиксированном интервале на стоянке число точек не уменьшается: экономия возможна только при явно включённом адаптивном режиме. Heartbeat не сохраняется как отдельная GPS-история; хранится последнее состояние и события потери/восстановления связи.
В MVP тарифные ограничения для компаний не включаются. Будущие варианты подписок: обычный — 3 водителя, повышенный — 7, премиум — 10, безлимитный — без тарифного лимита. Это примеры будущих тарифов, а не текущие ограничения. Безлимитный тариф не означает бесконечную техническую ёмкость. Отдельный сценарий частника рассчитан на одну активную машину.
Проверки выполняются по мере реализации; этот список не означает, что тесты уже пройдены. Первая практическая проверка — своя компания с двумя водителями, веб-карта и реальные Android/iOS.
| Этап | Результат |
|---|---|
| 0. Discovery и GPS spike | Два сценария регистрации, UX, прототип собственного нативного GPS и фоновой отправки, батарея и ограничения управления на Android/iOS |
| 1. Foundation | Репозиторий, CI/CD, БД, регистрация компаний и частников, multi-tenant, RBAC, Vue I18n и общие словари десяти языков |
| 2. Tracking & Sharing | Собственный Kotlin/Swift GPS-модуль, передача актуальной позиции без offline-очереди, политика/профиль, SignalR, постоянные ссылки и отзыв доступа |
| 3. Dashboard | OpenStreetMap + MapLibre, конфигурируемый провайдер тайлов, кабинет одной машины, карта автопарка, история и статусы |
| 4. Fleet & Trips | Водители, автомобили, замена телефона, рейсы/точки в произвольном порядке, быстрая связь с диспетчером, субаккаунты, архив |
| 5. Reports, Geofences & Alerts | Отчёты с timezone/единицами, круглые геозоны, push Android/iOS и центр уведомлений, пороги связи, аудит |
| 6. Hardening & Release | Безопасность, нагрузка, батарея, публикация приложений |
Прежняя оценка 12–20 недель остаётся предварительной: собственный GPS-клиент на двух ОС, геозоны, короткие пороги связи, push, десять языков и сценарий частника требуют уточнения после этапа 0. Документы исключены из текущей оценки. Разработка и испытания нативного фонового трекинга входят в MVP; срок не считается обязательством до оценки подтверждённых сценариев.
fleet-platform/
apps/
web/ # Vue dashboard
mobile/ # Tauri + Vue Android/iOS
src-tauri/ # оболочка и интеграция собственного GPS-плагина
plugins/
tracking/ # Tauri plugin: native GPS, отправитель текущей позиции
android/ # Kotlin: location service и фоновая работа
ios/ # Swift: Core Location и фоновая работа
backend/
Fleet.Api/
Fleet.Application/
Fleet.Domain/
Fleet.Infrastructure/
Fleet.Tests/
packages/
ui/
map/ # MapLibre, конфигурация подложки, слои мониторинга
api-client/
shared-types/
i18n/ # общие ключи и словари ru/en/pl/de/fr/ro/es/it/nl/cs
infra/
docker/
deploy/
docs/
api/
architecture/
decisions/
Bun управляет frontend-зависимостями и сборкой; .NET SDK — backend. OpenAPI используется для генерации типизированного API-клиента.
Один рейс объединяет грузы. Для каждого задаются описание и две точки: погрузка/разгрузка. Водитель видит, какой груз забрать/сдать в выбранной точке, и отмечает прибытие/завершение; после закрытия всех обязательных точек рейс завершается автоматически. Жёсткой очередности между разными грузами нет, но разгрузка конкретного груза следует после его погрузки. Совпадающие места отображаются одной остановкой. Проверяем удобство на своих перевозках и корректируем экран, без отдельной сложной модели заказов/склада.
Это рабочее предложение по неокончательно выбранному представлению грузов, а не дополнительное обязательство заказчика. Других вопросов, требующих ответа до начала тестовой реализации, сейчас нет. Частоты GPS/связи, пороги расчётов, батарея и поставщики уточняются обычными испытаниями и разработкой.
Документы (загрузка, заполнение, статусы и чтение диспетчером), переход частника в организацию, ETA, оптимизация маршрутов, экспорт PDF/XLSX, интеграции ERP/TMS, внешние GPS/OBD-устройства, фото-подтверждение доставки, техобслуживание, расширенная аналитика, подписки и биллинг SaaS. Собственный GPS-клиент Android/iOS, круглые геозоны, push и центр уведомлений входят в MVP. Дорожные слои отменены и не входят в план расширений автоматически.