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:
@@ -62,12 +62,10 @@ spring:
|
|||||||
uri: ${pcp.services.stations}
|
uri: ${pcp.services.stations}
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/api/stations/**
|
- Path=/api/stations/**
|
||||||
# --- Общий catch-all /api/** -> ui-service. Матчится ПОСЛЕДНИМ (order выше).
|
# Catch-all /api/** -> pcp-ui-service СНЯТ: фронт переведён на источники напрямую
|
||||||
- id: ui-catch-all
|
# (tgu/баллистика/mission), ui-service выводится из эксплуатации. См.
|
||||||
uri: ${pcp.services.ui}
|
# docs/план-перевода-с-pcp-ui-service.md и задачу #40 бэклога. Любой не-матчнутый
|
||||||
predicates:
|
# /api/** теперь честно отдаёт 404 на gateway, а не молча уходит в умирающий сервис.
|
||||||
- Path=/api/**
|
|
||||||
order: 1
|
|
||||||
|
|
||||||
# Валидация JWT на edge. По умолчанию ВЫКЛЮЧЕНА (anonymous, как сейчас работает фронт).
|
# Валидация JWT на edge. По умолчанию ВЫКЛЮЧЕНА (anonymous, как сейчас работает фронт).
|
||||||
# Для закрытого контура: GATEWAY_JWT_ENABLED=true И задать issuer через env
|
# Для закрытого контура: GATEWAY_JWT_ENABLED=true И задать issuer через env
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
> Задачи #12–#25 заведены по результатам аудита. Подробности, обоснование и рекомендации
|
> Задачи #12–#25 заведены по результатам аудита. Подробности, обоснование и рекомендации
|
||||||
> по каждой — в [аудит-проекта-2026-06-06.md](аудит-проекта-2026-06-06.md) (ссылки на коды находок B/F/R/C).
|
> по каждой — в [аудит-проекта-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** — Проверить сервис получения паспорта маршрута по отдельности.
|
- [ ] **#27** — Проверить сервис получения паспорта маршрута по отдельности.
|
||||||
- [ ] **#28** — Проверить связь сервиса заданий и сервиса получения паспорта маршрута между собой.
|
- [ ] **#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** — Ошибка при попытке принять план на вкладке «Планы».
|
- [ ] **#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 подтвердить у владельца каталога/баллистиков перед правкой._
|
- [ ] **#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).
|
||||||
|
Если такая обогащённая поверхность понадобится новому фронту — она должна переехать в
|
||||||
|
источник или во фронт, а не остаться «висеть» в умирающем сервисе. Сейчас новый фронт её
|
||||||
|
не использует.
|
||||||
@@ -4,6 +4,64 @@ import { apiFetch } from './apiFetch'
|
|||||||
|
|
||||||
const BASE = '/api/pcp-ballistics'
|
const BASE = '/api/pcp-ballistics'
|
||||||
|
|
||||||
|
export type FlightLinePoint = {
|
||||||
|
time: string
|
||||||
|
revolution: number
|
||||||
|
lat: number
|
||||||
|
long: number
|
||||||
|
latLeft: number
|
||||||
|
longLeft: number
|
||||||
|
latInnerLeft: number
|
||||||
|
longInnerLeft: number
|
||||||
|
latInnerRight: number
|
||||||
|
longInnerRight: number
|
||||||
|
latRight: number
|
||||||
|
longRight: number
|
||||||
|
revSign: 'ASC' | 'DESC'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Трасса полёта КА (flight-line) на интервале для отрисовки на карте/таймлайне из
|
||||||
|
* pcp-ballistics-service (GET /api/satellites/{norad}/flight-line). Шаг трассы 60 с.
|
||||||
|
* @param noradId NORAD-идентификатор КА.
|
||||||
|
* @param fromMs Начало интервала, мс от эпохи.
|
||||||
|
* @param toMs Конец интервала, мс от эпохи.
|
||||||
|
*
|
||||||
|
* @returns Точки трассы полёта на интервале.
|
||||||
|
*/
|
||||||
|
export async function fetchFlightLine(
|
||||||
|
noradId: string,
|
||||||
|
fromMs: number,
|
||||||
|
toMs: number,
|
||||||
|
): Promise<FlightLinePoint[]> {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
time_start: toLocalDateTime(fromMs),
|
||||||
|
time_stop: toLocalDateTime(toMs),
|
||||||
|
})
|
||||||
|
const url = `${BASE}/api/satellites/${encodeURIComponent(noradId)}/flight-line?${params.toString()}`
|
||||||
|
|
||||||
|
let response: Response
|
||||||
|
try {
|
||||||
|
response = await apiFetch(url, { headers: { Accept: 'application/json' } })
|
||||||
|
} catch {
|
||||||
|
throw new TguApiError('Не удалось подключиться к сервису баллистики.', 'network')
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = await response.text()
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new TguApiError(
|
||||||
|
extractErrorText(text) || 'Ошибка сервиса баллистики.',
|
||||||
|
'http',
|
||||||
|
response.status,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!text.trim()) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(text) as FlightLinePoint[]
|
||||||
|
}
|
||||||
|
|
||||||
export type PointVisibilityParam = {
|
export type PointVisibilityParam = {
|
||||||
/** NORAD-идентификатор КА. */
|
/** NORAD-идентификатор КА. */
|
||||||
noradId: number
|
noradId: number
|
||||||
|
|||||||
@@ -5,53 +5,21 @@ import {
|
|||||||
type TguPlanDecisionMessage,
|
type TguPlanDecisionMessage,
|
||||||
type TguPlatform,
|
type TguPlatform,
|
||||||
} from '../model/tguTypes'
|
} from '../model/tguTypes'
|
||||||
import { toLocalIso } from '../utils/localTime'
|
|
||||||
import { apiFetch } from './apiFetch'
|
import { apiFetch } from './apiFetch'
|
||||||
|
|
||||||
export type FlightLinePoint = {
|
|
||||||
time: string
|
|
||||||
revolution: number
|
|
||||||
lat: number
|
|
||||||
long: number
|
|
||||||
latLeft: number
|
|
||||||
longLeft: number
|
|
||||||
latInnerLeft: number
|
|
||||||
longInnerLeft: number
|
|
||||||
latInnerRight: number
|
|
||||||
longInnerRight: number
|
|
||||||
latRight: number
|
|
||||||
longRight: number
|
|
||||||
revSign: 'ASC' | 'DESC'
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Трасса полёта КА (flight-line) на интервале для отрисовки на карте/таймлайне.
|
* Запросы планирования ТГУ идут напрямую в pcp-tgu-service через gateway (`/api/pcp-tgu` →
|
||||||
* @param noradId NORAD-идентификатор КА.
|
* :7011), как и остальные tgu-вызовы (см. tguPlanApi.ts). Решение/подтверждение закладки —
|
||||||
* @param fromMs Начало интервала, мс от эпохи.
|
* на тест-контроллере ТГУ (`/test/tgu/*`, гейт `tgu.test-controller.enabled`, MVP-канал).
|
||||||
* @param toMs Конец интервала, мс от эпохи.
|
|
||||||
*
|
|
||||||
* @returns Точки трассы полёта на интервале.
|
|
||||||
*/
|
*/
|
||||||
export async function fetchFlightLine(
|
const BASE = '/api/pcp-tgu'
|
||||||
noradId: string,
|
|
||||||
fromMs: number,
|
|
||||||
toMs: number,
|
|
||||||
): Promise<FlightLinePoint[]> {
|
|
||||||
const query = new URLSearchParams({
|
|
||||||
time_start: toLocalIso(fromMs),
|
|
||||||
time_stop: toLocalIso(toMs),
|
|
||||||
})
|
|
||||||
return fetchJson<FlightLinePoint[]>(
|
|
||||||
`/api/tgu-planning/spacecraft/${encodeURIComponent(noradId)}/flight-line?${query}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Список платформ (КА) ТГУ.
|
* Список платформ (КА) ТГУ.
|
||||||
* @returns Платформы планирования ТГУ.
|
* @returns Платформы планирования ТГУ.
|
||||||
*/
|
*/
|
||||||
export async function fetchPlatforms(): Promise<TguPlatform[]> {
|
export async function fetchPlatforms(): Promise<TguPlatform[]> {
|
||||||
return fetchJson<TguPlatform[]>('/api/tgu-planning/platforms')
|
return fetchJson<TguPlatform[]>(`${BASE}/api/platforms`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,8 +32,8 @@ export async function fetchPlatforms(): Promise<TguPlatform[]> {
|
|||||||
export async function fetchPlans(historyDays: number, spacecraftId?: string): Promise<TguPlan[]> {
|
export async function fetchPlans(historyDays: number, spacecraftId?: string): Promise<TguPlan[]> {
|
||||||
const query = new URLSearchParams({ historyDays: String(historyDays) })
|
const query = new URLSearchParams({ historyDays: String(historyDays) })
|
||||||
const path = spacecraftId
|
const path = spacecraftId
|
||||||
? `/api/tgu-planning/spacecraft/${encodeURIComponent(spacecraftId)}/plans?${query}`
|
? `${BASE}/api/plans/${encodeURIComponent(spacecraftId)}?${query}`
|
||||||
: `/api/tgu-planning/plans?${query}`
|
: `${BASE}/api/plans?${query}`
|
||||||
|
|
||||||
return fetchJson<TguPlan[]>(path)
|
return fetchJson<TguPlan[]>(path)
|
||||||
}
|
}
|
||||||
@@ -89,7 +57,7 @@ export async function sendPlanDecision(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return fetchJson<TguPlanDecisionMessage>(
|
return fetchJson<TguPlanDecisionMessage>(
|
||||||
`/api/tgu-planning/plans/${encodeURIComponent(planId)}/decision?${query}`,
|
`${BASE}/test/tgu/plans/${encodeURIComponent(planId)}/decision?${query}`,
|
||||||
{ method: 'POST' },
|
{ method: 'POST' },
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
@@ -101,7 +69,7 @@ export async function sendPlanDecision(
|
|||||||
* @param planId Идентификатор плана.
|
* @param planId Идентификатор плана.
|
||||||
*/
|
*/
|
||||||
export async function dismissPlan(planId: string): Promise<void> {
|
export async function dismissPlan(planId: string): Promise<void> {
|
||||||
await fetchJson<void>(`/api/tgu-planning/plans/${encodeURIComponent(planId)}/dismiss`, {
|
await fetchJson<void>(`${BASE}/api/plans/${encodeURIComponent(planId)}/dismiss`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -111,7 +79,7 @@ export async function dismissPlan(planId: string): Promise<void> {
|
|||||||
* @param planId Идентификатор плана.
|
* @param planId Идентификатор плана.
|
||||||
*/
|
*/
|
||||||
export async function restorePlan(planId: string): Promise<void> {
|
export async function restorePlan(planId: string): Promise<void> {
|
||||||
await fetchJson<void>(`/api/tgu-planning/plans/${encodeURIComponent(planId)}/restore`, {
|
await fetchJson<void>(`${BASE}/api/plans/${encodeURIComponent(planId)}/restore`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -122,7 +90,7 @@ export async function restorePlan(planId: string): Promise<void> {
|
|||||||
* @param planId Идентификатор плана.
|
* @param planId Идентификатор плана.
|
||||||
*/
|
*/
|
||||||
export async function confirmPlanLayin(planId: string): Promise<void> {
|
export async function confirmPlanLayin(planId: string): Promise<void> {
|
||||||
await fetchJson<void>(`/api/tgu-planning/plans/${encodeURIComponent(planId)}/confirm-layin`, {
|
await fetchJson<void>(`${BASE}/test/tgu/plans/${encodeURIComponent(planId)}/confirm-layin`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,10 @@
|
|||||||
Возвращает { env, domain, sched } в том же виде, что buildEnv() прототипа,
|
Возвращает { env, domain, sched } в том же виде, что buildEnv() прототипа,
|
||||||
так что ConstellationDashboard рендерит реальные данные без изменений разметки.
|
так что ConstellationDashboard рендерит реальные данные без изменений разметки.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
import { fetchPlatforms, fetchFlightLine, fetchPlans, type FlightLinePoint } from "../../api/tguApi";
|
import { fetchPlatforms, fetchPlans } from "../../api/tguApi";
|
||||||
import { fetchStations, type StationDto } from "../../api/stationsApi";
|
import { fetchStations, type StationDto } from "../../api/stationsApi";
|
||||||
import { fetchRequestsForMap } from "../../api/requestApi";
|
import { fetchRequestsForMap } from "../../api/requestApi";
|
||||||
import { fetchSatelliteRva } from "../../api/ballisticsApi";
|
import { fetchSatelliteRva, fetchFlightLine, type FlightLinePoint } from "../../api/ballisticsApi";
|
||||||
import { fetchPlanModes, type PlanMode } from "../tgu-editor/editorApi";
|
import { fetchPlanModes, type PlanMode } from "../tgu-editor/editorApi";
|
||||||
import type { TguPlatform, TguPlan } from "../../model/tguTypes";
|
import type { TguPlatform, TguPlan } from "../../model/tguTypes";
|
||||||
import type { RequestMapItem } from "../../model/requestTypes";
|
import type { RequestMapItem } from "../../model/requestTypes";
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { useEffect, useMemo, useRef, useState, useCallback, type MutableRefObject } from "react";
|
import { useEffect, useMemo, useRef, useState, useCallback, type MutableRefObject } from "react";
|
||||||
import { fetchRequestsForMap } from "../../api/requestApi";
|
import { fetchRequestsForMap } from "../../api/requestApi";
|
||||||
import { fetchStations, type StationDto } from "../../api/stationsApi";
|
import { fetchStations, type StationDto } from "../../api/stationsApi";
|
||||||
import { fetchFlightLine, type FlightLinePoint } from "../../api/tguApi";
|
|
||||||
import type { RequestMapItem } from "../../model/requestTypes";
|
import type { RequestMapItem } from "../../model/requestTypes";
|
||||||
import { stationPasses, type OrbitPassInfo, type PassRange } from "../tgu-map-2d/geometry/passes";
|
import { stationPasses, type OrbitPassInfo, type PassRange } from "../tgu-map-2d/geometry/passes";
|
||||||
import { fetchPointVisibility, fetchSatelliteRva, type PointVisibilityParam, type RvaZone } from "../../api/ballisticsApi";
|
import { fetchPointVisibility, fetchSatelliteRva, fetchFlightLine, type FlightLinePoint, type PointVisibilityParam, type RvaZone } from "../../api/ballisticsApi";
|
||||||
import {
|
import {
|
||||||
buildSurveyRoute,
|
buildSurveyRoute,
|
||||||
calculateDrops,
|
calculateDrops,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { apiFetch } from "../../api/apiFetch";
|
||||||
|
|
||||||
export type PlanSurveyMode = {
|
export type PlanSurveyMode = {
|
||||||
id: number;
|
id: number;
|
||||||
planId: number;
|
planId: number;
|
||||||
@@ -30,9 +32,10 @@ export type PlanDropMode = {
|
|||||||
export type PlanMode = PlanSurveyMode | PlanDropMode;
|
export type PlanMode = PlanSurveyMode | PlanDropMode;
|
||||||
|
|
||||||
export async function fetchPlanModes(planId: string): Promise<PlanMode[]> {
|
export async function fetchPlanModes(planId: string): Promise<PlanMode[]> {
|
||||||
const response = await fetch(`/api/current-plans/missions/${encodeURIComponent(planId)}/modes`, {
|
const response = await apiFetch(
|
||||||
headers: { Accept: "application/json" }
|
`/api/pcp-mission/api/missions/${encodeURIComponent(planId)}/modes`,
|
||||||
});
|
{ headers: { Accept: "application/json" } }
|
||||||
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Ошибка загрузки включений плана: ${response.status}`);
|
throw new Error(`Ошибка загрузки включений плана: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user