pcp-tgu: якорь цепочки на LAID_IN, параллельные выдачи, SUPERSEDED
Базой цепочки план становится на ЗАКЛАДКЕ (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).
This commit is contained in:
+7
@@ -47,6 +47,13 @@ class TguPlanningRestController(
|
||||
@RequestParam(required = false) reason: String?
|
||||
) = tguPlanningService.sendDecision(planId, decision, reason)
|
||||
|
||||
/** ТЕСТ: подтвердить закладку плана (AWAITING_LAYIN → LAID_IN). */
|
||||
@PostMapping("/plans/{planId}/confirm-layin")
|
||||
fun confirmLayin(
|
||||
@PathVariable planId: UUID,
|
||||
@RequestParam(required = false) reason: String?
|
||||
) = tguPlanningService.confirmLayin(planId, reason)
|
||||
|
||||
@GetMapping("/spacecraft/{noradId}/flight-line")
|
||||
fun spacecraftFlightLine(
|
||||
@PathVariable noradId: Long,
|
||||
|
||||
+20
@@ -87,6 +87,26 @@ class TguPlanningService(webClientBuilderProvider: ObjectProvider<WebClient.Buil
|
||||
throw error
|
||||
}
|
||||
|
||||
fun confirmLayin(planId: UUID, reason: String?) {
|
||||
runCatching {
|
||||
webClientBuilder.build()
|
||||
.post()
|
||||
.uri(buildUri("/test/tgu/plans/{planId}/confirm-layin") { builder ->
|
||||
reason?.takeIf { it.isNotBlank() }?.let { builder.queryParam("reason", it) }
|
||||
builder.buildAndExpand(planId)
|
||||
})
|
||||
.retrieve()
|
||||
.onStatus({ status -> status.isError }, ::mapReadError)
|
||||
.bodyToMono(Void::class.java)
|
||||
.block()
|
||||
}.getOrElse { error ->
|
||||
if (error is WebClientRequestException) {
|
||||
throw ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, TEST_CONTROLLER_UNAVAILABLE_MESSAGE, error)
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildUri(
|
||||
path: String,
|
||||
customizer: (UriComponentsBuilder) -> Any = { builder -> builder.build() }
|
||||
|
||||
Reference in New Issue
Block a user