- новая вкладка: заявки интервала на карте, выбор заявки кликом/инспектором,
расчёт слотов покрытия, бронирование и снятие брони
- выбор заявки без выпадающего списка (не масштабируется на ~10к): все заявки
на карте, панель справа с кнопкой «Выбрать», имя выбранной заявки в тулбаре,
кнопка «Сброс», фильтр заявок по интервалу
- бронь отмечаем по ответу POST booking/request (by-request отдаёт слот в чужом
cycle-фрейме — ключ не совпадал, бейдж «бронь» не появлялся)
- подсветка и автоскролл активной строки по клику на слот (ручной scrollBy)
- меню выбора при плотном наложении слотов (SlotPickMenu); контур заявки
pickable:false, чтобы не «съедать» меню; панель не закрывается при выборе
- фильтр отображения по суткам (SlotDaySelector)
- gateway: маршрут slots (/api/pcp-slots/**)
- карточка полосы обзора (swath): КА и виток в инспекторе
Запросы tguApi.ts (platforms/plans/decision/dismiss/restore/confirm-layin)
и flight-line переведены с прокси /api/tgu-planning через умирающий
pcp-ui-service на прямые вызовы pcp-tgu-service (/api/pcp-tgu, decision и
confirm-layin — на тест-контроллер /test/tgu/*) и pcp-ballistics-service.
flight-line перенесён в ballisticsApi.ts. editorApi.ts modes переведён с
/api/current-plans на /api/pcp-mission. UX-перевод ошибок решения уже жил во
фронте (errorMessage в tguApi.ts), серверная обвязка ui-service дублировала.
Из config-repo/pcp-gateway-service.yaml снят маршрут ui-catch-all: не-матчнутый
/api/** теперь отдаёт 404 на gateway, а не уходит в pcp-ui-service.
Шаг 8 (вывод сервиса из эксплуатации: compose/helm/CI/реестр + удаление кода)
отложен до проверки репойнта на живом стенде; сервис пока задеплоен без
маршрутов. См. docs/план-перевода-с-pcp-ui-service.md и задачу #40 бэклога.
CORS-политика (allowedOriginPatterns:* + allowCredentials) переезжает в единый
globalcors на pcp-gateway-service. После сворачивания фронта на gateway браузер
ходит к бэкендам только через него (same-origin via Vite/nginx), поэтому
per-service CORS в pcp-ui-service/WebConfig.kt стал мёртвым грузом. globalcors
оставлен как страховка от cross-origin напрямую к gateway.
Откат регрессии 0ec8c30: в профиле dev datasource снова берётся из
SPRING_DATASOURCE_URL/USERNAME/PASSWORD (профиль local оставлен параметрическим).
dynamic-plan и tgu переведены на тот же механизм + добавлены helm extraEnv
с in-cluster postgres и кредами из секретов pcp-dynamic-plan-db / pcp-tgu-db.
Merge 0ec8c30 затёр ${SPRING_DATASOURCE_URL/USERNAME/PASSWORD} на конкретный
postgres в shared-профиле, из-за чего падал тест RequestServiceConfigTest
(shared datasource keeps environment placeholders) и красился CI.
Плейсхолдеры восстановлены; local-профиль остаётся с конкретным postgres.
RS1 (#29): планировщик истечения заявок — RequestExpiryService
(@Scheduled + FOR UPDATE SKIP LOCKED, мультиинстанс-safe), refreshStatus
вызывается и при создании заявки.
RS7 (#35): единый топик pcp.request.status.v1 → geoportal (COMPLETED/EXPIRED
в payload), замена orphan pcp.request.completed.v1; эмиссия из matching и
expiry через тот же outbox; backoff/next_attempt_at/cap для outbox FAILED.
RS3 (#31): DefaultErrorHandler + DeadLetterPublishingRecoverer
(DLQ pcp.route.georeference-dlq.v1), bounded retry, контрактные ошибки в DLQ.
RS6 (#34): проброс coverageRequiredPercent из API, разведение терминов
покрытие/важность.
Доки: бэклог, аудит сервиса заданий, карты взаимодействий.
RS2 (#30) отложено до #28, RS5 (#33) — до роста требований к точности.
application.yaml/yml всех сервисов и config-repo переведены с 192.168.60.x
на localhost (дефолты CONFIG_SERVER_URI и пр.); обновлены примеры в
changeIpInCurrentFolder.sh и create_pcp_databases.sh.
Базой цепочки план становится на ЗАКЛАДКЕ (LAID_IN), а не на приёмке.
После принятого плана можно отправить коррекцию — она тоже примется;
несколько выдач «в полёте» по одному КА теперь норма, а не конфликт.
- ACCEPT больше не двигает currentBasePlanId/chainVersion и не запирает
слот — только переводит план в AWAITING_LAYIN.
- Новый позитивный топик layin-confirmed + LayinConfirmedConsumer →
handleLayinConfirmed: AWAITING_LAYIN → LAID_IN, план становится базой
(chainVersion++), конкуренты на пересекающемся окне гасятся в новый
терминальный статус SUPERSEDED. Гард ±10 мин (LAYIN_MIN_SEPARATION_MINUTES):
нельзя заложить план со стартом в пределах 10 мин от уже заложенного (409).
- «В полёте» считается из статусов планов (ISSUING/WAITING_DECISION), а не
из единственного active-слота; снят гард «идёт выдача другого плана».
IssueDuePlanWorker гейтится hasBlockingInFlight, базу нового плана берёт
как latestCommittedPlanId; grace больше не двигает базу.
- PlanProjectionService якорит по самому дальнему committed-окну
{ISSUING, WAITING_DECISION, AWAITING_LAYIN, LAID_IN}.
- REJECT ставит паузу только если по КА не осталось живых кандидатов.
- CreateDraftUseCase: replace-черновик наследует ПРЕДШЕСТВЕННИКА исходного
плана (base.basePlanId), а не сам план — коррекция занимает тот же слот;
fork-forward по-прежнему ссылается на базу. Идемпотентность черновиков —
по слоту (КА + окно), а не по basePlanId.
- Тест-контроллер: POST /test/tgu/plans/{id}/confirm-layin (через BFF
pcp-ui-service) — имитация события «план заложен».
- Док PCP_TGU_PLAN_STATUS_LIFECYCLE обновлён (LAID_IN-переход, SUPERSEDED).
- Карта 2D: клик по линии (трасса КА / сеанс сброса) выбирает объект — общий
инспектор с таймлайном; MapLine.name, тип selection "line" (kind track/drop),
связь с работой через modeId; spatialIndex учитывает линии.
- Редактор (TguEditorTab/EditorPanels): единый WorkInspector для клика по карте
и по таймлайну, подсветка соответствующего включения, стили tgu-editor.css.
- config-repo: server.error.include-message=always для pcp-tgu-service и
pcp-mission-planing-service — UI показывает причину 4xx (reason/exception),
а не только HTTP-фразу.
Отказ от остаточной UTC-семантики: naive LocalDateTime трактуется как
локальное время единой зоны всех сервисов.
Backend (границы внешних систем):
- VisibilityPayloadParser, SyncSpacecraftFromNsiUseCase: ZoneOffset.UTC →
ZoneId.systemDefault() для проекции внешних Instant/ISO-с-Z в naive.
- тесты границ перевёрнуты на systemDefault (под Europe/Moscow ждут +3).
- чистка UTC-формулировок в DTO-комментариях, мёртвых ссылок на
docs/standards/DATETIME_UTC.md.
Frontend (pcp-tgu-ops-ui):
- utils/utcTime.ts → utils/localTime.ts: parseLocal/formatLocal*/toLocalIso/
toLocalInputValue с локальной семантикой (без дописывания Z и timeZone:UTC).
- замена getUTC*/Date.UTC/toISOString-сериализации на локальные аналоги.
- убраны пользовательские подписи «UTC» в редакторе/таймлайне.
- удалён utcTime.guard.test.ts (форсил обратный UTC-инвариант); тест-фикстуры
сделаны зоно-независимыми. tsc + vitest зелёные под TZ=UTC и TZ=Europe/Moscow.
Включает также прочие незакоммиченные правки рабочего дерева.
mission-planing: POST /api/missions/{id}/clone — полный клон (replace)
или по перекрытию с окном (fork-forward, [start,stop) открытый правый конец);
пустые drop-mode (все съёмки вне окна) не клонируются
tgu-service: PlannedPlanStatus.DRAFT; POST /api/plans/{planId}/draft
(CreateDraftUseCase: guard replace/fork-forward, clone mission → planId=newMissionId,
компенсация при сбое); DRAFT отфильтрован из PlanQueryService read-пути;
MissionPlaningClient + MissionPlaningProperties + mission-planing.base-url в yaml
тесты: MissionCloneTest (6 граничных кейсов предиката перекрытия),
PlanQueryServiceTest (DRAFT не видна в ответе)
- ручная выдача вынесена из TestTguController в PlanController
- assertReissuableInSlot: 422 при LAID_IN и заморозке (now >= start - layinFreezeMinutes),
409 при активной выдаче другого плана КА; перевыдача того же плана поверх своей
in-flight attempt разрешена (новая attemptNo++ замещает летящую)
- REISSUABLE_STATUSES удалён — гард по времени/статусу его заместил
- layinFreezeMinutes (деф. 10) в PlanningProperties + config-repo/application yaml
- граничные тесты в IssueDuePlanWorkerOnDemandTest, переписан PlanIssueAndDecisionFlowTest
- дизайн-док версионирования (фаза 1 отмечена сделанной)
Если план простоял в WAITING_DECISION дольше waiting-decision-grace-minutes
(по умолчанию 30) после своего начала и решения по нему так и нет — перестаём
ждать подтверждение: снимаем активную выдачу, двигаем якорь цепочки на этот
план и выдаём следующий. Статус плана не меняется (остаётся WAITING_DECISION,
не ACCEPTED) — это молчаливое продолжение цепочки, а не приёмка. Запоздалое
решение по такому плану просто пишется на attempt и не откатывает ушедшую
вперёд цепочку.
Раньше вся будущая цепочка планов материализовалась в planned_plan как
PLANNED-строки, а отдельный воркер пересчёта (RebuildPlanScheduleUseCase +
PlanRecalculationJob) держал её в актуальном состоянии и гасил выпавшие планы
в SUPERSEDED/EXPIRED. Это плодило вложенные планы, гонки пересчёта и мусор в БД.
Новая модель: в БД живёт только выданное (история навсегда), будущее — проекция,
вычисляемая по запросу из ЗРВ. Перекрывать (supersede) и протухать (expire)
больше нечего.
Бэкенд:
- Удалены TrackedPlan (entity/source/status + repo) и PlanRecalculationJob
(entity/repo/service/worker) — материализация и воркер пересчёта больше не нужны.
- Удалён RebuildPlanScheduleUseCase; будущее строит новый PlanProjectionService
поверх PlanCalculationService.
- PlanQueryService отдаёт проекцию (выданное из БД + будущее на лету).
- Новый флоу реакции на смену параметров станции: StationParamsChangedConsumer +
HandleStationParamsChangedUseCase (срочный перевыпуск через planning_state).
- Новые консьюмеры обратной связи исполнения: LayinFailedConsumer,
PlanNotAcceptedConsumer (+ DTO PlanExecutionFeedbackMessage,
StationParamsChangedMessage).
- PlannedPlanStatus: убраны мёртвые SUPERSEDED/EXPIRED; статусы выровнены под
жизненный цикл выдачи/закладки.
Миграции (ddl-auto: validate):
- V7 drop tracked_plan
- V8 drop plan_recalculation_job
- V9 planning_state: поля срочного перевыпуска
- V10 снять unique-ограничение бизнес-ключа planned_plan
- V11 удалить легаси PLANNED-строки (материализованное будущее)
- V12 удалить легаси SUPERSEDED/EXPIRED и столбец superseded_by_plan_id
Тесты: удалены JobRerunFlowTest и RebuildPlanScheduleUseCaseTest; добавлены
PlanProjectionServiceTest, PlanQueryServiceTest, IssueDuePlanWorkerOnDemandTest,
HandleStationParamsChangedUseCaseTest, VisibilityPayloadParserTest. Скорректированы
PlanIssueAndDecisionFlowTest, PlanCalculationServiceTest, VisibilityFlowTest и др.
V11/V12 необратимы — перед миграцией на проде снять дамп planned_plan.
Баллистика теперь сама берёт каталог станций у pcp-stations-service
(новый StationCatalogClient, settings.stations-service, GET /api/stations)
и строит ЗРВ по каждой реальной станции: PPI с ppiId == station.number,
поэтому RadioVisibilityAreaDTO.stationId совпадает с номером станции.
Раньше getRVA использовал одну захардкоженную фейковую станцию.
Заодно исправляет автоматический расчёт сбросов (calculateDrops),
который ходит в getRVA, и заполняет duration зоны.
Dev branch had changed datasource URLs to env vars (SPRING_DATASOURCE_URL etc.)
for k8s deployments, but route-processing uses parametric config-server URLs
for local development.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Classify Ordinis classifier transport failures and empty responses as unavailable, keep stale platform cache when possible, and expose Prometheus metrics/alerting for the outage signal.
состояние окон видимости, расписания планов, попыток выдачи и job’ов перенесено в БД;
план и попытка выдачи разделены на разные сущности;
Kafka-события теперь двигают persisted state/jobs, а не локальную память pod’а;
BPMN createSatelliteMission теперь отвечает только за одну попытку выдачи плана;
обновление слотов после принятия плана вынесено в отдельный BPMN-процесс;
workers используют DB claim/lock model и безопасны для нескольких pod’ов;
тестовый ручной API вынесен в отдельный disabled-by-default controller
- добавить properties и config values для route passport outbox worker
- включить Spring scheduling в route-processing service
- добавить транзакционный сервис claim и status transitions для outbox
- публиковать claimed outbox rows через существующий Kafka publisher
- обрабатывать publish failures по каждой row с bounded exponential backoff
- обеспечить безопасность при нескольких pod через FOR UPDATE SKIP LOCKED и lockedUntil
- добавить unit tests для claim, publish transitions, retry/fail behavior и batch-обработки worker