feat(slots-service): параметр all у request-cover — возврат занятых бронью (BUSY) слотов

«Показать все слоты» должно показывать полный набор. При all=true reqCover
запрашивает статусы [AVAILABLE, BOOKED, BUSY], а selectCoverageSlots собирает
BUSY отдельно и добавляет в ответ только в ветке без покрытия — в solver
(«Подобрать покрытие») занятые слоты по-прежнему не попадают.
This commit is contained in:
Дмитрий Соловьев
2026-06-15 16:45:14 +03:00
parent bffb8e47b7
commit 052ed5f04b
2 changed files with 31 additions and 10 deletions
@@ -103,8 +103,10 @@ class SlotController {
@RequestParam("sun") sun : Double?,
@Parameter(description = "Вариант расчета покрытия")
@RequestParam("coverageStrategy", required = false) coverageStrategy: SlotCoverageStrategy?,
@Parameter(description = "Вернуть и занятые бронью слоты (BUSY) — для обзора; по умолчанию только доступные/забронированные")
@RequestParam("all", required = false) all: Boolean?,
) : List<SlotDTO>
= slotService.reqCover(requestId, timeStart, timeStop, sign, cov, satellites, sun, coverageStrategy).toList()
= slotService.reqCover(requestId, timeStart, timeStop, sign, cov, satellites, sun, coverageStrategy, all == true).toList()
@GetMapping("interval")
@Operation(summary = "Получение всех слотов на заданном интервале времени")
@@ -1216,7 +1216,8 @@ class SlotService {
states: List<SlotStatus>? = null,
sats : List<Long>? = null,
sun : Double? = null,
coverageStrategy: SlotCoverageStrategy? = SlotCoverageStrategy.GREEDY
coverageStrategy: SlotCoverageStrategy? = SlotCoverageStrategy.GREEDY,
includeBusy: Boolean = false
): Iterable<SlotDTO> {
val target = buildCoverageTargets(
items = listOf(SlotCoverageTargetDTO(targetId = 1, wkt = wkt))
@@ -1235,7 +1236,8 @@ class SlotService {
batchContext = batchContext,
cov = cov,
sun = sun,
coverageStrategy = coverageStrategy
coverageStrategy = coverageStrategy,
includeBusy = includeBusy
)
logger.info(
"polySlots finish: targetId={}, candidateSlots={}, sunFilteredSlots={}, returnedSlots={}, sun={}",
@@ -1668,10 +1670,14 @@ class SlotService {
batchContext: CoverageBatchContext,
cov: Boolean?,
sun: Double?,
coverageStrategy: SlotCoverageStrategy?
coverageStrategy: SlotCoverageStrategy?,
// true — добавить в результат и занятые бронью слоты (BUSY) для обзора. В расчёт покрытия
// (solver) они не попадают: selectedSlots остаётся только из AVAILABLE/BOOKED.
includeBusy: Boolean = false
): CoverageSelectionResult {
val totalStartedAtMs = System.currentTimeMillis()
val selectedSlots = mutableListOf<SlotDTO>()
val busySlots = mutableListOf<SlotDTO>()
val targetSlotIdsBySatelliteId = batchContext.rawSlotIdsByTargetIdAndSatelliteId[target.targetId].orEmpty()
val sourceSlotIds = targetSlotIdsBySatelliteId.values.sumOf { it.size }
var preparedSlotsBeforeOccupiedFilter = 0
@@ -1710,6 +1716,12 @@ class SlotService {
val sunFilterResult = filterBySunAngle(filteredSlots, sun)
sunFilteredSlots += sunFilterResult.filteredCount
selectedSlots.addAll(sunFilterResult.slots)
// «Показать все слоты»: добавляем и занятые бронью (BUSY) для обзора — тот же
// sun-фильтр, чтобы не показывать заведомо непригодные по Солнцу.
if (includeBusy) {
val busyPrepared = preparedSlotsIncludingBlocked.filter { it.state == SlotStatus.BUSY }
busySlots.addAll(filterBySunAngle(busyPrepared, sun).slots)
}
}
}
logger.debug(
@@ -1815,15 +1827,17 @@ class SlotService {
candidateSelectionMs,
totalMs
)
// Без покрытия («Показать все слоты»): при includeBusy возвращаем и занятые бронью слоты.
val resultSlots = if (includeBusy) selectedSlots + busySlots else selectedSlots
return CoverageSelectionResult(
slots = selectedSlots,
slots = resultSlots,
sourceSlotIds = sourceSlotIds,
preparedSlotsBeforeOccupiedFilter = preparedSlotsBeforeOccupiedFilter,
occupiedFilteredSlots = occupiedFilteredSlots,
candidateSlotsBeforeSunFilter = candidateSlotsBeforeSunFilter,
sunFilteredSlots = sunFilteredSlots,
solverInputSlots = selectedSlots.size,
solverOutputSlots = selectedSlots.size,
solverOutputSlots = resultSlots.size,
candidateSelectionDurationMs = candidateSelectionMs,
solverDurationMs = 0L,
totalDurationMs = totalMs
@@ -1870,9 +1884,12 @@ class SlotService {
cov: Boolean?,
sats : List<Long>? = null,
sun : Double? = null,
coverageStrategy: SlotCoverageStrategy? = SlotCoverageStrategy.GREEDY
coverageStrategy: SlotCoverageStrategy? = SlotCoverageStrategy.GREEDY,
// true — вернуть и занятые бронью слоты (BUSY) для обзора («Показать все слоты»).
// Бронировать их нельзя, в расчёт покрытия они не идут.
includeBusy: Boolean = false
): Iterable<SlotDTO> {
logger.info("Запрос покрытия заявки $id")
logger.info("Запрос покрытия заявки $id (includeBusy=$includeBusy)")
val req = earthService.reqcells(id)?.request?.contour ?: throw CustomErrorException("Нет такой заявки!!!!")
logger.info("Получена заявка : $req")
return polySlots(
@@ -1881,10 +1898,12 @@ class SlotService {
timeStop,
sign,
cov,
listOf(SlotStatus.AVAILABLE, SlotStatus.BOOKED),
if (includeBusy) listOf(SlotStatus.AVAILABLE, SlotStatus.BOOKED, SlotStatus.BUSY)
else listOf(SlotStatus.AVAILABLE, SlotStatus.BOOKED),
sats,
sun,
coverageStrategy
coverageStrategy,
includeBusy
)
}