From f454b4991b8db16ef113f32acdd87b8c97c5ee52 Mon Sep 17 00:00:00 2001 From: Jason Yoo Date: Mon, 7 Oct 2024 23:38:01 +0900 Subject: [PATCH 1/4] feat: admin api impl --- api-admin/api-spec.yaml | 89 ++++++++++++++++++ .../in/AcceptClosedPlaceCandidateUseCase.kt | 33 +++++++ .../port/in/GetClosedPlaceCandidateUseCase.kt | 29 ++++++ .../in/IgnoreClosedPlaceCandidateUseCase.kt | 33 +++++++ .../in/ListClosedPlaceCandidatesUseCase.kt | 91 +++++++++++++++++++ .../ClosedPlaceCandidateRepository.kt | 18 ++++ .../result/NamedClosedPlaceCandidate.kt | 12 +++ .../domain/model/ClosedPlaceCandidate.kt | 9 ++ .../adapter/in/controller/AdminConverters.kt | 13 +++ .../in/controller/AdminPlaceController.kt | 46 ++++++++++ .../V33__closed_place_candidate_table.sql | 3 +- 11 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt create mode 100644 app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/GetClosedPlaceCandidateUseCase.kt create mode 100644 app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/IgnoreClosedPlaceCandidateUseCase.kt create mode 100644 app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/ListClosedPlaceCandidatesUseCase.kt create mode 100644 app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/result/NamedClosedPlaceCandidate.kt create mode 100644 app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminConverters.kt diff --git a/api-admin/api-spec.yaml b/api-admin/api-spec.yaml index 075608bc..9beea869 100644 --- a/api-admin/api-spec.yaml +++ b/api-admin/api-spec.yaml @@ -431,6 +431,63 @@ paths: responses: '204': {} + /closedPlaceCandidates: + get: + operationId: listClosedPlaceCandidates + summary: 폐업이 추정되는 장소의 리스트를 조회한다. + parameters: + - in: query + name: cursor + schema: + type: string + required: false + - in: query + name: limit + description: default 값은 50으로 설정된다. + schema: + type: string + required: false + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AdminListClosedPlaceCandidatesResponseDTO' + + /closedPlaceCandidates/{id}: + get: + operationId: getClosedPlaceCandidate + summary: 폐업이 추정되는 장소를 조회한다. + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AdminClosedPlaceCandidateDTO' + + /closedPlaceCandidates/{id}/accept: + put: + operationId: acceptClosedPlaceCandidate + summary: 해당 장소를 폐업 상태로 변경한다. + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AdminClosedPlaceCandidateDTO' + + /closedPlaceCandidates/{id}/ignore: + put: + operationId: ignoreClosedPlaceCandidate + summary: 해당 장소를 폐업의 폐업 추정을 무시한다. + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AdminClosedPlaceCandidateDTO' + + components: # Reusable schemas (data models) schemas: @@ -924,3 +981,35 @@ components: $ref: '#/components/schemas/LocationDTO' required: - boundaryVertices + + AdminListClosedPlaceCandidatesResponseDTO: + type: object + properties: + list: + type: array + items: + $ref: '#/components/schemas/AdminClosedPlaceCandidateDTO' + cursor: + type: string + description: 없으면 다음 페이지가 없다는 의미. + required: + - list + + AdminClosedPlaceCandidateDTO: + type: object + properties: + id: + type: string + placeId: + type: string + name: + type: string + address: + type: string + acceptedAt: + $ref: '#/components/schemas/EpochMillisTimestamp' + ignoredAt: + $ref: '#/components/schemas/EpochMillisTimestamp' + required: + - id + - placeId \ No newline at end of file diff --git a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt new file mode 100644 index 00000000..28a49dcd --- /dev/null +++ b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt @@ -0,0 +1,33 @@ +package club.staircrusher.place.application.port.`in` + +import club.staircrusher.place.application.port.out.persistence.ClosedPlaceCandidateRepository +import club.staircrusher.place.application.port.out.persistence.PlaceRepository +import club.staircrusher.place.application.result.NamedClosedPlaceCandidate +import club.staircrusher.stdlib.di.annotation.Component +import club.staircrusher.stdlib.persistence.TransactionManager +import org.springframework.data.repository.findByIdOrNull + +@Component +class AcceptClosedPlaceCandidateUseCase( + private val transactionManager: TransactionManager, + private val closedPlaceCandidateRepository: ClosedPlaceCandidateRepository, + private val placeRepository: PlaceRepository, +) { + fun handle(candidateId: String) = transactionManager.doInTransaction { + val candidate = closedPlaceCandidateRepository.findByIdOrNull(candidateId) + ?: throw IllegalArgumentException("closed place candidate with id($candidateId) not found") + + candidate.accept() + closedPlaceCandidateRepository.save(candidate) + + val place = placeRepository.findByIdOrNull(candidate.placeId)!! + return@doInTransaction NamedClosedPlaceCandidate( + candidateId = candidate.id, + placeId = place.id, + name = place.name, + address = place.address.toString(), + acceptedAt = candidate.acceptedAt, + ignoredAt = candidate.ignoredAt, + ) + } +} diff --git a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/GetClosedPlaceCandidateUseCase.kt b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/GetClosedPlaceCandidateUseCase.kt new file mode 100644 index 00000000..365a6d86 --- /dev/null +++ b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/GetClosedPlaceCandidateUseCase.kt @@ -0,0 +1,29 @@ +package club.staircrusher.place.application.port.`in` + +import club.staircrusher.place.application.port.out.persistence.ClosedPlaceCandidateRepository +import club.staircrusher.place.application.port.out.persistence.PlaceRepository +import club.staircrusher.place.application.result.NamedClosedPlaceCandidate +import club.staircrusher.stdlib.di.annotation.Component +import club.staircrusher.stdlib.persistence.TransactionManager +import org.springframework.data.repository.findByIdOrNull + +@Component +class GetClosedPlaceCandidateUseCase( + private val transactionManager: TransactionManager, + private val closedPlaceCandidateRepository: ClosedPlaceCandidateRepository, + private val placeRepository: PlaceRepository, +) { + fun handle(candidateId: String) = transactionManager.doInTransaction { + val candidate = closedPlaceCandidateRepository.findByIdOrNull(candidateId) ?: return@doInTransaction null + val place = placeRepository.findByIdOrNull(candidate.placeId) ?: return@doInTransaction null + + return@doInTransaction NamedClosedPlaceCandidate( + candidateId = candidate.id, + placeId = place.id, + name = place.name, + address = place.address.toString(), + acceptedAt = candidate.acceptedAt, + ignoredAt = candidate.ignoredAt, + ) + } +} diff --git a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/IgnoreClosedPlaceCandidateUseCase.kt b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/IgnoreClosedPlaceCandidateUseCase.kt new file mode 100644 index 00000000..852180a0 --- /dev/null +++ b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/IgnoreClosedPlaceCandidateUseCase.kt @@ -0,0 +1,33 @@ +package club.staircrusher.place.application.port.`in` + +import club.staircrusher.place.application.port.out.persistence.ClosedPlaceCandidateRepository +import club.staircrusher.place.application.port.out.persistence.PlaceRepository +import club.staircrusher.place.application.result.NamedClosedPlaceCandidate +import club.staircrusher.stdlib.di.annotation.Component +import club.staircrusher.stdlib.persistence.TransactionManager +import org.springframework.data.repository.findByIdOrNull + +@Component +class IgnoreClosedPlaceCandidateUseCase( + private val transactionManager: TransactionManager, + private val closedPlaceCandidateRepository: ClosedPlaceCandidateRepository, + private val placeRepository: PlaceRepository, +) { + fun handle(candidateId: String) = transactionManager.doInTransaction { + val candidate = closedPlaceCandidateRepository.findByIdOrNull(candidateId) + ?: throw IllegalArgumentException("closed place candidate with id($candidateId) not found") + + candidate.ignore() + closedPlaceCandidateRepository.save(candidate) + + val place = placeRepository.findByIdOrNull(candidate.placeId)!! + return@doInTransaction NamedClosedPlaceCandidate( + candidateId = candidate.id, + placeId = place.id, + name = place.name, + address = place.address.toString(), + acceptedAt = candidate.acceptedAt, + ignoredAt = candidate.ignoredAt, + ) + } +} diff --git a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/ListClosedPlaceCandidatesUseCase.kt b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/ListClosedPlaceCandidatesUseCase.kt new file mode 100644 index 00000000..a83ef050 --- /dev/null +++ b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/ListClosedPlaceCandidatesUseCase.kt @@ -0,0 +1,91 @@ +package club.staircrusher.place.application.port.`in` + +import club.staircrusher.place.application.port.out.persistence.ClosedPlaceCandidateRepository +import club.staircrusher.place.application.port.out.persistence.PlaceRepository +import club.staircrusher.place.application.result.NamedClosedPlaceCandidate +import club.staircrusher.place.domain.model.ClosedPlaceCandidate +import club.staircrusher.stdlib.di.annotation.Component +import club.staircrusher.stdlib.persistence.TimestampCursor +import club.staircrusher.stdlib.persistence.TransactionManager +import org.springframework.data.domain.PageRequest +import org.springframework.data.domain.Sort +import java.time.Instant + +@Component +class ListClosedPlaceCandidatesUseCase( + private val transactionManager: TransactionManager, + private val closedPlaceCandidateRepository: ClosedPlaceCandidateRepository, + private val placeRepository: PlaceRepository, +) { + fun handle( + limit: Int?, + cursorValue: String?, + ) = transactionManager.doInTransaction { + val cursor = cursorValue?.let { Cursor.parse(it) } ?: Cursor.initial() + val normalizedLimit = limit ?: DEFAULT_LIMIT + + val pageRequest = PageRequest.of( + 0, + normalizedLimit, + Sort.by( + listOf( + Sort.Order.desc("createdAt"), + Sort.Order.desc("id"), + ), + ), + ) + val result = closedPlaceCandidateRepository.findCursored( + cursorCreatedAt = cursor.timestamp, + cursorId = cursor.id, + pageable = pageRequest, + ) + + val nextCursor = if (result.hasNext()) { + Cursor(result.content[normalizedLimit - 1]) + } else { + null + } + + val placeIds = result.content.map { it.placeId } + val places = placeRepository.findAllByIdIn(placeIds) + return@doInTransaction ListClosedPlaceCandidatesResult( + candidates = result.mapNotNull { candidate -> + val place = places.find { it.id == candidate.placeId } ?: return@mapNotNull null + NamedClosedPlaceCandidate( + candidateId = candidate.id, + placeId = place.id, + name = place.name, + address = place.address.toString(), + acceptedAt = candidate.acceptedAt, + ignoredAt = candidate.ignoredAt, + ) + }, + nextCursor = nextCursor?.value, + ) + } + + data class ListClosedPlaceCandidatesResult( + val candidates: List, + val nextCursor: String?, + ) + + private data class Cursor( + val createdAt: Instant, + val candidateId: String, + ) : TimestampCursor(createdAt, candidateId) { + constructor(candidate: ClosedPlaceCandidate) : this( + createdAt = candidate.createdAt, + candidateId = candidate.id, + ) + + companion object { + fun parse(cursorValue: String) = TimestampCursor.parse(cursorValue) + + fun initial() = TimestampCursor.initial() + } + } + + companion object { + private const val DEFAULT_LIMIT = 50 + } +} diff --git a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/out/persistence/ClosedPlaceCandidateRepository.kt b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/out/persistence/ClosedPlaceCandidateRepository.kt index 2bf64355..8fda6c59 100644 --- a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/out/persistence/ClosedPlaceCandidateRepository.kt +++ b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/out/persistence/ClosedPlaceCandidateRepository.kt @@ -1,8 +1,26 @@ package club.staircrusher.place.application.port.out.persistence import club.staircrusher.place.domain.model.ClosedPlaceCandidate +import org.springframework.data.domain.Page +import org.springframework.data.domain.Pageable +import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.CrudRepository +import java.time.Instant interface ClosedPlaceCandidateRepository : CrudRepository { + @Query(""" + SELECT c + FROM ClosedPlaceCandidate c + WHERE + ( + (c.createdAt = :cursorCreatedAt AND c.id < :cursorId) + OR (c.createdAt < :cursorCreatedAt) + ) + """) + fun findCursored( + cursorCreatedAt: Instant, + cursorId: String, + pageable: Pageable, + ): Page fun findByExternalIdIn(externalIds: List): List } diff --git a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/result/NamedClosedPlaceCandidate.kt b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/result/NamedClosedPlaceCandidate.kt new file mode 100644 index 00000000..44486a94 --- /dev/null +++ b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/result/NamedClosedPlaceCandidate.kt @@ -0,0 +1,12 @@ +package club.staircrusher.place.application.result + +import java.time.Instant + +data class NamedClosedPlaceCandidate( + val candidateId: String, + val placeId: String, + val name: String, + val address: String, + val acceptedAt: Instant?, + val ignoredAt: Instant?, +) diff --git a/app-server/subprojects/bounded_context/place/domain/src/main/kotlin/club/staircrusher/place/domain/model/ClosedPlaceCandidate.kt b/app-server/subprojects/bounded_context/place/domain/src/main/kotlin/club/staircrusher/place/domain/model/ClosedPlaceCandidate.kt index ef04c838..b835c6e0 100644 --- a/app-server/subprojects/bounded_context/place/domain/src/main/kotlin/club/staircrusher/place/domain/model/ClosedPlaceCandidate.kt +++ b/app-server/subprojects/bounded_context/place/domain/src/main/kotlin/club/staircrusher/place/domain/model/ClosedPlaceCandidate.kt @@ -1,5 +1,6 @@ package club.staircrusher.place.domain.model +import club.staircrusher.stdlib.clock.SccClock import club.staircrusher.stdlib.persistence.jpa.TimeAuditingBaseEntity import jakarta.persistence.Column import jakarta.persistence.Entity @@ -23,6 +24,14 @@ class ClosedPlaceCandidate( @Column(nullable = true) var ignoredAt: Instant? = null, ) : TimeAuditingBaseEntity() { + fun accept() { + acceptedAt = SccClock.instant() + } + + fun ignore() { + ignoredAt = SccClock.instant() + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminConverters.kt b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminConverters.kt new file mode 100644 index 00000000..b697e024 --- /dev/null +++ b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminConverters.kt @@ -0,0 +1,13 @@ +package club.staircrusher.place.infra.adapter.`in`.controller + +import club.staircrusher.admin_api.converter.toDTO +import club.staircrusher.place.application.result.NamedClosedPlaceCandidate + +fun NamedClosedPlaceCandidate.toAdminDTO() = club.staircrusher.admin_api.spec.dto.AdminClosedPlaceCandidateDTO( + id = candidateId, + placeId = placeId, + name = name, + address = address, + acceptedAt = acceptedAt?.toDTO(), + ignoredAt = ignoredAt?.toDTO(), +) diff --git a/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt index 2396dff1..9e435dec 100644 --- a/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt +++ b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt @@ -1,18 +1,64 @@ package club.staircrusher.place.infra.adapter.`in`.controller import club.staircrusher.admin_api.converter.toModel +import club.staircrusher.admin_api.spec.dto.AdminClosedPlaceCandidateDTO +import club.staircrusher.admin_api.spec.dto.AdminListClosedPlaceCandidatesResponseDTO import club.staircrusher.admin_api.spec.dto.StartPlaceCrawlingRequestDTO +import club.staircrusher.place.application.port.`in`.AcceptClosedPlaceCandidateUseCase +import club.staircrusher.place.application.port.`in`.GetClosedPlaceCandidateUseCase +import club.staircrusher.place.application.port.`in`.IgnoreClosedPlaceCandidateUseCase +import club.staircrusher.place.application.port.`in`.ListClosedPlaceCandidatesUseCase import club.staircrusher.place.application.port.`in`.StartPlaceCrawlingUseCase +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.PutMapping import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController @RestController class AdminPlaceController( private val startPlaceCrawlingUseCase: StartPlaceCrawlingUseCase, + private val listClosedPlaceCandidatesUseCase: ListClosedPlaceCandidatesUseCase, + private val getClosedPlaceCandidateUseCase: GetClosedPlaceCandidateUseCase, + private val acceptClosedPlaceCandidateUseCase: AcceptClosedPlaceCandidateUseCase, + private val ignoreClosedPlaceCandidateUseCase: IgnoreClosedPlaceCandidateUseCase, ) { @PostMapping("/admin/places/startCrawling") fun startPlaceCrawling(@RequestBody request: StartPlaceCrawlingRequestDTO) { startPlaceCrawlingUseCase.handle(request.boundaryVertices.map { it.toModel() }) } + + @GetMapping("/admin/closedPlaceCandidates") + fun listClosedPlaceCandidates( + @RequestParam(required = false) limit: Int?, + @RequestParam(required = false) cursor: String?, + ): AdminListClosedPlaceCandidatesResponseDTO { + return listClosedPlaceCandidatesUseCase.handle( + limit = limit, + cursorValue = cursor, + ).run { + AdminListClosedPlaceCandidatesResponseDTO( + list = candidates.map { it.toAdminDTO() }, + cursor = nextCursor, + ) + } + } + + @GetMapping("/admin/closedPlaceCandidates/{candidateId}") + fun getClosedPlaceCandidate(@PathVariable candidateId: String): AdminClosedPlaceCandidateDTO { + return getClosedPlaceCandidateUseCase.handle(candidateId)?.toAdminDTO() + ?: throw IllegalArgumentException("closed place candidate with id($candidateId) not found") + } + + @PutMapping("/admin/closedPlaceCandidates/{candidateId}/accept") + fun acceptClosedPlaceCandidate(@PathVariable candidateId: String): AdminClosedPlaceCandidateDTO { + return acceptClosedPlaceCandidateUseCase.handle(candidateId).toAdminDTO() + } + + @PutMapping("/admin/closedPlaceCandidates/{candidateId}/ignore") + fun ignoreClosedPlaceCandidate(@PathVariable candidateId: String): AdminClosedPlaceCandidateDTO { + return ignoreClosedPlaceCandidateUseCase.handle(candidateId).toAdminDTO() + } } diff --git a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/resources/db/migration/V33__closed_place_candidate_table.sql b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/resources/db/migration/V33__closed_place_candidate_table.sql index 2095dba2..036c8d37 100644 --- a/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/resources/db/migration/V33__closed_place_candidate_table.sql +++ b/app-server/subprojects/cross_cutting_concern/infra/persistence_model/src/main/resources/db/migration/V33__closed_place_candidate_table.sql @@ -10,4 +10,5 @@ CREATE TABLE IF NOT EXISTS closed_place_candidate ( ); CREATE INDEX idx_closed_place_candidate_1 ON closed_place_candidate(place_id); -CREATE INDEX idx_closed_place_candidate_2 ON closed_place_candidate(external_id); \ No newline at end of file +CREATE INDEX idx_closed_place_candidate_2 ON closed_place_candidate(external_id); +CREATE INDEX idx_closed_place_candidate_3 ON closed_place_candidate(created_at); \ No newline at end of file From d8f93574b3af56a171fb1d4eae8ee97787ae0ba7 Mon Sep 17 00:00:00 2001 From: Jason Yoo Date: Thu, 10 Oct 2024 22:20:21 +0900 Subject: [PATCH 2/4] chore: change path and param name --- api-admin/api-spec.yaml | 2 +- .../infra/adapter/in/controller/AdminPlaceController.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api-admin/api-spec.yaml b/api-admin/api-spec.yaml index 9beea869..abdbdb5c 100644 --- a/api-admin/api-spec.yaml +++ b/api-admin/api-spec.yaml @@ -985,7 +985,7 @@ components: AdminListClosedPlaceCandidatesResponseDTO: type: object properties: - list: + items: type: array items: $ref: '#/components/schemas/AdminClosedPlaceCandidateDTO' diff --git a/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt index 9e435dec..ed51ea01 100644 --- a/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt +++ b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt @@ -30,7 +30,7 @@ class AdminPlaceController( startPlaceCrawlingUseCase.handle(request.boundaryVertices.map { it.toModel() }) } - @GetMapping("/admin/closedPlaceCandidates") + @GetMapping("/admin/closed-place-candidates") fun listClosedPlaceCandidates( @RequestParam(required = false) limit: Int?, @RequestParam(required = false) cursor: String?, @@ -46,18 +46,18 @@ class AdminPlaceController( } } - @GetMapping("/admin/closedPlaceCandidates/{candidateId}") + @GetMapping("/admin/closed-place-candidates/{candidateId}") fun getClosedPlaceCandidate(@PathVariable candidateId: String): AdminClosedPlaceCandidateDTO { return getClosedPlaceCandidateUseCase.handle(candidateId)?.toAdminDTO() ?: throw IllegalArgumentException("closed place candidate with id($candidateId) not found") } - @PutMapping("/admin/closedPlaceCandidates/{candidateId}/accept") + @PutMapping("/admin/closed-place-candidates/{candidateId}/accept") fun acceptClosedPlaceCandidate(@PathVariable candidateId: String): AdminClosedPlaceCandidateDTO { return acceptClosedPlaceCandidateUseCase.handle(candidateId).toAdminDTO() } - @PutMapping("/admin/closedPlaceCandidates/{candidateId}/ignore") + @PutMapping("/admin/closed-place-candidates/{candidateId}/ignore") fun ignoreClosedPlaceCandidate(@PathVariable candidateId: String): AdminClosedPlaceCandidateDTO { return ignoreClosedPlaceCandidateUseCase.handle(candidateId).toAdminDTO() } From 99132c554c21712e4cf4bd2d97d861ffc5de9cf5 Mon Sep 17 00:00:00 2001 From: Jason Yoo Date: Thu, 10 Oct 2024 22:23:24 +0900 Subject: [PATCH 3/4] fix: build --- .../place/infra/adapter/in/controller/AdminPlaceController.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt index ed51ea01..2a0461ae 100644 --- a/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt +++ b/app-server/subprojects/bounded_context/place/infra/src/main/kotlin/club/staircrusher/place/infra/adapter/in/controller/AdminPlaceController.kt @@ -40,7 +40,7 @@ class AdminPlaceController( cursorValue = cursor, ).run { AdminListClosedPlaceCandidatesResponseDTO( - list = candidates.map { it.toAdminDTO() }, + items = candidates.map { it.toAdminDTO() }, cursor = nextCursor, ) } From 60e3bfeb591494e1d008b05b682a27f992dd1621 Mon Sep 17 00:00:00 2001 From: Jason Yoo Date: Thu, 10 Oct 2024 22:30:33 +0900 Subject: [PATCH 4/4] chore: accept place closed --- .../application/port/in/AcceptClosedPlaceCandidateUseCase.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt index 28a49dcd..dfd54285 100644 --- a/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt +++ b/app-server/subprojects/bounded_context/place/application/src/main/kotlin/club/staircrusher/place/application/port/in/AcceptClosedPlaceCandidateUseCase.kt @@ -16,11 +16,13 @@ class AcceptClosedPlaceCandidateUseCase( fun handle(candidateId: String) = transactionManager.doInTransaction { val candidate = closedPlaceCandidateRepository.findByIdOrNull(candidateId) ?: throw IllegalArgumentException("closed place candidate with id($candidateId) not found") + val place = placeRepository.findByIdOrNull(candidate.placeId)!! candidate.accept() closedPlaceCandidateRepository.save(candidate) + place.setIsClosed(true) + placeRepository.save(place) - val place = placeRepository.findByIdOrNull(candidate.placeId)!! return@doInTransaction NamedClosedPlaceCandidate( candidateId = candidate.id, placeId = place.id,