feat(ui): перевести фронт с pcp-ui-service на источники, снять gateway catch-all

Запросы 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 бэклога.
This commit is contained in:
Дмитрий Соловьев
2026-06-10 14:47:05 +03:00
parent 3c4ebcd7d3
commit 3765efb4fe
8 changed files with 209 additions and 57 deletions
+11 -1
View File
@@ -13,7 +13,7 @@
> Задачи #12–#25 заведены по результатам аудита. Подробности, обоснование и рекомендации
> по каждой — в [аудит-проекта-2026-06-06.md](аудит-проекта-2026-06-06.md) (ссылки на коды находок B/F/R/C).
_Обновлено: 2026-06-08_
_Обновлено: 2026-06-10_
---
@@ -43,6 +43,16 @@ _Обновлено: 2026-06-08_
- [ ] **#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](план-перевода-с-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.
- [x] Шаги 1–7: репойнт фронта на источники (`tguApi.ts``/api/pcp-tgu` + `/test/tgu/*`; flight-line → `ballisticsApi.ts`; `editorApi.ts` modes → `/api/pcp-mission`) и снятие `ui-catch-all` из gateway. Перевод ошибок уже жил во фронте (`errorMessage` в `tguApi.ts`). Typecheck чист.
- [ ] Шаг 8: вывод сервиса из эксплуатации (compose/helm/CI/config-repo/реестр `pcp.services.ui`) и удаление кода — **отложено** до проверки репойнта на живом стенде. Пока сервис задеплоен, но без маршрутов.
### Слоты (вкладка «Заявка» и «Редактор»)
- [ ] **#41** — Запрос слотового покрытия по заявке. На вкладке «Заявка» (и в «Редакторе») запрашивать у slots-service слотовое покрытие заявки — какие слоты КА покрывают AOI заявки на её интервале. Источник — slots-service (минуя умирающий `pcp-ui-service`, ср. #40). Уточнить контракт ответа (слоты + связь с ячейками/покрытием заявки) и где он отрисовывается.
- [ ] **#42** — Бронирование слотов по заявке. С вкладки «Заявка» (и/или «Редактор») бронировать выбранные слоты под заявку через slots-service. Связано с показом забронированных слотов (#3) и пересчётом комплексного плана при бронировании (#6). Уточнить: единичное/пакетное бронирование, идемпотентность, что возвращается после брони.
- [ ] **#43** — Уточнение статуса слота. На вкладке «Заявка» (и «Редактор») показывать/обновлять актуальный статус слота (свободен / забронирован / занят по заложенному плану). Учитывать, что слоты занимаются по `LAID_IN`, а не по `ACCEPT` (ср. #10). Источник статусов — slots-service; согласовать с #41/#42.
### Баги
- [ ] **#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 подтвердить у владельца каталога/баллистиков перед правкой._
@@ -0,0 +1,116 @@
# План перевода запросов с pcp-ui-service на источники
_Составлено: 2026-06-10. Связано с задачей #40 бэклога [бэклог-pcp-tgu-ops-ui.md](бэклог-pcp-tgu-ops-ui.md)._
## Зачем
`pcp-ui-service` — наследие старого server-rendered UI (Thymeleaf-страницы + REST-прокси-обвязка
над доменными сервисами). Новый фронт `pcp-tgu-ui-service` ходит к бэкендам через
`pcp-gateway-service`, и большинство его клиентов уже бьют в доменные сервисы напрямую
(`/api/pcp-tgu`, `/api/pcp-mission`, `/api/pcp-ballistics`, `/api/pcp-complex`,
`/api/pcp-request`). `pcp-ui-service` остаётся в маршрутизации только как **catch-all**
(`ui-catch-all`, `order:1` в `config-repo/pcp-gateway-service.yaml`) для двух поверхностей.
Цель — увести эти две поверхности на источники и вывести `pcp-ui-service` из эксплуатации.
## Кто ещё зависит от pcp-ui-service
Проверено (grep по `config-repo/`, `services/`, `libs/`): **никто, кроме фронта и catch-all
gateway.** Остальные ссылки — это самопрописка сервиса в Spring Boot Admin
(`config-repo/pcp-ui-service.yaml`) и комментарии в `pcp-types-lib` про то, что обогащение
DTO «исторически делалось в ui-service». Восток-запад (межсервис) сервис не вызывает никто.
## Карта запросов фронта, идущих через pcp-ui-service
Через catch-all `pcp-ui-service` сейчас проходят запросы только из **двух** файлов фронта.
Всё остальное (`ballisticsApi`, `complexPlanApi`, `missionPlanningApi`, `requestApi`,
`tguPlanApi`, `stationsApi`) уже ходит мимо ui-service.
### 1. `src/api/tguApi.ts` → `/api/tgu-planning/*` (+ flight-line)
Контроллер `TguPlanningRestController` (`services/pcp-ui-service/.../controller/TguPlanningController.kt`)
проксирует через `TguPlanningService`/`BallisticsService`. Источники и реальные пути:
| Запрос фронта (через ui-service) | HTTP | Реальный источник | Путь на источнике |
|--------------------------------------------------------------|------|------------------------------|----------------------------------------------------|
| `/api/tgu-planning/platforms` | GET | pcp-tgu-service | `/api/platforms` |
| `/api/tgu-planning/plans?historyDays` | GET | pcp-tgu-service | `/api/plans?historyDays` |
| `/api/tgu-planning/spacecraft/{id}/plans?historyDays` | GET | pcp-tgu-service | `/api/plans/{spacecraftId}?historyDays` |
| `/api/tgu-planning/plans/{planId}/decision?decision&reason` | POST | pcp-tgu-service (**TEST**) | `/test/tgu/plans/{planId}/decision` |
| `/api/tgu-planning/plans/{planId}/dismiss` | POST | pcp-tgu-service | `/api/plans/{planId}/dismiss` |
| `/api/tgu-planning/plans/{planId}/restore` | POST | pcp-tgu-service | `/api/plans/{planId}/restore` |
| `/api/tgu-planning/plans/{planId}/confirm-layin?reason` | POST | pcp-tgu-service (**TEST**) | `/test/tgu/plans/{planId}/confirm-layin` |
| `/api/tgu-planning/spacecraft/{noradId}/flight-line` | GET | pcp-ballistics-service | `/api/satellites/{noradId}/flight-line` (+интервал)|
**Что ui-service делает поверх прокси (нельзя потерять при переезде):**
- **Перевод ошибок в человекочитаемые сообщения** для решения по плану (`decision`):
404 → «План не найден.» / «Test controller недоступен или отключён.»; 409 → «План не
является активным планом, ожидающим решения…»; 5xx → «Test controller недоступен…».
Сетевые сбои (`WebClientRequestException`) → 503 «Test controller недоступен…».
При переезде эту UX-обвязку нужно перенести во фронт (`tguApi.ts`/общий `http.ts`, см. #17),
иначе оператор увидит сырой 4xx/5xx.
- `decision` и `confirm-layin` идут в **тест-контроллер** tgu (`/test/tgu/...`), гейтится
`tgu.test-controller.enabled` в `config-repo/pcp-tgu-service.yaml`. Это MVP-канал
подтверждений (ср. #13). При переезде поведение не меняется — но прод-канал решений
остаётся отдельным вопросом.
- DTO (`TguPlanResponse`, `TguPlatformResponse`) проксируются **как есть** (`bodyToFlux`
того же типа) — форма ответа источника и ui-service идентична, фронт не сломается по схеме.
### 2. `src/features/tgu-editor/editorApi.ts` → `/api/current-plans/missions/{planId}/modes`
| Запрос фронта (через ui-service) | HTTP | Реальный источник | Путь на источнике |
|---------------------------------------------------|------|-----------------------------|------------------------------------|
| `/api/current-plans/missions/{planId}/modes` | GET | pcp-mission-planing-service | `/api/missions/{missionId}/modes` |
`CurrentPlansController.modes``MissionService.modesByMission``${settings.mission-service}`
(= `pcp.services.mission` = pcp-mission-planing-service), прямой проброс `List<ModeResponseDTO>`.
Фронтовый `missionPlanningApi.ts` уже ходит в этот же сервис через `/api/pcp-mission` — endpoint
переезжает туда же.
## План перевода (порядок шагов)
> **Статус (2026-06-10): шаги 1–7 выполнены** — фронт ходит в источники напрямую, catch-all
> снят. Шаг 8 (вывод сервиса из эксплуатации) отложен до проверки репойнта на живом стенде;
> до этого pcp-ui-service остаётся задеплоен, но без маршрутов (безвреден). Перевод ошибок
> решения уже жил во фронте (`errorMessage` в `tguApi.ts`) — серверная обвязка ui-service
> дублировала его, при прямом обращении к tgu ничего не потеряно.
1. **flight-line → `/api/pcp-ballistics`.** Перенести `fetchFlightLine` в `ballisticsApi.ts`
(там уже `BASE=/api/pcp-ballistics`), путь `/api/satellites/{noradId}/flight-line` + интервал
`time_start`/`time_stop`. Сверить формат точек `FlightLinePoint` с тем, что отдаёт
баллистика напрямую (ui-service отдавал `collectList().block()` того же flux — форма та же).
2. **platforms / plans / spacecraft-plans → `/api/pcp-tgu`.** Перепривязать GET-методы
`tguApi.ts` на `/api/pcp-tgu` + `/api/platforms`, `/api/plans`, `/api/plans/{id}`.
`tguPlanApi.ts` уже использует этот префикс — переиспользовать его конвенции/`fetchJson`.
3. **dismiss / restore → `/api/pcp-tgu`** на `/api/plans/{id}/dismiss|restore` (реальный API).
4. **decision / confirm-layin → `/api/pcp-tgu`** на `/test/tgu/plans/{id}/decision|confirm-layin`.
**Одновременно** перенести во фронт перевод ошибок (карта статусов → сообщения выше).
Учесть зависимость от `tgu.test-controller.enabled`.
5. **modes → `/api/pcp-mission`.** В `editorApi.ts` заменить
`/api/current-plans/missions/{id}/modes` на `/api/pcp-mission/api/missions/{id}/modes`
(через общий клиент/`apiFetch`). Свериться с `missionPlanningApi.ts`.
6. **Vite/nginx.** Проверить, что `/api/pcp-*` и `/api/pcp-mission` уже проксируются на gateway
(да — единый `/api` → gateway). Спец-обработка `/api/tgu-planning`/`/api/current-plans`
не нужна.
7. **Снять catch-all.** Удалить маршрут `ui-catch-all` из `config-repo/pcp-gateway-service.yaml`
(после того как фронт перестал слать `/api/tgu-planning` и `/api/current-plans`). Проверить,
что никакой `/api/**` больше не должен попадать в ui-service.
8. **Вывод сервиса из эксплуатации.** Убрать `pcp-ui-service` из gateway-реестра-потребителей,
`docker-compose`/helm/CI (`.gitlab/ci/pcp-ui-service.yml`, include в `.gitlab-ci.yml`,
`changes:`-списки), `config-repo/pcp-ui-service.yaml`, Spring Boot Admin. Код сервиса —
архивировать/удалить отдельным шагом после стабилизации.
## Риски и на что смотреть
- **Потеря UX-перевода ошибок решения** — главный функциональный риск. Перенести в фронт
вместе с шагом 4, не откладывать.
- **Тест-контроллер как канал решений** — `decision`/`confirm-layin` остаются на `/test/tgu/*`.
Это сознательный MVP-срез; прод-канал — отдельная задача (ср. #13).
- **Мёртвые поверхности ui-service.** Контроллеры `bookings`, `catalog`, `rva`, `dynamic-plan`,
`tle-analysis`, `tle-comparison`, `com-plan`, `requests`, `map`, `satellites/pdcm`,
`RvaController` и server-rendered страницы новый фронт **не вызывает** — они умирают вместе
с сервисом. Перед удалением кода ещё раз убедиться, что у них нет внешних потребителей
(геопортал и т.п.).
- **Обогащение DTO в ui-service** (`BookedSlotDetailsDTO`: имя/тип КА добавлялись в ui-service).
Если такая обогащённая поверхность понадобится новому фронту — она должна переехать в
источник или во фронт, а не остаться «висеть» в умирающем сервисе. Сейчас новый фронт её
не использует.