pcp-tgu-service: миграция с WebFlux на servlet/MVC
WebFlux в tgu был фиктивным: блокирующая JPA, контроллеры оборачивали
блокирующие вызовы в Mono.fromCallable{}.subscribeOn(boundedElastic()),
WebClient .block()-ался сразу. Единственный WebFlux-only сервис флота.
- build: подключён pcp-types-lib (тянет starter-web → servlet), springdoc
webflux-ui → webmvc-ui; starter-webflux оставлен только ради WebClient
- контроллеры возвращают значения напрямую (без Mono-обёрток)
- удалено локальное Jackson-2 зеркало PcpTimeModule.kt → модуль из либы
в CamundaJacksonConfig; Jackson-3 зеркало остаётся (Boot 4 держит
Jackson 3 на HTTP-границе и для MVC), WebFluxJacksonConfig → RestJacksonConfig
This commit is contained in:
@@ -65,17 +65,16 @@
|
||||
`findAndRegisterModules()` (напр. `pcp-request-service`).
|
||||
- Точечная регистрация в мапперах, что обходят оба: `pcp-dynamic-plan-service/JacksonConfig`.
|
||||
|
||||
**Особый случай — pcp-tgu-service.** Он не зависит от pcp-types-lib и работает на WebFlux
|
||||
(подключение либы притянуло бы `spring-boot-starter-web`/MVC и переключило тип приложения на
|
||||
сервлетный). Поэтому в tgu лежат **два локальных зеркала** (поведение обязано совпадать с
|
||||
библиотечным, правки синхронизировать), покрывающих обе Jackson-среды сервиса:
|
||||
- `config/PcpTimeModule.kt` (Jackson 2) — зарегистрирован в `CamundaJacksonConfig`, покрывает
|
||||
Camunda/внутренний парсинг;
|
||||
- `config/PcpTimeModuleV3.kt` (**Jackson 3**, `tools.jackson.*`) — REST-кодеки WebFlux, которые
|
||||
Boot 4 авто-конфигурирует на Jackson 3 `JsonMapper`. Регистрируется бином
|
||||
`tools.jackson.databind.JacksonModule` (`WebFluxJacksonConfig`); `JacksonAutoConfiguration`
|
||||
собирает все такие бины в общий `JsonMapper` (`builder.addModules(...)`). Тем самым входящий
|
||||
REST-JSON tgu тоже читается толерантно к обоим форматам.
|
||||
**Особый случай — pcp-tgu-service.** Сервис мигрирован с WebFlux на **servlet/MVC** (см. ниже
|
||||
«Миграция tgu на MVC») и теперь зависит от pcp-types-lib, как остальной флот. Но из-за Boot 4 на
|
||||
HTTP-границе всё равно работает **Jackson 3**, поэтому время покрывается двумя путями:
|
||||
- Jackson-2 модуль **из либы** (`pcp_types_lib.utils.json.PcpTimeModule`) — явно зарегистрирован в
|
||||
ручном маппере `CamundaJacksonConfig` (Camunda/внутренний парсинг); своего зеркала больше нет.
|
||||
- `config/PcpTimeModuleV3.kt` (**Jackson 3**, `tools.jackson.*`) — REST HTTP message converters (MVC),
|
||||
которые Boot 4 авто-конфигурирует на Jackson 3 `JsonMapper`. Регистрируется бином
|
||||
`tools.jackson.databind.JacksonModule` (`RestJacksonConfig`); `JacksonAutoConfiguration` собирает
|
||||
все такие бины в общий `JsonMapper` (`builder.addModules(...)`). Лежит локально, т.к. либа даёт
|
||||
только Jackson-2 модуль; поведение обязано совпадать с библиотечным, правки синхронизировать.
|
||||
|
||||
Контракт слоя закреплён `PcpTimeModuleTest` (pcp-types-lib): оба формата → один и тот же UTC-момент,
|
||||
плюс проверка авто-подхвата через SPI.
|
||||
@@ -139,8 +138,8 @@ mission-planing) используют тот же дефолтный Jackson-к
|
||||
синхронно; фронт `parseUtc` совместим. Контракт закреплён тестами выше.
|
||||
|
||||
- **Слой толерантности (`PcpTimeModule`) — раскатан по флоту.** 12 сервисов на pcp-types-lib
|
||||
получают его авто-конфигурацией/SPI; dynamic-plan — точечно; tgu — двумя локальными зеркалами
|
||||
(Jackson-2 для Camunda и Jackson-3 для REST-кодеков WebFlux). Это включатель: дальнейший флип
|
||||
получают его авто-конфигурацией/SPI; dynamic-plan — точечно; tgu — модулем из либы (Camunda,
|
||||
Jackson 2) плюс локальное Jackson-3 зеркало для REST-границы MVC. Это включатель: дальнейший флип
|
||||
продюсеров на `Z` больше не требует синхронного деплоя.
|
||||
|
||||
После толерантности продюсеров можно флипать **в любом порядке**. Кандидаты: остальные naive-DTO
|
||||
@@ -155,16 +154,25 @@ ui-service, ballistics, complex-mission, dynamic-plan, slots, coverage-scheme.
|
||||
`…+00:00`. **Инстант не изменился** (тот же момент), но стенные часы теперь совпадают с UTC, поэтому
|
||||
потребитель, дропающий зону, получает корректное время. Закреплено `LocalDateTimeTimeZoneSerializerTest`.
|
||||
|
||||
### Исправленные баги формата
|
||||
|
||||
- **pcp-tgu-service REST-codec на Jackson 3** — теперь толерантен (см. 2bis). Добавлен Jackson-3
|
||||
- **pcp-tgu-service REST-граница на Jackson 3** — теперь толерантна (см. 2bis). Добавлен Jackson-3
|
||||
вариант модуля `config/PcpTimeModuleV3.kt` и его регистрация бином `JacksonModule`
|
||||
(`config/WebFluxJacksonConfig.kt`); входящий REST-JSON WebFlux читается в обоих форматах
|
||||
(naive=UTC и `Z`). Закреплено `PcpTimeModuleV3Test`.
|
||||
(`config/RestJacksonConfig.kt`); входящий REST-JSON читается в обоих форматах (naive=UTC и `Z`).
|
||||
Закреплено `PcpTimeModuleV3Test`.
|
||||
|
||||
### Миграция tgu на MVC
|
||||
|
||||
- **pcp-tgu-service: WebFlux → servlet/MVC.** Сервис не имел реального реактивного кода — JPA
|
||||
блокирующая, контроллеры оборачивали блокирующие вызовы в `Mono.fromCallable{}.subscribeOn(
|
||||
boundedElastic())`, WebClient-вызовы `.block()`-ались сразу. Это был единственный WebFlux-only
|
||||
сервис флота. Переведён на MVC: подключён pcp-types-lib (тянет `starter-web` → тип приложения
|
||||
servlet), `springdoc` → `webmvc-ui`, контроллеры возвращают значения напрямую (без `Mono`).
|
||||
`starter-webflux` оставлен только ради WebClient. Побочный эффект для времени: убрано локальное
|
||||
Jackson-2 зеркало (`config/PcpTimeModule.kt`) в пользу модуля из либы; Jackson-3 зеркало остаётся
|
||||
(Boot 4 держит Jackson 3 на HTTP-границе и для MVC).
|
||||
|
||||
### Остаток (под-задачи)
|
||||
|
||||
- Обе Jackson-среды tgu теперь толерантны; отдельных остатков по чтению времени нет. При миграции
|
||||
- Обе Jackson-среды tgu толерантны; отдельных остатков по чтению времени нет. При миграции
|
||||
оставшихся продьюсеров на `Z` (раздел 4) добавлять сериализационные тесты контракта.
|
||||
|
||||
## 5. Правило для нового кода
|
||||
|
||||
@@ -17,11 +17,17 @@ java {
|
||||
|
||||
dependencies {
|
||||
|
||||
// Общие типы/слой толерантности времени; тянет spring-boot-starter-web (servlet/MVC),
|
||||
// из-за чего приложение запускается как сервлетное (MVC), а не WebFlux.
|
||||
implementation(project(":libs:pcp-types-lib"))
|
||||
|
||||
// Spring Boot
|
||||
// starter-webflux оставлен только ради WebClient (внешние HTTP-клиенты, см. integration/api);
|
||||
// тип приложения — сервлетный (MVC), т.к. в classpath есть spring-webmvc из starter-web выше.
|
||||
implementation("org.springframework.boot:spring-boot-starter-webflux")
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
runtimeOnly("io.micrometer:micrometer-registry-prometheus")
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webflux-ui:3.0.0")
|
||||
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:${property("versions.open-api")}")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-flyway")
|
||||
implementation("org.flywaydb:flyway-database-postgresql")
|
||||
|
||||
+3
-2
@@ -10,6 +10,7 @@ import io.camunda.client.api.JsonMapper as CamundaJsonMapper
|
||||
import io.camunda.client.impl.CamundaObjectMapper
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import space.nstart.pcp.pcp_types_lib.utils.json.PcpTimeModule
|
||||
|
||||
@Configuration
|
||||
class CamundaJacksonConfig {
|
||||
@@ -25,8 +26,8 @@ class CamundaJacksonConfig {
|
||||
.addModule(KotlinModule.Builder().build())
|
||||
.addModule(JavaTimeModule())
|
||||
// Слой толерантности времени (читать naive=UTC и Z) — см. docs/standards/DATETIME_UTC.md.
|
||||
// Покрывает Jackson-2 путь (Camunda/внутренний парсинг). REST-codec на Jackson 3
|
||||
// (WebFlux auto-config) покрыт близнецом PcpTimeModuleV3 (см. WebFluxJacksonConfig).
|
||||
// Модуль из pcp-types-lib; покрывает Jackson-2 путь (этот ручной маппер для Camunda).
|
||||
// REST-границу (Jackson 3) покрывает PcpTimeModuleV3 (см. RestJacksonConfig).
|
||||
.addModule(PcpTimeModule())
|
||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||
.build()
|
||||
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
package space.nstart.pcp_tgu_service.config
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonToken
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeParseException
|
||||
|
||||
/**
|
||||
* Локальное зеркало слоя толерантности времени из libs/pcp-types-lib
|
||||
* (`space.nstart.pcp.pcp_types_lib.utils.json.PcpTimeModule`).
|
||||
*
|
||||
* Канонический источник — там; здесь копия, потому что pcp-tgu-service работает на WebFlux,
|
||||
* а pcp-types-lib тянет `spring-boot-starter-web` (MVC/Tomcat) и переключил бы тип приложения
|
||||
* на сервлетный. Поведение обязано совпадать с библиотечным; правки синхронизировать.
|
||||
*
|
||||
* Любой потребитель с этим модулем читает время в обоих форматах на проводе (naive=UTC и `Z`/офсет),
|
||||
* нормализуя к UTC. Трогает только десериализацию. См. docs/standards/DATETIME_UTC.md.
|
||||
*/
|
||||
class PcpTimeModule : SimpleModule(NAME) {
|
||||
|
||||
init {
|
||||
addDeserializer(LocalDateTime::class.java, TolerantLocalDateTimeDeserializer())
|
||||
addDeserializer(OffsetDateTime::class.java, TolerantOffsetDateTimeDeserializer())
|
||||
addDeserializer(Instant::class.java, TolerantInstantDeserializer())
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "PcpTimeModule"
|
||||
}
|
||||
}
|
||||
|
||||
private class TolerantLocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() {
|
||||
|
||||
private val default = LocalDateTimeDeserializer.INSTANCE
|
||||
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime? {
|
||||
if (p.currentToken() == JsonToken.VALUE_STRING) {
|
||||
val text = p.text.trim()
|
||||
if (text.isEmpty()) return null
|
||||
try {
|
||||
return OffsetDateTime.parse(text).withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime()
|
||||
} catch (_: DateTimeParseException) {
|
||||
// naive-строка без зоны — трактуем как UTC
|
||||
}
|
||||
}
|
||||
return default.deserialize(p, ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
private class TolerantOffsetDateTimeDeserializer : JsonDeserializer<OffsetDateTime>() {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val default = InstantDeserializer.OFFSET_DATE_TIME as JsonDeserializer<OffsetDateTime>
|
||||
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): OffsetDateTime? {
|
||||
if (p.currentToken() == JsonToken.VALUE_STRING) {
|
||||
val text = p.text.trim()
|
||||
if (text.isEmpty()) return null
|
||||
try {
|
||||
return OffsetDateTime.parse(text).withOffsetSameInstant(ZoneOffset.UTC)
|
||||
} catch (_: DateTimeParseException) {
|
||||
return LocalDateTime.parse(text).atOffset(ZoneOffset.UTC)
|
||||
}
|
||||
}
|
||||
return default.deserialize(p, ctxt)
|
||||
}
|
||||
}
|
||||
|
||||
private class TolerantInstantDeserializer : JsonDeserializer<Instant>() {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val default = InstantDeserializer.INSTANT as JsonDeserializer<Instant>
|
||||
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Instant? {
|
||||
if (p.currentToken() == JsonToken.VALUE_STRING) {
|
||||
val text = p.text.trim()
|
||||
if (text.isEmpty()) return null
|
||||
try {
|
||||
return OffsetDateTime.parse(text).toInstant()
|
||||
} catch (_: DateTimeParseException) {
|
||||
return LocalDateTime.parse(text).toInstant(ZoneOffset.UTC)
|
||||
}
|
||||
}
|
||||
return default.deserialize(p, ctxt)
|
||||
}
|
||||
}
|
||||
+6
-5
@@ -16,11 +16,12 @@ import java.time.format.DateTimeParseException
|
||||
/**
|
||||
* Слой толерантности времени для **Jackson 3** (`tools.jackson.*`).
|
||||
*
|
||||
* Близнец [PcpTimeModule] (Jackson 2): то же поведение — читать время в обоих форматах на проводе
|
||||
* (naive=UTC и `Z`/офсет), нормализуя к UTC; трогает только десериализацию. Нужен отдельно, потому
|
||||
* что Boot 4 авто-конфигурирует Jackson 3 `JsonMapper` для REST-кодеков WebFlux, а он не видит
|
||||
* Jackson-2 модули. Регистрируется как бин `tools.jackson.databind.JacksonModule`
|
||||
* ([WebFluxJacksonConfig]) — `JacksonAutoConfiguration` собирает такие бины в общий `JsonMapper`.
|
||||
* Близнец библиотечного `PcpTimeModule` (Jackson 2): то же поведение — читать время в обоих
|
||||
* форматах на проводе (naive=UTC и `Z`/офсет), нормализуя к UTC; трогает только десериализацию.
|
||||
* Нужен отдельно, потому что Boot 4 авто-конфигурирует Jackson 3 `JsonMapper` для REST HTTP message
|
||||
* converters (MVC), а он не видит Jackson-2 модули. Регистрируется как бин
|
||||
* `tools.jackson.databind.JacksonModule` ([RestJacksonConfig]) — `JacksonAutoConfiguration` собирает
|
||||
* такие бины в общий `JsonMapper`.
|
||||
*
|
||||
* Канонический источник поведения — `space.nstart.pcp.pcp_types_lib.utils.json.PcpTimeModule`;
|
||||
* правки синхронизировать. См. docs/standards/DATETIME_UTC.md.
|
||||
|
||||
+4
-5
@@ -6,16 +6,15 @@ import tools.jackson.databind.JacksonModule
|
||||
|
||||
/**
|
||||
* Регистрирует слой толерантности времени ([PcpTimeModuleV3]) в авто-конфигурируемом Jackson 3
|
||||
* `JsonMapper`, который Boot 4 использует для REST-кодеков WebFlux.
|
||||
* `JsonMapper`, который Boot 4 использует для REST HTTP message converters (MVC).
|
||||
*
|
||||
* `JacksonAutoConfiguration` собирает все бины `tools.jackson.databind.JacksonModule` из контекста
|
||||
* и добавляет их в общий `JsonMapper` (`builder.addModules(...)`), поэтому достаточно объявить бин
|
||||
* этого типа. Закрывает tgu-follow-up из docs/standards/DATETIME_UTC.md: входящий REST-JSON теперь
|
||||
* читается толерантно к обоим форматам времени (naive=UTC и `Z`), как и Jackson-2 путь
|
||||
* ([CamundaJacksonConfig]).
|
||||
* этого типа. Входящий REST-JSON читается толерантно к обоим форматам времени (naive=UTC и `Z`),
|
||||
* как и Jackson-2 путь Camunda ([CamundaJacksonConfig]). См. docs/standards/DATETIME_UTC.md.
|
||||
*/
|
||||
@Configuration
|
||||
class WebFluxJacksonConfig {
|
||||
class RestJacksonConfig {
|
||||
|
||||
@Bean
|
||||
fun pcpTimeModuleV3(): JacksonModule = PcpTimeModuleV3()
|
||||
+2
-8
@@ -6,8 +6,6 @@ import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.core.scheduler.Schedulers
|
||||
import space.nstart.pcp_tgu_service.dto.PlanResponse
|
||||
import space.nstart.pcp_tgu_service.service.PlanQueryService
|
||||
|
||||
@@ -19,18 +17,14 @@ class PlanController(
|
||||
|
||||
/** Возвращает планы по всем КА (persisted ∪ проекция будущего) с окном истории. */
|
||||
@GetMapping
|
||||
fun getAllPlans(@RequestParam(defaultValue = "7") historyDays: Long): Mono<List<PlanResponse>> =
|
||||
Mono.fromCallable {
|
||||
fun getAllPlans(@RequestParam(defaultValue = "7") historyDays: Long): List<PlanResponse> =
|
||||
planQueryService.getAllPlans(historyDays)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
|
||||
/** Возвращает цепочку планов для одного КА (persisted ∪ проекция будущего). */
|
||||
@GetMapping("/{spacecraftId}")
|
||||
fun getPlansBySpacecraftId(
|
||||
@PathVariable spacecraftId: String,
|
||||
@RequestParam(defaultValue = "7") historyDays: Long
|
||||
): Mono<List<PlanResponse>> =
|
||||
Mono.fromCallable {
|
||||
): List<PlanResponse> =
|
||||
planQueryService.getPlansBySpacecraftId(spacecraftId, historyDays)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
}
|
||||
|
||||
+1
-5
@@ -10,8 +10,6 @@ import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.core.scheduler.Schedulers
|
||||
|
||||
/**
|
||||
* REST-контроллер для просмотра списка космических аппаратов.
|
||||
@@ -30,10 +28,8 @@ class PlatformController(
|
||||
@Operation(summary = "Получить список космических аппаратов")
|
||||
fun getPlatforms(
|
||||
@RequestParam(required = false) status: String?
|
||||
): Mono<List<PlatformResponse>> =
|
||||
Mono.fromCallable {
|
||||
): List<PlatformResponse> =
|
||||
platformService.loadPlatforms(status).map { platform -> platform.toResponse() }
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
|
||||
private fun PlatformCatalogItem.toResponse(): PlatformResponse =
|
||||
PlatformResponse(
|
||||
|
||||
+12
-27
@@ -9,8 +9,6 @@ import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.core.scheduler.Schedulers
|
||||
import space.nstart.pcp_tgu_service.domain.PlanDecision
|
||||
import space.nstart.pcp_tgu_service.dto.PlanDecisionMessage
|
||||
import space.nstart.pcp_tgu_service.dto.PlanResponse
|
||||
@@ -49,31 +47,25 @@ class TestTguController(
|
||||
fun refreshVisibility(
|
||||
@PathVariable spacecraftId: String,
|
||||
@RequestParam(required = false) noradId: Long?
|
||||
): Mono<Void> =
|
||||
Mono.fromRunnable<Void> {
|
||||
) {
|
||||
visibilityRefreshJobService.enqueue(spacecraftId, noradId, "TEST_CONTROLLER")
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
}
|
||||
|
||||
/** Возвращает вычисленную on-demand проекцию будущей цепочки КА (диагностика). */
|
||||
@GetMapping("/spacecraft/{spacecraftId}/projection")
|
||||
fun getProjection(@PathVariable spacecraftId: String): Mono<List<PlanResponse>> =
|
||||
Mono.fromCallable {
|
||||
fun getProjection(@PathVariable spacecraftId: String): List<PlanResponse> =
|
||||
planProjectionService.project(spacecraftId).map(PlanResponse::projected)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
|
||||
/** Принудительно выдаёт конкретный persisted plan через новую attempt. */
|
||||
@PostMapping("/plans/{planId}/issue")
|
||||
fun issuePlan(@PathVariable planId: UUID): Mono<ProcessStartResponse> =
|
||||
Mono.fromCallable {
|
||||
fun issuePlan(@PathVariable planId: UUID): ProcessStartResponse =
|
||||
issueDuePlanWorker.issuePlanNow(planId)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
|
||||
/** Инжектит decision event через тот же use case, что Kafka consumer. */
|
||||
@PostMapping("/plan-decisions")
|
||||
fun handleDecision(@RequestBody message: PlanDecisionMessage): Mono<Void> =
|
||||
Mono.fromRunnable<Void> {
|
||||
fun handleDecision(@RequestBody message: PlanDecisionMessage) {
|
||||
handlePlanDecisionUseCase.handle(message)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
}
|
||||
|
||||
/** Генерирует ACCEPTED/REJECTED decision event для active attempt указанного persisted plan. */
|
||||
@PostMapping("/plans/{planId}/decision")
|
||||
@@ -81,23 +73,20 @@ class TestTguController(
|
||||
@PathVariable planId: UUID,
|
||||
@RequestParam decision: PlanDecision,
|
||||
@RequestParam(required = false) reason: String?
|
||||
): Mono<PlanDecisionMessage> =
|
||||
Mono.fromCallable {
|
||||
): PlanDecisionMessage =
|
||||
testPlanDecisionService.generateDecision(planId, decision, reason)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
|
||||
/** Загружает cache аппаратов из файла с root-array payload НСИ вместо запроса в НСИ. */
|
||||
@PostMapping("/platforms/load-from-file")
|
||||
fun loadPlatformsFromFile(@RequestParam("path") filePath: String): Mono<TestPlatformsLoadResponse> =
|
||||
Mono.fromCallable {
|
||||
fun loadPlatformsFromFile(@RequestParam("path") filePath: String): TestPlatformsLoadResponse {
|
||||
val path = Path.of(filePath).toAbsolutePath().normalize()
|
||||
val platforms = platformService.loadPlatformsFromNsiPayload(Files.readString(path))
|
||||
TestPlatformsLoadResponse(
|
||||
return TestPlatformsLoadResponse(
|
||||
filePath = path.toString(),
|
||||
loadedCount = platforms.size,
|
||||
allowedCount = platformService.loadAllowedPlatforms().size
|
||||
)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
}
|
||||
|
||||
/** Загружает raw payload окон видимости из баллистики для диагностики. */
|
||||
@GetMapping("/external-points/{noradId}")
|
||||
@@ -109,17 +98,13 @@ class TestTguController(
|
||||
@RequestParam("time_stop")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
|
||||
timeStop: LocalDateTime
|
||||
): Mono<String> =
|
||||
Mono.fromCallable {
|
||||
): String =
|
||||
externalPointsClient.fetchRawVisibilityPayload(noradId, timeStart, timeStop)
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
|
||||
/** Возвращает persisted plan для проверки результата ручных операций. */
|
||||
@GetMapping("/plans/{planId}")
|
||||
fun getPlan(@PathVariable planId: UUID): Mono<PlanResponse> =
|
||||
Mono.fromCallable {
|
||||
fun getPlan(@PathVariable planId: UUID): PlanResponse =
|
||||
PlanResponse.from(planQueryService.getByPlanId(planId))
|
||||
}.subscribeOn(Schedulers.boundedElastic())
|
||||
}
|
||||
|
||||
data class TestPlatformsLoadResponse(
|
||||
|
||||
Reference in New Issue
Block a user