From bb84844674723647b4248a25ff1ffeb5bd66bde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=A1=D0=BE?= =?UTF-8?q?=D0=BB=D0=BE=D0=B2=D1=8C=D0=B5=D0=B2?= Date: Sat, 30 May 2026 23:28:45 +0300 Subject: [PATCH] refactor(pcp-request-service): rename REST endpoints and DTOs for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GET /v1/requests/with-geometry → /v1/requests/map - GET /v1/requests/{id}/with-cells → /v1/requests/{id}/cells - GET /v1/cells/with-requests → /v1/cells/priority-map - CreateRequestRequestDto → CreateRequestDto - ListRequestsQueryDto → RequestListFilter - RequestWithGeometry* → RequestMap* (RequestMapItemDto, RequestMapPageDto) - CellsWithRequestsResponseDto → CellPriorityMapResponseDto - DeleteRequestStatusDto removed; status field removed from DeleteRequestResponseDto Updated all consumers (pcp-complex-mission-service, pcp-ui-service, slots-service), tests, and OpenAPI YAML. Co-Authored-By: Claude Sonnet 4.6 --- .../service/EarthGridService.kt | 2 +- .../service/EarthGridServiceTest.kt | 8 +- .../docs/OpenAPIRequestPCP.yaml | 270 ++++++++++++++++-- .../controller/CellsController.kt | 10 +- .../controller/RequestController.kt | 47 ++- .../dep265/requestservice/dto/CellsApiDtos.kt | 8 +- .../requestservice/dto/RequestApiDtos.kt | 85 +++++- .../service/CellsQueryService.kt | 8 +- .../requestservice/service/RequestService.kt | 75 +++-- .../controller/CellsControllerTest.kt | 18 +- .../RequestControllerOpenApiSkeletonTest.kt | 41 ++- .../dto/RequestApiDtoContractTest.kt | 6 +- .../service/CellsQueryServiceJpaTest.kt | 8 +- .../service/CellsQueryServiceTest.kt | 10 +- .../RequestServiceListRequestsJpaTest.kt | 37 ++- .../service/RequestServiceTest.kt | 36 ++- .../pcp/slots_service/service/EarthService.kt | 2 +- .../service/CoverageSchemeServiceTest.kt | 4 +- .../slots_service/service/EarthServiceTest.kt | 2 +- .../pcp/slots_service/service/EarthService.kt | 2 +- .../slots_service/service/EarthServiceTest.kt | 2 +- 21 files changed, 545 insertions(+), 136 deletions(-) diff --git a/services/pcp-complex-mission-service/src/main/kotlin/space/nstart/pcp/pcp_satellites_service/service/EarthGridService.kt b/services/pcp-complex-mission-service/src/main/kotlin/space/nstart/pcp/pcp_satellites_service/service/EarthGridService.kt index 725fefb..0710a46 100644 --- a/services/pcp-complex-mission-service/src/main/kotlin/space/nstart/pcp/pcp_satellites_service/service/EarthGridService.kt +++ b/services/pcp-complex-mission-service/src/main/kotlin/space/nstart/pcp/pcp_satellites_service/service/EarthGridService.kt @@ -50,7 +50,7 @@ class EarthGridService(webClientBuilderProvider: ObjectProvider uri.path == "/api/earth-grid/with-requests" }) assertEquals(1L, cell.id) @@ -55,7 +55,7 @@ class EarthGridServiceTest { earthGridService().cells(countLat = 12, countLong = 24).toList() - assertEquals("/v1/cells/with-requests", requestedUris.single().path) + assertEquals("/v1/cells/priority-map", requestedUris.single().path) assertEquals("minImportance=0.001&countLat=12&countLong=24", requestedUris.single().query) assertFalse(requestedUris.any { uri -> uri.path == "/api/earth-grid/with-requests" }) } @@ -93,7 +93,7 @@ class EarthGridServiceTest { }.cells().toList() assertEquals(itemCount, cells.size) - assertEquals("/v1/cells/with-requests", requestedUris.single().path) + assertEquals("/v1/cells/priority-map", requestedUris.single().path) assertEquals("minImportance=0.001", requestedUris.single().query) } @@ -128,7 +128,7 @@ class EarthGridServiceTest { responseBody: String ): HttpServer = HttpServer.create(InetSocketAddress(0), 0).apply { - createContext("/v1/cells/with-requests") { exchange -> + createContext("/v1/cells/priority-map") { exchange -> requestedUris += exchange.requestURI exchange.responseHeaders.add("Content-Type", "application/json") exchange.sendResponseHeaders(200, responseBody.toByteArray().size.toLong()) diff --git a/services/pcp-request-service/docs/OpenAPIRequestPCP.yaml b/services/pcp-request-service/docs/OpenAPIRequestPCP.yaml index c9a3f89..4497cd0 100644 --- a/services/pcp-request-service/docs/OpenAPIRequestPCP.yaml +++ b/services/pcp-request-service/docs/OpenAPIRequestPCP.yaml @@ -10,6 +10,7 @@ info: - заявка создается через POST /v1/requests; - чтение одной заявки выполняется через GET /v1/requests/{id}; - чтение списка заявок выполняется через GET /v1/requests; + - чтение списка заявок с geometry выполняется через GET /v1/requests/map; - удаление заявки выполняется через DELETE /v1/requests/{id}; - удаление является мягким: заявка получает status = DELETED и deletedAt; - заявка должна содержать хотя бы один payload-блок: optics или rsa; @@ -106,14 +107,15 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /v1/cells/with-requests: + /v1/cells/priority-map: get: tags: - Cells summary: Получить ячейки сетки с фрагментами заявок description: | Возвращает список ячеек сетки вместе с фрагментами заявок, пересекающими каждую ячейку. - operationId: listCellsWithRequests + Используется для построения тепловой карты приоритетов. + operationId: listCellsPriorityMap parameters: - name: minImportance in: query @@ -143,7 +145,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CellsWithRequestsResponse' + $ref: '#/components/schemas/CellPriorityMapResponse' '400': description: Некорректные query parameters content: @@ -304,7 +306,8 @@ paths: Возвращает постраничный список заявок. По умолчанию удаленные заявки не возвращаются. Для технического поиска можно передать includeDeleted=true. - Полная geometry не возвращается в списке по умолчанию; для полной заявки используйте GET /v1/requests/{id}. + Полная geometry не возвращается в списке по умолчанию; для списка с geometry используйте GET /v1/requests/map, + для полной заявки используйте GET /v1/requests/{id}. operationId: listRequests parameters: - name: status @@ -415,7 +418,127 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - /v1/requests/{requestId}/with-cells: + /v1/requests/map: + get: + tags: + - Requests + summary: Получить список заявок с геометрией для карты + description: | + Возвращает постраничный список заявок с полной geometry в WKT. + + Endpoint использует те же фильтры и сортировку, что GET /v1/requests, но возвращает расширенную read-модель. + Используйте его только там, где клиенту действительно нужна геометрия списка (например, для отображения на карте). + operationId: listRequestsForMap + parameters: + - name: status + in: query + required: false + description: Фильтр по статусу заявки + schema: + $ref: '#/components/schemas/RequestStatus' + - name: surveyType + in: query + required: false + description: Фильтр по вычисленному типу заявки + schema: + $ref: '#/components/schemas/SurveyType' + - name: kpp + in: query + required: false + description: Фильтр по номеру КПП. Возвращает заявки, содержащие указанный КПП. + schema: + type: integer + example: 1 + - name: highPriorityTransmit + in: query + required: false + description: Фильтр по признаку высокоприоритетной передачи + schema: + type: boolean + - name: beginFrom + in: query + required: false + description: Нижняя граница beginDateTime, включительно + schema: + type: string + format: date-time + - name: beginTo + in: query + required: false + description: Верхняя граница beginDateTime, включительно + schema: + type: string + format: date-time + - name: endFrom + in: query + required: false + description: Нижняя граница endDateTime, включительно + schema: + type: string + format: date-time + - name: endTo + in: query + required: false + description: Верхняя граница endDateTime, включительно + schema: + type: string + format: date-time + - name: includeDeleted + in: query + required: false + description: Включать soft-deleted заявки в результат + schema: + type: boolean + default: false + - name: page + in: query + required: false + description: Номер страницы, начиная с 0 + schema: + type: integer + minimum: 0 + default: 0 + - name: size + in: query + required: false + description: Размер страницы + schema: + type: integer + minimum: 1 + maximum: 500 + default: 50 + - name: sort + in: query + required: false + description: Сортировка в формате field,direction. Разрешенные поля зависят от реализации. + schema: + type: string + default: createdAt,desc + examples: + createdDesc: + value: createdAt,desc + beginAsc: + value: beginDateTime,asc + responses: + '200': + description: Список заявок с геометрией + content: + application/json: + schema: + $ref: '#/components/schemas/RequestMapPageResponse' + '400': + description: Некорректные query parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Внутренняя ошибка сервера + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /v1/requests/{id}/cells: get: tags: - Cells @@ -425,7 +548,7 @@ paths: Возвращает заявку и проекцию ее пересечений с ячейками сетки. operationId: getRequestWithCells parameters: - - $ref: '#/components/parameters/RequestIdWithCellsPathParam' + - $ref: '#/components/parameters/RequestIdPathParam' responses: '200': description: Заявка с привязанными ячейками @@ -500,7 +623,6 @@ paths: $ref: '#/components/schemas/DeleteRequestResponse' example: id: 3fa85f64-5717-4562-b3fc-2c963f66afa6 - status: DELETED deletedAt: '2026-05-19T13:00:00Z' '404': description: Заявка не найдена @@ -609,15 +731,6 @@ components: type: string format: uuid example: 3fa85f64-5717-4562-b3fc-2c963f66afa6 - RequestIdWithCellsPathParam: - name: requestId - in: path - required: true - description: Идентификатор заявки - schema: - type: string - format: uuid - example: 3fa85f64-5717-4562-b3fc-2c963f66afa6 schemas: CreateRequestRequest: type: object @@ -921,12 +1034,125 @@ components: minimum: 0 example: 1 additionalProperties: false + RequestMapItemResponse: + type: object + description: Краткая карточка заявки с полной геометрией для GET /v1/requests/map + required: + - id + - name + - status + - surveyType + - geometry + - importance + - beginDateTime + - endDateTime + - highPriorityTransmit + - coveragePercent + - createdAt + - updatedAt + properties: + id: + type: string + format: uuid + description: Идентификатор заявки + example: 3fa85f64-5717-4562-b3fc-2c963f66afa6 + name: + type: string + minLength: 1 + maxLength: 255 + description: Наименование задания. + example: Тест1 + status: + $ref: '#/components/schemas/RequestStatus' + surveyType: + $ref: '#/components/schemas/SurveyType' + geometry: + type: string + format: wkt + description: Полная геометрия заявки в формате WKT + example: POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5)) + importance: + type: number + format: double + minimum: 0 + description: Важность заявки, используемая при расчете важности ячеек сетки. + example: 10.0 + beginDateTime: + type: string + format: date-time + example: '2026-01-01T00:00:00Z' + endDateTime: + type: string + format: date-time + example: '2026-01-31T23:59:59Z' + kpp: + type: array + items: + type: integer + description: Список номеров КПП для сброса + example: [1, 2, 3] + highPriorityTransmit: + type: boolean + example: false + coveragePercent: + type: number + format: double + minimum: 0 + maximum: 100 + description: Текущий процент покрытия заявки + example: 35.5 + createdAt: + type: string + format: date-time + example: '2026-05-19T12:00:00Z' + updatedAt: + type: string + format: date-time + example: '2026-05-19T12:30:00Z' + deletedAt: + type: string + format: date-time + nullable: true + description: Дата-время soft delete. Обычно отсутствует/null при includeDeleted=false. + example: null + additionalProperties: false + RequestMapPageResponse: + type: object + description: Постраничный список заявок с полной геометрией для GET /v1/requests/map + required: + - items + - page + - size + - totalItems + - totalPages + properties: + items: + type: array + items: + $ref: '#/components/schemas/RequestMapItemResponse' + page: + type: integer + minimum: 0 + example: 0 + size: + type: integer + minimum: 1 + example: 50 + totalItems: + type: integer + format: int64 + minimum: 0 + example: 1 + totalPages: + type: integer + minimum: 0 + example: 1 + additionalProperties: false DeleteRequestResponse: type: object description: Ответ на soft delete заявки required: - id - - status - deletedAt properties: id: @@ -934,12 +1160,6 @@ components: format: uuid description: Идентификатор заявки example: 3fa85f64-5717-4562-b3fc-2c963f66afa6 - status: - type: string - enum: - - DELETED - description: Итоговый статус после soft delete - example: DELETED deletedAt: type: string format: date-time @@ -1091,9 +1311,9 @@ components: description: Важность заявки для расчета важности ячейки example: 10.0 additionalProperties: false - CellsWithRequestsResponse: + CellPriorityMapResponse: type: object - description: Список ячеек сетки с фрагментами заявок + description: Список ячеек сетки с фрагментами заявок для GET /v1/cells/priority-map required: - items properties: diff --git a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/CellsController.kt b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/CellsController.kt index 6843806..de20ff8 100644 --- a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/CellsController.kt +++ b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/CellsController.kt @@ -1,7 +1,7 @@ package org.nstart.dep265.requestservice.controller +import org.nstart.dep265.requestservice.dto.CellPriorityMapResponseDto import org.nstart.dep265.requestservice.dto.CellsListResponseDto -import org.nstart.dep265.requestservice.dto.CellsWithRequestsResponseDto import org.nstart.dep265.requestservice.service.CellsQueryService import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping @@ -32,13 +32,13 @@ class CellsController( ) } - @GetMapping("/with-requests") - fun listCellsWithRequests( + @GetMapping("/priority-map") + fun listCellsPriorityMap( @RequestParam(required = false) minImportance: Double?, @RequestParam(required = false) countLat: Int?, @RequestParam(required = false) countLong: Int?, - ): CellsWithRequestsResponseDto { - return cellsQueryService.listCellsWithRequests( + ): CellPriorityMapResponseDto { + return cellsQueryService.listCellsPriorityMap( minImportance = minImportance, countLat = countLat, countLong = countLong, diff --git a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/RequestController.kt b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/RequestController.kt index 37e05a1..c1d5367 100644 --- a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/RequestController.kt +++ b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/controller/RequestController.kt @@ -1,11 +1,12 @@ package org.nstart.dep265.requestservice.controller import jakarta.validation.Valid -import org.nstart.dep265.requestservice.dto.CreateRequestRequestDto +import org.nstart.dep265.requestservice.dto.CreateRequestDto import org.nstart.dep265.requestservice.dto.CreateRequestResponseDto import org.nstart.dep265.requestservice.dto.DeleteRequestResponseDto -import org.nstart.dep265.requestservice.dto.ListRequestsQueryDto +import org.nstart.dep265.requestservice.dto.RequestListFilter import org.nstart.dep265.requestservice.dto.RequestListResponseDto +import org.nstart.dep265.requestservice.dto.RequestMapPageDto import org.nstart.dep265.requestservice.dto.RequestResponseDto import org.nstart.dep265.requestservice.dto.RequestStatusDto import org.nstart.dep265.requestservice.dto.RequestWithCellsResponseDto @@ -30,7 +31,7 @@ class RequestController( private val requestService: RequestService, ) { @PostMapping - fun createRequest(@Valid @RequestBody request: CreateRequestRequestDto): ResponseEntity { + fun createRequest(@Valid @RequestBody request: CreateRequestDto): ResponseEntity { return ResponseEntity.status(201).body(requestService.createRequest(request)) } @@ -49,7 +50,7 @@ class RequestController( @RequestParam(defaultValue = "50") size: Int, @RequestParam(defaultValue = "createdAt,desc") sort: String, ): RequestListResponseDto { - val query = ListRequestsQueryDto( + val query = RequestListFilter( status = status, surveyType = surveyType, kpp = kpp, @@ -66,6 +67,38 @@ class RequestController( return requestService.listRequests(query) } + @GetMapping("/map") + fun listRequestsForMap( + @RequestParam(required = false) status: RequestStatusDto?, + @RequestParam(required = false) surveyType: SurveyTypeDto?, + @RequestParam(required = false) kpp: Int?, + @RequestParam(required = false) highPriorityTransmit: Boolean?, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginFrom: OffsetDateTime?, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginTo: OffsetDateTime?, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endFrom: OffsetDateTime?, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endTo: OffsetDateTime?, + @RequestParam(defaultValue = "false") includeDeleted: Boolean, + @RequestParam(defaultValue = "0") page: Int, + @RequestParam(defaultValue = "50") size: Int, + @RequestParam(defaultValue = "createdAt,desc") sort: String, + ): RequestMapPageDto { + val query = RequestListFilter( + status = status, + surveyType = surveyType, + kpp = kpp, + highPriorityTransmit = highPriorityTransmit, + beginFrom = beginFrom, + beginTo = beginTo, + endFrom = endFrom, + endTo = endTo, + includeDeleted = includeDeleted, + page = page, + size = size, + sort = sort, + ) + return requestService.listRequestsForMap(query) + } + @GetMapping("/{id}") fun getRequestById(@PathVariable id: UUID): ResponseEntity { return requestService.getRequestById(id) @@ -73,9 +106,9 @@ class RequestController( ?: ResponseEntity.notFound().build() } - @GetMapping("/{requestId}/with-cells") - fun getRequestWithCells(@PathVariable requestId: UUID): ResponseEntity { - return requestService.getRequestWithCells(requestId) + @GetMapping("/{id}/cells") + fun getRequestWithCells(@PathVariable id: UUID): ResponseEntity { + return requestService.getRequestWithCells(id) ?.let { response -> ResponseEntity.ok(response) } ?: ResponseEntity.notFound().build() } diff --git a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/CellsApiDtos.kt b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/CellsApiDtos.kt index 2e2d603..028e438 100644 --- a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/CellsApiDtos.kt +++ b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/CellsApiDtos.kt @@ -43,7 +43,7 @@ data class CellsListResponseDto( ) /** - * Фрагмент заявки внутри ячейки сетки для GET /v1/cells/with-requests. + * Фрагмент заявки внутри ячейки сетки для GET /v1/cells/priority-map. */ data class CellRequestFragmentResponseDto( /** Идентификатор заявки. */ @@ -60,7 +60,7 @@ data class CellRequestFragmentResponseDto( ) /** - * Ячейка сетки с фрагментами заявок для GET /v1/cells/with-requests. + * Ячейка сетки с фрагментами заявок для GET /v1/cells/priority-map. */ data class CellWithRequestsResponseDto( /** Номер ячейки сетки. */ @@ -83,9 +83,9 @@ data class CellWithRequestsResponseDto( ) /** - * Список ячеек сетки с фрагментами заявок. + * Список ячеек сетки с фрагментами заявок для GET /v1/cells/priority-map. */ -data class CellsWithRequestsResponseDto( +data class CellPriorityMapResponseDto( /** Ячейки сетки с привязанными request_cells fragments. */ val items: List, ) diff --git a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtos.kt b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtos.kt index 9690b31..2131f87 100644 --- a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtos.kt +++ b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtos.kt @@ -27,7 +27,7 @@ abstract class StrictRequestApiDto { /** * Тело запроса на создание заявки по OpenAPI. */ -data class CreateRequestRequestDto( +data class CreateRequestDto( /** Внешний идентификатор заявки. */ @field:NotNull val id: UUID, @@ -68,7 +68,6 @@ data class CreateRequestRequestDto( @field:Valid val rsa: RsaParamsDto? = null, ) : StrictRequestApiDto() { - /** В create-запросе должен быть указан хотя бы один payload-блок съемки. */ @get:AssertTrue(message = "Должен быть указан хотя бы один блок съемки: optics или rsa") @get:JsonIgnore val hasSurveyPayload: Boolean @@ -215,6 +214,73 @@ data class RequestListResponseDto( val totalPages: Int, ) +/** + * Краткая карточка заявки с геометрией для GET /v1/requests/map. + */ +data class RequestMapItemDto( + /** Идентификатор заявки. */ + val id: UUID, + + /** Наименование задания. */ + val name: String, + + /** Текущий статус заявки. */ + val status: RequestStatusDto, + + /** Вычисленный тип заявки. */ + val surveyType: SurveyTypeDto, + + /** Полная геометрия заявки в WKT. */ + val geometry: String, + + /** Важность заявки для расчета важности ячеек сетки. */ + val importance: Double, + + /** Начало временного окна выполнения заявки. */ + val beginDateTime: OffsetDateTime, + + /** Окончание временного окна выполнения заявки. */ + val endDateTime: OffsetDateTime, + + /** Список номеров КПП для сброса. */ + val kpp: List = emptyList(), + + /** Признак высокоприоритетной передачи. */ + val highPriorityTransmit: Boolean, + + /** Текущий процент покрытия заявки. */ + val coveragePercent: Double, + + /** Время создания заявки. */ + val createdAt: OffsetDateTime, + + /** Время последнего обновления заявки. */ + val updatedAt: OffsetDateTime, + + /** Время soft delete, если заявка удалена. */ + val deletedAt: OffsetDateTime? = null, +) + +/** + * Постраничный список заявок с полной геометрией для GET /v1/requests/map. + */ +data class RequestMapPageDto( + /** Элементы текущей страницы. */ + val items: List, + + /** Номер страницы, начиная с 0. */ + val page: Int, + + /** Размер страницы. */ + val size: Int, + + /** Общее количество элементов. */ + val totalItems: Long, + + /** Общее количество страниц. */ + val totalPages: Int, +) + /** * Ячейка, связанная с заявкой через request_cells projection. */ @@ -250,9 +316,6 @@ data class DeleteRequestResponseDto( /** Идентификатор заявки. */ val id: UUID, - /** Итоговый статус после удаления. */ - val status: DeleteRequestStatusDto, - /** Время soft delete. */ val deletedAt: OffsetDateTime, ) @@ -342,9 +405,9 @@ data class ErrorDetailDto( ) /** - * Query parameters list endpoint. + * Query parameters для list endpoint. */ -data class ListRequestsQueryDto( +data class RequestListFilter( /** Фильтр по статусу заявки. */ val status: RequestStatusDto? = null, @@ -405,14 +468,6 @@ enum class RequestStatusDto { DELETED, } -/** - * Статус ответа delete endpoint. - */ -enum class DeleteRequestStatusDto { - /** Заявка удалена или уже была удалена ранее. */ - DELETED, -} - /** * Вычисленный тип заявки по наличию optics/rsa. */ diff --git a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/CellsQueryService.kt b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/CellsQueryService.kt index 975ec28..05597f7 100644 --- a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/CellsQueryService.kt +++ b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/CellsQueryService.kt @@ -4,7 +4,7 @@ import org.nstart.dep265.requestservice.dto.CellRequestFragmentResponseDto import org.nstart.dep265.requestservice.dto.CellSummaryResponseDto import org.nstart.dep265.requestservice.dto.CellWithRequestsResponseDto import org.nstart.dep265.requestservice.dto.CellsListResponseDto -import org.nstart.dep265.requestservice.dto.CellsWithRequestsResponseDto +import org.nstart.dep265.requestservice.dto.CellPriorityMapResponseDto import org.nstart.dep265.requestservice.entity.EarthCellEntity import org.nstart.dep265.requestservice.entity.RequestCellEntity import org.nstart.dep265.requestservice.repository.EarthCellRepository @@ -49,11 +49,11 @@ class CellsQueryService( } @Transactional(readOnly = true) - fun listCellsWithRequests( + fun listCellsPriorityMap( minImportance: Double?, countLat: Int?, countLong: Int?, - ): CellsWithRequestsResponseDto { + ): CellPriorityMapResponseDto { validateWithRequestsQuery(minImportance, countLat, countLong) val items = if (countLat == null && countLong == null) { @@ -68,7 +68,7 @@ class CellsQueryService( .map { earthCell -> earthCell.toWithRequestsResponse() } } - return CellsWithRequestsResponseDto(items = items) + return CellPriorityMapResponseDto(items = items) } private fun validateQuery( diff --git a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/RequestService.kt b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/RequestService.kt index 6647e3b..13df5cb 100644 --- a/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/RequestService.kt +++ b/services/pcp-request-service/src/main/kotlin/org/nstart/dep265/requestservice/service/RequestService.kt @@ -4,18 +4,19 @@ import jakarta.persistence.EntityExistsException import jakarta.persistence.EntityManager import jakarta.persistence.PersistenceException import org.hibernate.exception.ConstraintViolationException -import org.nstart.dep265.requestservice.dto.CreateRequestRequestDto +import org.nstart.dep265.requestservice.dto.CreateRequestDto import org.nstart.dep265.requestservice.dto.CreateRequestResponseDto import org.nstart.dep265.requestservice.dto.CoverageStateDto import org.nstart.dep265.requestservice.dto.DeleteRequestResponseDto -import org.nstart.dep265.requestservice.dto.DeleteRequestStatusDto -import org.nstart.dep265.requestservice.dto.ListRequestsQueryDto +import org.nstart.dep265.requestservice.dto.RequestListFilter import org.nstart.dep265.requestservice.dto.OpticsParamsDto import org.nstart.dep265.requestservice.dto.RequestListResponseDto import org.nstart.dep265.requestservice.dto.RequestCellResponseDto import org.nstart.dep265.requestservice.dto.RequestResponseDto import org.nstart.dep265.requestservice.dto.RequestSummaryResponseDto import org.nstart.dep265.requestservice.dto.RequestWithCellsResponseDto +import org.nstart.dep265.requestservice.dto.RequestMapPageDto +import org.nstart.dep265.requestservice.dto.RequestMapItemDto import org.nstart.dep265.requestservice.dto.RequestStatusDto import org.nstart.dep265.requestservice.dto.RsaParamsDto import org.nstart.dep265.requestservice.dto.SurveyTypeDto @@ -32,6 +33,7 @@ import org.nstart.dep265.requestservice.repository.RequestCellRepository import org.nstart.dep265.requestservice.repository.RequestOpticsParamsRepository import org.nstart.dep265.requestservice.repository.RequestRepository import org.nstart.dep265.requestservice.repository.RequestRsaParamsRepository +import org.springframework.data.domain.Page import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Sort import org.springframework.stereotype.Service @@ -54,7 +56,7 @@ class RequestService( private val entityManager: EntityManager, ) { @Transactional - fun createRequest(request: CreateRequestRequestDto): CreateRequestResponseDto { + fun createRequest(request: CreateRequestDto): CreateRequestResponseDto { val requestId = request.id if (requestRepository.existsById(requestId)) { throw RequestAlreadyExistsException(requestId) @@ -105,20 +107,8 @@ class RequestService( } @Transactional(readOnly = true) - fun listRequests(query: ListRequestsQueryDto): RequestListResponseDto { - val requests = requestRepository.findRequests( - includeDeleted = query.includeDeleted, - deletedStatus = RequestStatus.DELETED, - status = query.status?.toEntity(), - surveyType = query.surveyType?.toEntity(), - kpp = query.kpp, - highPriorityTransmit = query.highPriorityTransmit, - beginFrom = query.beginFrom?.toUtcLocalDateTime(), - beginTo = query.beginTo?.toUtcLocalDateTime(), - endFrom = query.endFrom?.toUtcLocalDateTime(), - endTo = query.endTo?.toUtcLocalDateTime(), - pageable = PageRequest.of(query.page, query.size, query.sort.toRequestSort()), - ) + fun listRequests(query: RequestListFilter): RequestListResponseDto { + val requests = findRequests(query) return RequestListResponseDto( items = requests.content.map { request -> request.toSummaryResponse() }, page = query.page, @@ -128,6 +118,18 @@ class RequestService( ) } + @Transactional(readOnly = true) + fun listRequestsForMap(query: RequestListFilter): RequestMapPageDto { + val requests = findRequests(query) + return RequestMapPageDto( + items = requests.content.map { request -> request.toMapItemResponse() }, + page = query.page, + size = query.size, + totalItems = requests.totalElements, + totalPages = requests.totalPages, + ) + } + @Transactional(readOnly = true) fun getRequestById(id: UUID): RequestResponseDto? { val request = requestRepository.findById(id).orElse(null) ?: return null @@ -160,7 +162,6 @@ class RequestService( requestCellRepository.deleteByRequestId(request.id) return DeleteRequestResponseDto( id = request.id, - status = DeleteRequestStatusDto.DELETED, deletedAt = deletedAt.toOffsetDateTime(), ) } @@ -197,6 +198,21 @@ class RequestService( } } + private fun findRequests(query: RequestListFilter): Page = + requestRepository.findRequests( + includeDeleted = query.includeDeleted, + deletedStatus = RequestStatus.DELETED, + status = query.status?.toEntity(), + surveyType = query.surveyType?.toEntity(), + kpp = query.kpp, + highPriorityTransmit = query.highPriorityTransmit, + beginFrom = query.beginFrom?.toUtcLocalDateTime(), + beginTo = query.beginTo?.toUtcLocalDateTime(), + endFrom = query.endFrom?.toUtcLocalDateTime(), + endTo = query.endTo?.toUtcLocalDateTime(), + pageable = PageRequest.of(query.page, query.size, query.sort.toRequestSort()), + ) + private fun RequestEntity.toResponse(): RequestResponseDto { val optics = requestOpticsParamsRepository.findById(id).orElse(null)?.toDto() val rsa = requestRsaParamsRepository.findById(id).orElse(null)?.toDto() @@ -247,6 +263,25 @@ class RequestService( ) } + private fun RequestEntity.toMapItemResponse(): RequestMapItemDto { + return RequestMapItemDto( + id = id, + name = name, + status = status.toDto(), + surveyType = surveyType.toDto(), + geometry = geometry, + importance = importance, + beginDateTime = beginDateTime.toOffsetDateTime(), + endDateTime = endDateTime.toOffsetDateTime(), + kpp = kpp, + highPriorityTransmit = highPriorityTransmit, + coveragePercent = coverage().currentPercent, + createdAt = createdAt.toOffsetDateTime(), + updatedAt = updatedAt.toOffsetDateTime(), + deletedAt = deletedAt?.toOffsetDateTime(), + ) + } + private fun RequestEntity.coverage(): CoverageStateDto { return CoverageStateDto( requiredPercent = coverageRequiredPercent, @@ -254,7 +289,7 @@ class RequestService( ) } - private fun CreateRequestRequestDto.surveyType(): RequestSurveyType { + private fun CreateRequestDto.surveyType(): RequestSurveyType { return when { optics != null && rsa != null -> RequestSurveyType.COMBINED optics != null -> RequestSurveyType.OPTICS diff --git a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/CellsControllerTest.kt b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/CellsControllerTest.kt index 90c921e..362c69c 100644 --- a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/CellsControllerTest.kt +++ b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/CellsControllerTest.kt @@ -133,8 +133,8 @@ class CellsControllerTest { } @Test - fun `get v1 cells with requests returns request fragments`() { - mockMvc.perform(get("/v1/cells/with-requests").accept(MediaType.APPLICATION_JSON)) + fun `get v1 cells priority map returns request fragments`() { + mockMvc.perform(get("/v1/cells/priority-map").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk) .andExpect(jsonPath("$.items").isArray) .andExpect(jsonPath("$.items[0].cellNum").value(42)) @@ -150,9 +150,9 @@ class CellsControllerTest { } @Test - fun `get v1 cells with requests rejects partial aggregation parameters`() { + fun `get v1 cells priority map rejects partial aggregation parameters`() { mockMvc.perform( - get("/v1/cells/with-requests") + get("/v1/cells/priority-map") .queryParam("countLat", "2") .accept(MediaType.APPLICATION_JSON), ) @@ -162,9 +162,9 @@ class CellsControllerTest { } @Test - fun `get v1 cells with requests rejects invalid numeric parameters`() { + fun `get v1 cells priority map rejects invalid numeric parameters`() { mockMvc.perform( - get("/v1/cells/with-requests") + get("/v1/cells/priority-map") .queryParam("minImportance", "-1") .accept(MediaType.APPLICATION_JSON), ) @@ -173,7 +173,7 @@ class CellsControllerTest { .andExpect(jsonPath("$.message").value("minImportance must be greater than or equal to 0")) mockMvc.perform( - get("/v1/cells/with-requests") + get("/v1/cells/priority-map") .queryParam("countLat", "0") .queryParam("countLong", "1") .accept(MediaType.APPLICATION_JSON), @@ -184,9 +184,9 @@ class CellsControllerTest { } @Test - fun `get v1 cells with requests accepts complete aggregation parameters`() { + fun `get v1 cells priority map accepts complete aggregation parameters`() { mockMvc.perform( - get("/v1/cells/with-requests") + get("/v1/cells/priority-map") .queryParam("countLat", "1") .queryParam("countLong", "1") .accept(MediaType.APPLICATION_JSON), diff --git a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/RequestControllerOpenApiSkeletonTest.kt b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/RequestControllerOpenApiSkeletonTest.kt index c29db4d..c91a450 100644 --- a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/RequestControllerOpenApiSkeletonTest.kt +++ b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/controller/RequestControllerOpenApiSkeletonTest.kt @@ -11,6 +11,7 @@ import org.mockito.ArgumentMatchers.isNull import org.mockito.Mockito.doAnswer import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock +import org.nstart.dep265.requestservice.dto.CreateRequestDto import org.nstart.dep265.requestservice.entity.EarthCellEntity import org.nstart.dep265.requestservice.entity.RequestEntity import org.nstart.dep265.requestservice.entity.RequestCellEntity @@ -345,6 +346,32 @@ class RequestControllerOpenApiSkeletonTest { .andExpect(jsonPath("$.items[0].importance").value(10.0)) } + @Test + fun `get v1 requests map returns map items with geometry`() { + doReturn(PageImpl(listOf(existingRequest()))) + .`when`(requestRepository) + .findRequests( + anyBoolean(), + deletedStatusMatcher(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + pageableMatcher(), + ) + + mockMvc.perform(get("/v1/requests/map").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk) + .andExpect(jsonPath("$.items[0].name").value("Тест1")) + .andExpect(jsonPath("$.items[0].geometry").value("POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))")) + .andExpect(jsonPath("$.items[0].importance").value(10.0)) + .andExpect(jsonPath("$.items[0].coveragePercent").value(50.0)) + } + @Test fun `get v1 requests rejects unsupported sort field`() { mockMvc.perform( @@ -370,7 +397,7 @@ class RequestControllerOpenApiSkeletonTest { } @Test - fun `get v1 request with cells returns request and cell projection fields`() { + fun `get v1 request cells returns request and cell projection fields`() { val id = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6") val earthCell = EarthCellEntity( cellId = 1, @@ -397,7 +424,7 @@ class RequestControllerOpenApiSkeletonTest { .`when`(requestCellRepository) .findWithCellByRequestId(id) - mockMvc.perform(get("/v1/requests/$id/with-cells").accept(MediaType.APPLICATION_JSON)) + mockMvc.perform(get("/v1/requests/$id/cells").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk) .andExpect(jsonPath("$.request.id").value(id.toString())) .andExpect(jsonPath("$.request.name").value("Тест1")) @@ -411,7 +438,7 @@ class RequestControllerOpenApiSkeletonTest { } @Test - fun `get v1 request with cells returns empty cells for request without projections`() { + fun `get v1 request cells returns empty cells for request without projections`() { val id = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6") doReturn(Optional.of(existingRequest())) .`when`(requestRepository) @@ -420,14 +447,14 @@ class RequestControllerOpenApiSkeletonTest { .`when`(requestCellRepository) .findWithCellByRequestId(id) - mockMvc.perform(get("/v1/requests/$id/with-cells").accept(MediaType.APPLICATION_JSON)) + mockMvc.perform(get("/v1/requests/$id/cells").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk) .andExpect(jsonPath("$.request.id").value(id.toString())) .andExpect(jsonPath("$.cells").isEmpty) } @Test - fun `get v1 request with cells returns deleted request with empty cells`() { + fun `get v1 request cells returns deleted request with empty cells`() { val id = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6") val request = existingRequest().also { existingRequest -> existingRequest.status = RequestStatus.DELETED @@ -440,7 +467,7 @@ class RequestControllerOpenApiSkeletonTest { .`when`(requestCellRepository) .findWithCellByRequestId(id) - mockMvc.perform(get("/v1/requests/$id/with-cells").accept(MediaType.APPLICATION_JSON)) + mockMvc.perform(get("/v1/requests/$id/cells").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk) .andExpect(jsonPath("$.request.status").value("DELETED")) .andExpect(jsonPath("$.request.deletedAt").exists()) @@ -454,7 +481,7 @@ class RequestControllerOpenApiSkeletonTest { mockMvc.perform(get("/v1/requests/$id").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound) - mockMvc.perform(get("/v1/requests/$id/with-cells").accept(MediaType.APPLICATION_JSON)) + mockMvc.perform(get("/v1/requests/$id/cells").accept(MediaType.APPLICATION_JSON)) .andExpect(status().isNotFound) mockMvc.perform(delete("/v1/requests/$id").accept(MediaType.APPLICATION_JSON)) diff --git a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtoContractTest.kt b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtoContractTest.kt index 85984fe..fcec078 100644 --- a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtoContractTest.kt +++ b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/dto/RequestApiDtoContractTest.kt @@ -18,7 +18,7 @@ class RequestApiDtoContractTest { @Test fun `create request dto matches openapi input fields`() { - val request = objectMapper.readValue( + val request = objectMapper.readValue( """ { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", @@ -66,14 +66,14 @@ class RequestApiDtoContractTest { """.trimIndent() assertFailsWith { - objectMapper.readValue(payloadWithSurveyType) + objectMapper.readValue(payloadWithSurveyType) } } @Test fun `create request serialization does not expose survey type`() { val json = objectMapper.writeValueAsString( - CreateRequestRequestDto( + CreateRequestDto( id = UUID.fromString("3fa85f64-5717-4562-b3fc-2c963f66afa6"), name = "Тест1", geometry = "POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))", diff --git a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceJpaTest.kt b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceJpaTest.kt index 07faf2c..6dfdaa5 100644 --- a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceJpaTest.kt +++ b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceJpaTest.kt @@ -212,7 +212,7 @@ class CellsQueryServiceJpaTest @Autowired constructor( ) entityManager.clear() - val response = service.listCellsWithRequests( + val response = service.listCellsPriorityMap( minImportance = null, countLat = null, countLong = null, @@ -234,7 +234,7 @@ class CellsQueryServiceJpaTest @Autowired constructor( saveCell(cellNum = 11, importance = 2.0) entityManager.clear() - val response = service.listCellsWithRequests( + val response = service.listCellsPriorityMap( minImportance = 2.0, countLat = null, countLong = null, @@ -249,7 +249,7 @@ class CellsQueryServiceJpaTest @Autowired constructor( saveCell(cellNum = 11, importance = 1.0) entityManager.clear() - val response = service.listCellsWithRequests( + val response = service.listCellsPriorityMap( minImportance = 0.0, countLat = null, countLong = null, @@ -264,7 +264,7 @@ class CellsQueryServiceJpaTest @Autowired constructor( saveCell(cellNum = 11, importance = 0.002) entityManager.clear() - val response = service.listCellsWithRequests( + val response = service.listCellsPriorityMap( minImportance = 0.001, countLat = null, countLong = null, diff --git a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceTest.kt b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceTest.kt index 3d4637d..042644b 100644 --- a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceTest.kt +++ b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/CellsQueryServiceTest.kt @@ -121,7 +121,7 @@ class CellsQueryServiceTest { } val service = service(earthCellRepository) - val response = service.listCellsWithRequests( + val response = service.listCellsPriorityMap( minImportance = null, countLat = null, countLong = null, @@ -149,7 +149,7 @@ class CellsQueryServiceTest { } val service = service(earthCellRepository) - val response = service.listCellsWithRequests( + val response = service.listCellsPriorityMap( minImportance = 2.0, countLat = null, countLong = null, @@ -164,7 +164,7 @@ class CellsQueryServiceTest { val service = service(emptyCellsRepository()) assertThrows { - service.listCellsWithRequests( + service.listCellsPriorityMap( minImportance = null, countLat = 2, countLong = null, @@ -172,7 +172,7 @@ class CellsQueryServiceTest { } assertThrows { - service.listCellsWithRequests( + service.listCellsPriorityMap( minImportance = null, countLat = null, countLong = 2, @@ -194,7 +194,7 @@ class CellsQueryServiceTest { } val service = service(earthCellRepository) - val response = service.listCellsWithRequests( + val response = service.listCellsPriorityMap( minImportance = null, countLat = 1, countLong = 2, diff --git a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceListRequestsJpaTest.kt b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceListRequestsJpaTest.kt index b78136c..50d8f8b 100644 --- a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceListRequestsJpaTest.kt +++ b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceListRequestsJpaTest.kt @@ -2,7 +2,7 @@ package org.nstart.dep265.requestservice.service import jakarta.persistence.EntityManager import org.junit.jupiter.api.Test -import org.nstart.dep265.requestservice.dto.ListRequestsQueryDto +import org.nstart.dep265.requestservice.dto.RequestListFilter import org.nstart.dep265.requestservice.dto.RequestStatusDto import org.nstart.dep265.requestservice.dto.SurveyTypeDto import org.nstart.dep265.requestservice.PcpRequestServiceApplication @@ -57,7 +57,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( val activeId = saveRequest(status = RequestStatus.ACTIVE, createdAt = "2026-01-02T00:00:00") saveRequest(status = RequestStatus.DELETED, createdAt = "2026-01-03T00:00:00") - val response = service.listRequests(ListRequestsQueryDto()) + val response = service.listRequests(RequestListFilter()) assertEquals(listOf(activeId), response.items.map { request -> request.id }) assertEquals("Тест1", response.items.single().name) @@ -65,12 +65,25 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( assertEquals(1, response.totalPages) } + @Test + fun `list requests with geometry returns geometry and keeps list filters`() { + val activeId = saveRequest(status = RequestStatus.ACTIVE, createdAt = "2026-01-02T00:00:00") + saveRequest(status = RequestStatus.DELETED, createdAt = "2026-01-03T00:00:00") + + val response = service.listRequestsForMap(RequestListFilter()) + + assertEquals(listOf(activeId), response.items.map { request -> request.id }) + assertEquals("POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))", response.items.single().geometry) + assertEquals(1, response.totalItems) + assertEquals(1, response.totalPages) + } + @Test fun `list requests includes deleted when includeDeleted is true`() { val activeId = saveRequest(status = RequestStatus.ACTIVE, createdAt = "2026-01-02T00:00:00") val deletedId = saveRequest(status = RequestStatus.DELETED, createdAt = "2026-01-03T00:00:00") - val response = service.listRequests(ListRequestsQueryDto(includeDeleted = true)) + val response = service.listRequests(RequestListFilter(includeDeleted = true)) assertEquals(listOf(deletedId, activeId), response.items.map { request -> request.id }) assertEquals(2, response.totalItems) @@ -81,7 +94,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( val activeId = saveRequest(status = RequestStatus.ACTIVE) saveRequest(status = RequestStatus.ACCEPTED) - val response = service.listRequests(ListRequestsQueryDto(status = RequestStatusDto.ACTIVE)) + val response = service.listRequests(RequestListFilter(status = RequestStatusDto.ACTIVE)) assertEquals(listOf(activeId), response.items.map { request -> request.id }) } @@ -91,7 +104,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( val rsaId = saveRequest(surveyType = RequestSurveyType.RSA) saveRequest(surveyType = RequestSurveyType.OPTICS) - val response = service.listRequests(ListRequestsQueryDto(surveyType = SurveyTypeDto.RSA)) + val response = service.listRequests(RequestListFilter(surveyType = SurveyTypeDto.RSA)) assertEquals(listOf(rsaId), response.items.map { request -> request.id }) } @@ -101,7 +114,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( val matchedId = saveRequest(kpp = mutableListOf(1, 2)) saveRequest(kpp = mutableListOf(3, 4)) - val response = service.listRequests(ListRequestsQueryDto(kpp = 2)) + val response = service.listRequests(RequestListFilter(kpp = 2)) assertEquals(listOf(matchedId), response.items.map { request -> request.id }) } @@ -111,7 +124,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( val highPriorityId = saveRequest(highPriorityTransmit = true) saveRequest(highPriorityTransmit = false) - val response = service.listRequests(ListRequestsQueryDto(highPriorityTransmit = true)) + val response = service.listRequests(RequestListFilter(highPriorityTransmit = true)) assertEquals(listOf(highPriorityId), response.items.map { request -> request.id }) } @@ -123,7 +136,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( saveRequest(beginDateTime = "2026-02-01T00:00:00", endDateTime = "2026-02-28T00:00:00") val response = service.listRequests( - ListRequestsQueryDto( + RequestListFilter( beginFrom = OffsetDateTime.parse("2026-01-10T00:00:00Z"), beginTo = OffsetDateTime.parse("2026-01-31T23:59:59Z"), ) @@ -139,7 +152,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( saveRequest(endDateTime = "2026-02-01T00:00:00") val response = service.listRequests( - ListRequestsQueryDto( + RequestListFilter( endFrom = OffsetDateTime.parse("2026-01-10T00:00:00Z"), endTo = OffsetDateTime.parse("2026-01-31T23:59:59Z"), ) @@ -154,7 +167,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( saveRequest(createdAt = "2026-01-02T00:00:00") val oldestId = saveRequest(createdAt = "2026-01-01T00:00:00") - val response = service.listRequests(ListRequestsQueryDto(page = 1, size = 2)) + val response = service.listRequests(RequestListFilter(page = 1, size = 2)) assertEquals(listOf(oldestId), response.items.map { request -> request.id }) assertEquals(1, response.page) @@ -169,7 +182,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( val newestId = saveRequest(createdAt = "2026-01-03T00:00:00") val middleId = saveRequest(createdAt = "2026-01-02T00:00:00") - val response = service.listRequests(ListRequestsQueryDto()) + val response = service.listRequests(RequestListFilter()) assertEquals(listOf(newestId, middleId, oldestId), response.items.map { request -> request.id }) } @@ -179,7 +192,7 @@ class RequestServiceListRequestsJpaTest @Autowired constructor( val latestBeginId = saveRequest(beginDateTime = "2026-01-03T00:00:00") val earliestBeginId = saveRequest(beginDateTime = "2026-01-01T00:00:00") - val response = service.listRequests(ListRequestsQueryDto(sort = "beginDateTime,asc")) + val response = service.listRequests(RequestListFilter(sort = "beginDateTime,asc")) assertEquals(listOf(earliestBeginId, latestBeginId), response.items.map { request -> request.id }) } diff --git a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceTest.kt b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceTest.kt index 0139de1..d6e071e 100644 --- a/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceTest.kt +++ b/services/pcp-request-service/src/test/kotlin/org/nstart/dep265/requestservice/service/RequestServiceTest.kt @@ -11,8 +11,8 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoInteractions -import org.nstart.dep265.requestservice.dto.CreateRequestRequestDto -import org.nstart.dep265.requestservice.dto.ListRequestsQueryDto +import org.nstart.dep265.requestservice.dto.CreateRequestDto +import org.nstart.dep265.requestservice.dto.RequestListFilter import org.nstart.dep265.requestservice.dto.OpticsParamsDto import org.nstart.dep265.requestservice.dto.OpticsResultTypeDto import org.nstart.dep265.requestservice.entity.EarthCellEntity @@ -215,12 +215,38 @@ class RequestServiceTest { pageableMatcher(), ) - val response = service.listRequests(ListRequestsQueryDto()) + val response = service.listRequests(RequestListFilter()) assertEquals(REQUEST_NAME, response.items.single().name) assertEquals(REQUEST_IMPORTANCE, response.items.single().importance) } + @Test + fun `list requests with geometry returns expanded summary`() { + doReturn(PageImpl(listOf(existingRequest()))) + .`when`(requestRepository) + .findRequests( + anyBoolean(), + deletedStatusMatcher(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + isNull(), + pageableMatcher(), + ) + + val response = service.listRequestsForMap(RequestListFilter()) + + assertEquals(REQUEST_NAME, response.items.single().name) + assertEquals("POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))", response.items.single().geometry) + assertEquals(REQUEST_IMPORTANCE, response.items.single().importance) + assertEquals(50.0, response.items.single().coveragePercent) + } + @Test fun `create request rejects duplicate id without touching existing request state`() { doReturn(true) @@ -254,8 +280,8 @@ class RequestServiceTest { verify(requestCellRepository).deleteByRequestId(REQUEST_ID) } - private fun createRequestDto(): CreateRequestRequestDto { - return CreateRequestRequestDto( + private fun createRequestDto(): CreateRequestDto { + return CreateRequestDto( id = REQUEST_ID, name = REQUEST_NAME, geometry = "POLYGON ((37.5 55.5, 37.7 55.5, 37.7 55.7, 37.5 55.7, 37.5 55.5))", diff --git a/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt b/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt index 26583e7..9c2a2ee 100644 --- a/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt +++ b/services/pcp-ui-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt @@ -52,7 +52,7 @@ class EarthService( fun reqcells(id : String): RequestWithCellsDTO? = webClientBuilder.build() .get() - .uri("$url/v1/requests/$id/with-cells") + .uri("$url/v1/requests/$id/cells") .retrieve() .bodyToMono(RequestWithCellsResponseDto::class.java) .block() diff --git a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/CoverageSchemeServiceTest.kt b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/CoverageSchemeServiceTest.kt index ef1e07f..9a18c62 100644 --- a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/CoverageSchemeServiceTest.kt +++ b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/CoverageSchemeServiceTest.kt @@ -38,7 +38,7 @@ class CoverageSchemeServiceTest { val timeStop = LocalDateTime.of(2026, 4, 14, 12, 0) val serverUrl = startServer( mapOf( - "/v1/requests/req-1/with-cells" to stringHandler(200, requestWithCellsJson()), + "/v1/requests/req-1/cells" to stringHandler(200, requestWithCellsJson()), "/api/coverage-schemes/calculate" to jsonHandler( CoverageSchemeResponseDTO( targetPolygonWkt = "POLYGON ((30 10, 40 40, 20 40, 30 10))", @@ -111,7 +111,7 @@ class CoverageSchemeServiceTest { fun `requestCoverageScheme converts backend error to CustomErrorException`() { val serverUrl = startServer( mapOf( - "/v1/requests/req-1/with-cells" to stringHandler(200, requestWithCellsJson()), + "/v1/requests/req-1/cells" to stringHandler(200, requestWithCellsJson()), "/api/coverage-schemes/calculate" to stringHandler(400, """{"message":"coverage failed"}""") ) ) diff --git a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt index 7c004b1..875f1c8 100644 --- a/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt +++ b/services/pcp-ui-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt @@ -377,7 +377,7 @@ class EarthServiceTest { service.reqcells(requestId.toString()) - assertEquals("/v1/requests/$requestId/with-cells", requestedUris.single().path) + assertEquals("/v1/requests/$requestId/cells", requestedUris.single().path) assertFalse(requestedUris.any { uri -> uri.path.startsWith("/api/") }) } diff --git a/services/slots-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt b/services/slots-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt index afa5901..1f07834 100644 --- a/services/slots-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt +++ b/services/slots-service/src/main/kotlin/space/nstart/pcp/slots_service/service/EarthService.kt @@ -49,7 +49,7 @@ class EarthService( fun reqcells(id : String): RequestWithCellsDTO? = webClientBuilder.build() .get() - .uri("$url/v1/requests/$id/with-cells") + .uri("$url/v1/requests/$id/cells") .retrieve() .bodyToMono(RequestWithCellsResponseDto::class.java) .block() diff --git a/services/slots-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt b/services/slots-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt index 3f0f307..dc0f03c 100644 --- a/services/slots-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt +++ b/services/slots-service/src/test/kotlin/space/nstart/pcp/slots_service/service/EarthServiceTest.kt @@ -115,7 +115,7 @@ class EarthServiceTest { val response = service.reqcells(requestId.toString())!! - assertEquals("/v1/requests/$requestId/with-cells", requestedUris.single().path) + assertEquals("/v1/requests/$requestId/cells", requestedUris.single().path) assertFalse(requestedUris.any { uri -> uri.path.contains("/api/requests/by-number") }) assertEquals(requestId, response.request.requestId) assertEquals("Request with cells", response.request.name)