Безопасный коммит незакоммиченной работы перед реализацией бронирования слотов. Вне коммита оставлены: package-lock.json (пустой, мусор), requests.json (scratch-данные).
41 KiB
Бэклог pcp-tgu-ops-ui
Список задач по сервису pcp-tgu-ops-ui (и связанным бэкендам). Ведётся вручную и с помощью Claude: по просьбе — отмечаем выполненное и добавляем новые задачи.
Обозначения статуса:
[ ]— не начато[~]— в работе[x]— выполнено (переносится в раздел «Выполнено» с пометкой)
У каждой задачи есть стабильный номер #N — он не меняется при правках списка, на него удобно ссылаться. Новые задачи получают следующий свободный номер.
Задачи #12–#25 заведены по результатам аудита. Подробности, обоснование и рекомендации по каждой — в аудит-проекта-2026-06-06.md (ссылки на коды находок B/F/R/C).
Обновлено: 2026-06-11
Активные задачи
Карта и отображение
- #1 — Отображать трассы полос исходя из возможности съёмки КА. Для оптики показывать только участки, попадающие в допустимый угол Солнца.
- #2 — При клике в область, где пересекается несколько полос обзора, выводить список этих объектов на правой панели; при выборе элемента — выделять соответствующий объект на карте. Сделано: на вкладке «Редактор» (где рисуются полосы обзора) клик собирает полосы обзора отдельно от прочих объектов — если под точкой нет иного интерактивного объекта (заявки/маршрута/сброса/станции), а полос обзора несколько (наложение соседних витков), они отдаются списком в правую панель (
MapPickPanel), как у прочих наложений; одиночная полоса — сразу карточка. Выбор/раскрытие элемента подсвечивает полосу на карте (selectedObjectId→mapFocus.objectId). Полоса обзора получила карточку (КА + виток): новые поляrevolution/spacecraftIdу swath-полигона, проброс вTgu2DMapSelection, ветка swath вMapInfoPanel. Клик по заявке/маршруту поверх полос не засоряется полосами (полосы — только когда другого объекта под точкой нет). Файлы:Tgu2DMapView.handleEditorClick,mapTypes.ts,mapSelectionTypes.ts,TguEditorTab(сборка сцены),EditorPanels.MapInfoPanel. Доработка (2026-06-11): когда под точкой есть наложение ≥2 интерактивных объектов (напр. маршрут + заявка), в список выбора теперь добавляются и лежащие под кликом полосы обзора, чтобы их данные тоже были видны. Список упорядочен по приоритету (editorHitRank): маршрут съёмки → заявка → полоса обзора (маркеры/станции — перед маршрутом, сбросы — между заявкой и полосой). Одиночный клик по маршруту/заявке по-прежнему сразу выбирает объект (без списка), иначе любой клик в редакторе превращался бы в список из-за сплошного покрытия полосами. - #3 — Показ забронированных слотов на вкладках «Карта» и «Редактор». Включает подгрузку данных по забронированным слотам с бэкенда (фетч), а не только отрисовку уже имеющихся. На вкладке «Редактор» они должны быть другого цвета и выделяться.
Группировка
- #36 — Заполнение бортового накопителя на вкладке «Группировка». Сейчас (MVP) считается на фронте из объёмов маршрутов плана (
volumeMb): старт 0 на начало интервала плана,+volна конце съёмки,−volна конце сброса, ёмкость борта — модельные 500 ГБ (ONBOARD_CAPACITY_MBвfeatures/constellation/realData.ts). Довести до реального источника: брать ёмкость борта из НСИ платформы (не хардкод), а заполнение — из телеметрии/бортового учёта, когда такой канал появится; уточнить модель «съёмка копит / сброс опустошает» (частичные сбросы, очерёдность).
Редактор
- #4 — Показывать на вкладке «Редактор» только те заявки, которые может удовлетворить аппарат (например, для оптики не показывать заявку, для которой доступна только локация).
- #5 — Дать возможность превратить план в статусе
PLANNED(построенный цепочкой от последнегоLAID_IN) в черновик и вручную наполнить его включениями — до того, как он будет автоматически выдан по таймеру. - #38 — Правка SLOTS-маршрута «телепортирует» его (исчезает с карты). При «Редактировать» редактор пересобирает контур через
survey-route/build, передавая сохранённыйtimeStart. Для маршрутовsource=MANUALэто корректно (ихtimeStart= время построения, контур воспроизводится). Дляsource=SLOTStimeStart/виток/контур посчитаны другим конвейером и не согласованы: напр. режим 174 хранитtimeStart=2026-06-07T01:51:38, виток 152, контур над Чёрным морем (центр 45.42, 39.40), но в 01:51:38 спутник по flight-line над Индией (12–16° с.ш., виток 150) — пересборка даёт контур над Индией (10.73, 77.06), исходный прячется → маршрут «пропадает» из вида. Корень — та же баллистическая рассинхронизация, что и [#37]. Решение (2026-06-08): чиним только на бэкенде — согласоватьtimeStart/виток/контур у SLOTS-режимов, чтобыsurvey-route/buildвоспроизводил их. UI-страховку (не прятать исходный контур / блокировать правку SLOTS) сознательно НЕ делаем — симптом остаётся до бэкенд-фикса.
Комплексный план
- #6 — Пересчитывать комплексный план при бронировании новых слотов или при поступлении новой заявки.
- #44 — Добавить на вкладку «Комплексный план» просмотр тепловой карты (приоритеты/покрытие ячеек). Источник —
priority-mapсервиса заданий (importance ячеек) и/или статистика покрытия прогона; отрисовать как heatmap-слой поверх карты. Сделано: чекбокс «Тепловая карта» в тулбаре; фоновый слой-заливка ячеек по важности (cool→hot) под контурами плана/заявок; источникGET /api/pcp-request/v1/cells/priority-map, не интерактивен (в spatial-индекс не попадает).cellsApi.ts+drawHeatmap.ts+ слойheatmapвTgu2DMapView. Клик по объекту (ячейка/включение/заявка) показывает его данные в правом инспектореComplexInfoPanel(по аналогии с редактором; при наложении — список под точкой клика с раскрытием).
Бэкенд / инфраструктура
- #7 — Перевести сервис станций на ordinis (по примеру получения аппаратов в сервисе tgu).
- #47 — Перевести сервисы с Jackson 2 на Jackson 3 и убрать двойной стек. Под Spring Boot 4.0.2 MVC уже десериализует входящий JSON через Jackson 3 (
tools.jackson.*), а весь код монорепы написан под Jackson 2 (com.fasterxml.jackson,versions.jackson_databind=2.18.2); вpcp-request-serviceдля внутреннего парсинга держится отдельный Jackson 2-бин (JacksonConfig). Из-за этой рассинхронизации словили баг: Jackson 3 по умолчанию включаетFAIL_ON_NULL_FOR_PRIMITIVES, и явныйnullна примитивномDouble coverageRequiredPercentронял создание заявки (контракт-тесты не ловили — они на Jackson 2). Срочности/EOL-риска нет, это техдолг и консистентность.- Сейчас, дёшево (часы): системно снять класс багов «null на примитиве» — выставить
spring.jackson.deserialization.fail-on-null-for-primitives=falseв config-repo request-service (проверить, что свойство применяется к Jackson 3-бину; если нет — черезJackson3ObjectMapperBuilderCustomizer). Не латать каждое поле по очереди. - Среднесрок, по сервису: мигрировать по одному, пилот —
pcp-request-service(~7 файлов main, ~0.5–1 день). Аннотации не трогаем —jackson-annotationsв Jackson 3 остались в пакетеcom.fasterxml.jackson.annotation(у нас это основная масса:@JsonIgnoreProperties×36,@JsonProperty×16,@JsonInclude×15…). Менять только databind-поверхность:ObjectMapper×23,TypeReference×8,JsonNode×5,jacksonObjectMapper()×12 → пакетыtools.jackson.databind/core/module.kotlin; в Jackson 3JsonProcessingExceptionстала unchecked. Дропнуть Jackson 2-зависимости и Jackson 2-бинJacksonConfig. - Главный риск — Kafka:
KafkaConsumerConfig/сериализаторы завязаны наcom.fasterxmlObjectMapper. Перед стартом — спайк (~полдня): умеет лиspring-kafkaв нашем Boot 4 Jackson 3-сериализаторы. Если нет — Kafka-слой мигрируем отдельно, HTTP/внутренний JSON переводим независимо. - Масштаб (src/main): 67 файлов в 10 сервисах — pcp-dynamic-plan-service 22, pcp-ui-service 16 (выводится из эксплуатации, ср. #40 — можно не мигрировать), pcp-request-service 7, slots-service/pcp-tgu-service по 5, остальные 1–4. Грубая оценка всей репы — ~6–10 человеко-дней, основной риск — Kafka. Big-bang не нужен: шаг 1 закрывает причину бага, полную миграцию вести фоново сервис за сервисом.
- Сейчас, дёшево (часы): системно снять класс багов «null на примитиве» — выставить
- #45 — Собрать native-образ сервиса (Kotlin/Native,
kompile) и контейнер на его основе. Цель — быстрый старт и меньший footprint в сравнении с JVM-образом. Уточнить: какой сервис(ы) переводим первым, совместимость с рефлексией/AOT-конфигом (reachability metadata), сборку в CI и базовый образ контейнера. - #37 — Рассинхрон номеров витков между баллистическими источниками. Для одного и того же КА/времени
flight-line(трасса,/api/tgu-planning/spacecraft/{norad}/flight-line) иsurvey-route/build(контур, pcp-mission-planing) отдают разный номер витка: на Emissio 1 (norad 2) 2026-06-06 12:50–12:55 трасса помечена витком 142, а контур/сохранённые режимы — витком 144 (по широте ~74° с.ш. согласованы, расходится только нумерация). В редакторе карта фильтрует трассу/полосу обзора по видимому витку (buildEditorMapScene→visibleRevolutions), поэтому при сужении селектора до «виток N» точки трассы с другим номером выпадают и пропадает орбитальный контекст. Свести нумерацию витков к единому источнику (или маппинг на границе), чтобы трасса и маршрут согласовывались по витку.
Проверка сервисов
- #27 — Проверить сервис получения паспорта маршрута по отдельности.
- #28 — Проверить связь сервиса заданий и сервиса получения паспорта маршрута между собой.
Бэкенд / вывод pcp-ui-service из эксплуатации
- [~] #40 — Перевести все запросы фронта с
pcp-ui-serviceна источники и вывести сервис из эксплуатации.pcp-ui-service— наследие старого server-rendered UI; новый фронт ходит к доменным сервисам через gateway, иpcp-ui-serviceостаётся в маршрутизации только как catch-all для двух поверхностей:tguApi.ts→/api/tgu-planning/*(+ flight-line) иeditorApi.ts→/api/current-plans/missions/{id}/modes. Карта запросов, источники, реальные пути и пошаговый план — в план-перевода-с-pcp-ui-service.md. Ключевые моменты: (1) перенести во фронт UX-перевод ошибок решения по плану (сейчас живёт вTguPlanningService); (2)decision/confirm-layinидут в тест-контроллер tgu (/test/tgu/*, гейтtgu.test-controller.enabled) — поведение не меняем; (3) DTO проксируются как есть, схема фронта не ломается; (4) внешних потребителей у сервиса нет (проверено grep), но перед удалением кода свериться по мёртвым поверхностям (bookings/catalog/rva/com-plan/…). После переезда — снятьui-catch-allизconfig-repo/pcp-gateway-service.yaml, убрать сервис из docker-compose/helm/CI/config-repo.- Шаги 1–7: репойнт фронта на источники (
tguApi.ts→/api/pcp-tgu+/test/tgu/*; flight-line →ballisticsApi.ts;editorApi.tsmodes →/api/pcp-mission) и снятиеui-catch-allиз gateway. Перевод ошибок уже жил во фронте (errorMessageвtguApi.ts). Typecheck чист. - Шаг 8: вывод сервиса из эксплуатации (compose/helm/CI/config-repo/реестр
pcp.services.ui) и удаление кода — отложено до проверки репойнта на живом стенде. Пока сервис задеплоен, но без маршрутов.
- Шаги 1–7: репойнт фронта на источники (
Слоты (вкладка «Слоты»)
- #41 — Запрос слотового покрытия по заявке. Сделано (2026-06-11): добавлена отдельная вкладка «Слоты» (вместо размещения на «Заявке»/«Редакторе» — операции со слотами объёмные, их собрали в одном рабочем месте). Оператор выбирает заявку, интервал и фильтры (ветвь витка, признак «Покрытие») и жмёт «Рассчитать слоты» → слоты-кандидаты от slots-service напрямую через gateway (
GET /api/pcp-slots/api/slots/request-cover, минуя умирающийpcp-ui-service, ср. #40). Результат — таблица (КА, виток/ветвь, слот/цикл, время, крен, статус) и отрисовка контуров на 2D-карте поверх контура заявки; клик по строке центрирует карту и подсвечивает контур. Новый роут gatewayid: slots(/api/pcp-slots/**, StripPrefix=2 → slots-service). Файлы:config-repo/pcp-gateway-service.yaml,api/slotsApi.ts,model/slotTypes.ts,features/tgu-slots/TguSlotsTab.tsx, регистрация вкладки вTguActiveTab/TguToolbar/TguPlanningPage, стили.tgu-slots-*. - #42 — Бронирование слотов по заявке. Сделано (2026-06-11): на вкладке «Слоты» чекбоксами выбираются слоты → «Забронировать» (пакетно,
POST /api/slots/booking/requestс{requestId, slots:[{satelliteId, slotNum, cycle}]}) и «Снять бронь» (DELETE …/booking/request?requestId=). После операции перечитываются брони заявки (GET …/booking/by-request/{id}), забронированные слоты получают бейдж «бронь» в таблице и отдельный цвет (слойcomplexPlan) на карте. Связано с показом забронированных слотов (#3) и пересчётом комплексного плана при бронировании (#6) — последнее пока на бэкенде не триггерится. Уточнить при необходимости идемпотентность повторной брони. - Фильтр отображения слотов по суткам (2026-06-11): на вкладке «Слоты» добавлен ползунок-фильтр «Сутки» — тот же двуручейный селектор, что «Витки» на «Редакторе» (классы
map-pass-selector*), наложенный на карту. Слоты группируются по локальным суткам начала (tn); выбранный диапазон суток сужает и таблицу, и сцену карты (кандидаты), «Выбрать все» работает по видимым слотам. Новый расчёт раскрывает фильтр на весь диапазон. Клик по слоту на карте теперь не только подсвечивает строку, но и подскроливает её в зону видимости таблицы. Файлы:features/tgu-slots/SlotDaySelector.tsx(новый),features/tgu-slots/TguSlotsTab.tsx.- Доп. (2026-06-11): подсветка активной строки сделана явной (заливка + левый акцентный бар + bold). Скролл к строке — ручной
scrollByпоgetBoundingClientRectс центрированием и плавной анимацией:scrollIntoView({block:"nearest"})оказался ненадёжен (выбирал не тот скролл-предок / не двигался). Строка ищется по стабильномуdata-slot-id, а не поref(тот при «переезде» между строками мог занулиться). Подсветку ведут только клики по слоту; клик по контуру заявки её не сбрасывает, клик по пустой карте — снимает.
- Доп. (2026-06-11): подсветка активной строки сделана явной (заливка + левый акцентный бар + bold). Скролл к строке — ручной
- Выбор заявки на карте (вместо списка) (2026-06-11): на вкладке «Слоты» убран выпадающий список заявок — он не масштабируется на тысячи заявок. Базово все заявки интервала отображаются на карте; клик по заявке (или по наложению нескольких — список с раскрытием) открывает плавающую панель-инспектор справа над картой (общий
MapObjectInspector) с данными заявки и кнопкой «Выбрать». До выбора в тулбаре вместо списка — имя выбранной заявки или «—», а левая панель показывает фильтр «Заявки на интервал» (С/По + «Загрузить»/«Сегодня»/«7 дней»/«30 дней», как на «Карте») и подсказку «Кликните заявку на карте». После выбора заявки появляется кнопка «Сброс» справа над картой — возвращает окно в состояние «заявка не выбрана, слоты не показаны». Файлы:features/tgu-slots/TguSlotsTab.tsx,features/tgu-map-2d/MapObjectInspector.tsx(опц.onSelectRequest→ кнопка «Выбрать» в карточке заявки), стили.tgu-slots-request*/.tgu-slots-reset/.tgu-slots-inspector/.tgu-slots-requests-panel. - Бронь не отражалась в UI + явная подсветка строки (2026-06-11): «не работает кнопка бронь» — диагностировано: POST
booking/requestбронирует корректно и в ответеBookedSlotDTOнесёт верныйcycle, ноGET booking/by-request/{id}отдаёт базовый слот в другом cycle-фрейме (bookedSlot.slot.toDTOбез сдвига наbookedSlot.cycle), из-за чего ключsat:slotNum:cycleне совпадал с рассчитанной строкой → бейдж «бронь» не появлялся, на карте — лишний полигон. Фронт-обход: после брони отмечаем забронированными ровно те рассчитанные слоты (с контуром), что вернул POST; by-request на горячем пути не перечитываем; cancel чистит локально. Заодно подсветка активной строки слота сделана явной (фон + левый акцентный бар + bold). Root-fix (на бэке, отдельно):slots-service SlotService.bookedByReqId/bookedByIdдолжны возвращать слот, сдвинутый наbookedSlot.cycle(какresolveBookedSlotStartTime/перечисление вrequest-cover) — иначе пред-существующие брони не коррелируют с расчётом. Файлы:features/tgu-slots/TguSlotsTab.tsx, стили.tgu-slots-table tbody tr.is-active. - #43 — Уточнение статуса слота. На вкладке «Слоты» уже показывается базовый статус слота (
state: свободен/бронь/занят) и бейдж брони по заявке. Осталось: подтягивать/обновлять актуальный статус занятости по заложенному плану (слоты занимаются поLAID_IN, а не поACCEPT, ср. #10) и согласовать его источник со slots-service (real-time/после закладки), а также прокинуть статус на вкладки «Карта»/«Редактор» (#3).
Аутентификация и роли
- #46 — Вывести роли на морду и связать их с аутентификацией. На фронте показывать роль(и) пользователя и разграничивать доступ к действиям/вкладкам по роли; источник ролей — аутентификация (токен/headless-auth,
@nstart/auth). Уточнить: какие роли есть, где они приходят (claims токена / профиль), как маппятся на UI-разрешения (что скрывать/блокировать), и нужен ли серверный enforcement на стороне сервисов, а не только UI-гейтинг.
Баги
- #8 — Ошибка при попытке принять план на вкладке «Планы».
- #39 — Авторасчёт плана выдаёт нереализуемые маршруты съёмки с креном > 45° (напр.
survey-211, Emissio 1, крен 46.8°). Такой маршрут нельзя построить вручную: фронт зажимаетMAX_ROUTE_ROLL_DEG = 45(TguEditorTab.tsx), а баллистика/геометрия маршрута и фильтр видимости (МПЛ,abs(kren) <= 45°) определены только до 45°. Диагностика (2026-06-08): корень — данные каталога, не логика. slots-service строит крен слота как центр угловой полосы из каталога (SlotService.kt:903-905,gamma=(begin+end)/2) и предел 45° не учитывает; полосы берутся напрямую изsatellite_slot_angle. В сиде каталога (pcp-satellite-catalog-service/.../V1__initial.sql) у оптических Emissio (id 1-6, 22) собственный профиль полос уходит до 50.42°, и полоса №17 [46.08°, 47.51°] даёт центр ровно 46.8°. Версия «посчитали по логике локации» проверена и НЕ подтвердилась: в slots-service нет ветвления по типу КА (оптика/локация) — все КА строятся однимTestSatelliteImpl, солвер выбирается стратегией запроса (GREEDY/CONTINUOUS),type_codeне используется. Reflexio (локация) наоборот имеет узкие полосы ~17-25°. Решение (вернуться позже): либо обрезать/убрать в каталоге оптические полосы за 45° (поз. №16-20 у Emissio), либо добавить в slots-service зажим/отсев слотов по рабочему пределу крена КА. Истинный предел Emissio подтвердить у владельца каталога/баллистиков перед правкой.
Аудит — надёжность и бэкенд
Источник и рекомендации: аудит-проекта-2026-06-06.md.
- #12 — (B1, высокий) Camunda job-воркеры в
FlowServiceмолча глотают исключения (catch { logger.warn(ex.message) }) приautoComplete=false: задача не завершается и не падает, BPMN-процесс зависает без инцидента. Завершать черезnewFailCommand/проброс наверх, логироватьerror(msg, ex). - #13 — (B2, средний) Снять симуляционную заглушку
Thread.sleep(5000)(«имитация подтверждения плана») вFlowService.sendPlanпри появлении реального канала подтверждений. - #14 — (B3, средний) Привести в порядок несогласованные пакеты-копипасты (ballistics→
pcp_request_service, complex-mission→pcp_satellites_service, ui-service и slots-service делятslots_service, request→org.nstart.dep265, tgu→без.pcp.). Отдельной задачей из-за широкого охвата. - #15 — (B4, низкий) Опечатка «srpring» в
pcp-srpring-boot-admin-serverрастиражирована по каталогу, пакету, CI, docker-compose, gradle. Чинить вместе с #14. - #16 — (B5, низкий) ~9 мест
logger.warn/error(ex.message)теряют stack trace — заменить наlogger.error("...", ex).
Аудит — фронтенд
Источник и рекомендации: аудит-проекта-2026-06-06.md.
- #17 — (F1, средний) Вынести общий
src/api/http.ts(fetchJson+extractErrorText+ маппинг ошибок):extractErrorTextпродублирован в 5 клиентах, fetch-обвязка повторяется ~22 раза. Заодно устранить расхождение приоритета полей ошибки (F5). - #18 — (F2, низкий) Перевести нетипизированный островок
features/constellation/*(.jsx/.js,allowJs) на TS или добавить декларации типов на границе. - #19 — (F3, низкий) Удалить мёртвый barrel
features/constellation/index.js(нигде не импортируется). - #20 — (F4, низкий) Декомпозировать God-компонент
TguEditorTab.tsx(1727 строк): вынести хуки (useDraftHistory,useRouteBuilder), разнести панели.
Аудит — гигиена репозитория и конфиги
Источник и рекомендации: аудит-проекта-2026-06-06.md.
- #21 — (R1, средний) Убрать из git крупные дампы в корне (
pcp_requests_converted_create_requests.json~228 КБ,response_заданияКубернетс.json~157 КБ,response_заданияЕмельянов.json~66 КБ) — перенести вsrc/test/resources/docsили удалить с добавлением в.gitignore. - #22 — (R2, низкий) Удалить посторонние артефакты в корне:
node_modules/+ пустойpackage-lock.json,obj_terra.zip; при желании добавить/package-lock.jsonв.gitignore. - #23 — (R3, низкий) Прогнать
./gradlew clean/ удалить 26 устаревших каталоговbin/(задваивают исходники, шумят при grep). - #24 — (C1, средний) Унифицировать креды БД в config-repo: привести все сервисы к переопределяемому
password: ${DB_PASSWORD:...}(как в tgu-service), убрать зашитыеpassword: password. - #25 — (C2, низкий) Сузить CORS для прода в
pcp-ui-service(allowedOriginPatterns("*")+allowCredentials) до конкретных origin; держать в курсе отсутствие CORS у напрямую вызываемых сервисов (работает только через прокси/nginx).
Аудит — сервис заданий (pcp-request-service)
Источник и рекомендации: аудит-сервис-заданий.md (коды находок RS1–RS7).
- #30 — (RS2, высокий) Матчинг засчитывает покрытие по любому геометрическому пересечению, игнорируя совместимость аппарата (
survey_type, крен, разрешение, угол Солнца). Добавить фильтр совместимости маршрут↔заявка перед зачётом покрытия; тип сенсора в паспорте маршрута согласовать в рамках #28. Отложено до #28 (решение, 2026-06-06):RoutePassportDtoне несёт явныйsurveyType— контракт фиксируется в рамках аудита связи сервисов #28, тогда же делаем фильтр. - #33 — (RS5, низкий) Площади считаются планарно в градусах (JTS
areaна EPSG:4326) — искажение для крупных/высокоширотных заявок. Перейти на геодезию (ST_Area(geography)или равновеликая проекция), когда вырастут требования к точности. Отложено по решению аудита: покрытие считается отношением площадей (планар ≈ геодезия для обычных AOI), а переключениеarea()затрагивает matching и точечные ассерты тестов — вводим при росте требований к точности.
Выполнено
- #34 — (RS6) Проброс требуемого процента покрытия из API + разведение терминов. Готово: в
CreateRequestDtoдобавлено опциональное полеcoverageRequiredPercent(0 < x ≤ 100, по умолчанию 100), оно пробрасывается в сущность и используется как порог COMPLETED; захардкоженная константа убрана. В KDoc/коде разведены «прогресс покрытия» (coverageRequiredPercent) и «важность заявки» (importance, карта приоритетов ячеек). Тесты:RequestServiceTest,RequestApiDtoContractTest. - #31 — (RS3) DLQ и error-handler для Kafka-листенера маршрутов. Готово:
KafkaConsumerConfig—DefaultErrorHandler+DeadLetterPublishingRecovererв топикpcp.route.georeference-dlq.v1, экспоненциальный bounded retry, контрактные ошибки (JsonProcessingException/ConstraintViolationException) → DLQ без ретраев, диагностические заголовки. Настройкиapp.kafka.consumer.route.*, активируется при заданномspring.kafka.bootstrap-servers. ТестKafkaConsumerConfigTest. - #35 — (RS7) Уведомление geoportal о финале заявки. Готово: единый топик
pcp.request.status.v1(заменил orphanpcp.request.completed.v1) с терминальным статусом в payload (COMPLETED/EXPIRED). Эмиссия из двух мест через тот же transactional outbox:RouteMatchingService(по покрытию) иRequestExpiryService(по сроку, #29). СервисRequestCompletedOutboxService→RequestStatusOutboxService, eventTypeREQUEST_TERMINAL_STATUS. Outbox получил backoff: колонкиattempts/next_attempt_at, удвоениеinitial→max, пределmax-attempts(терминальный FAILED). Конфигpcp.outbox.*. Тесты:OutboxPublisherServiceTest,OutboxEventRepositoryJpaTest,RequestStatusOutboxServiceTest,RequestExpiryServiceTest,RouteMatchingServiceTest. Док: интеракции обновлены. Известный мелкий зазор: заявка, созданная уже просроченной, события не шлёт (вырожденный кейс). - #29 — (RS1) Планировщик истечения заявок. Готово:
RequestExpiryService(@Scheduled,FOR UPDATE SKIP LOCKED, батчи с верхним лимитом) переводит просроченныеACCEPTED/ACTIVEвEXPIRED; несколько инстансов безопасны.refreshStatusтеперь вызывается и при создании заявки — статус приёма отражает окно (ACCEPTED/ACTIVE/EXPIRED), а не всегдаACCEPTED. Конфигpcp.request.expiry.*в config-repo. Тесты:RequestExpiryServiceTest(юнит) +RequestRepositoryJpaTest(H2). Разблокирует EXPIRED-эмиссию в #35. - #32 — (RS4) Удалить мёртвый конфиг сервиса заданий. Готово: из
config-repo/pcp-request-service.yamlубраныapp.request.cleanup-cron/cleanup-retention-daysи неиспользуемый топикapp.kafka.topics.request(pcp.request.in.v1); кода под ними не было,@Value-привязок нет. - #26 — Проверить сервис заданий (
pcp-request-service) по отдельности. Готово: аудит в аудит-сервис-заданий.md — карта взаимодействий, учёт покрытия, жизненный цикл, мультиинстанс; находки RS1–RS7 (RS1/RS2 — высокий приоритет). - #9 — Расширить вправо левую панель информации на вкладке «Редактор» (цифры у границы смотрелись плохо). Готово: commit
69dfd80, рейл 15.5rem → 17rem. - #10 — Проставлять статусы слотов только после того, как план заложен на КА. Готово: commit
69dfd80, слоты занимаются поLAID_IN, а не поACCEPT. - #11 — Запретить нажимать «Подтвердить закладку», пока начало закладки в будущем. Готово: commit
69dfd80.
Добавил вручную:
убрать кнопки + - квадрат с вкладки редактор и карта с карты ибо ничего не делаютРешено иначе (2026-06-11): кнопки не убрали, а починили — они не срабатывали из-заsetPointerCaptureна контейнере карты вonPointerDown(перехватывал жест, и клик по кнопке внутри карты не доходил). Добавлен гард: нажатие в области.tgu-2d-map__zoomне запускает панорамирование/захват, плюсstopPropagationна кнопках. Теперь «+/−» (зум) и «□» (вернуть обзор) работают на всех вкладках с картой.- Линейка на карте (2026-06-11): инструмент измерения расстояния на всех вкладках с картой (Карта, Заявки, Комплексный план, Редактор, Слоты — общий
Tgu2DMapView). Иконка-линейка в правом нижнем углу рядом с «+/−/□», включается/выключается кликом (тумблер). В активном режиме клик по карте ставит вершины замера, рисуется пунктирная ломаная с подписями накопленного расстояния по дуге большого круга (haversine), в бейдже — итоговая длина; «Сброс» очищает замер, повторный клик по иконке выключает. Реализовано целиком вTgu2DMapView(состояниеrulerActive/rulerPoints), стили.tgu-2d-map__ruler*.