diff --git a/piikii-application/src/main/kotlin/com/piikii/application/domain/course/CourseService.kt b/piikii-application/src/main/kotlin/com/piikii/application/domain/course/CourseService.kt index 091b6106..82acb622 100644 --- a/piikii-application/src/main/kotlin/com/piikii/application/domain/course/CourseService.kt +++ b/piikii-application/src/main/kotlin/com/piikii/application/domain/course/CourseService.kt @@ -1,5 +1,6 @@ package com.piikii.application.domain.course +import com.piikii.application.domain.generic.LongTypeId import com.piikii.application.domain.generic.UuidTypeId import com.piikii.application.domain.place.Place import com.piikii.application.domain.schedule.Schedule @@ -56,6 +57,26 @@ class CourseService( ) } + @Transactional + override fun updateCoursePlace( + roomUid: UuidTypeId, + placeId: LongTypeId, + ) { + val place = placeQueryPort.findByPlaceId(placeId) + + if (place.isInvalidRoomUid(roomUid)) { + throw PiikiiException( + exceptionCode = ExceptionCode.ILLEGAL_ARGUMENT_EXCEPTION, + detailMessage = "Room UUID: $roomUid, Room UUID in Place: ${place.roomUid}", + ) + } + + placeQueryPort.findConfirmedByScheduleId(place.scheduleId)?.let { confirmedPlace -> + placeCommandPort.update(confirmedPlace.id, confirmedPlace.copy(confirmed = false)) + } + placeCommandPort.update(place.id, place.copy(confirmed = true)) + } + private fun getPlaceBySchedule( schedules: List, places: List, diff --git a/piikii-application/src/main/kotlin/com/piikii/application/domain/place/Place.kt b/piikii-application/src/main/kotlin/com/piikii/application/domain/place/Place.kt index 9f4e75b7..a63d7da3 100644 --- a/piikii-application/src/main/kotlin/com/piikii/application/domain/place/Place.kt +++ b/piikii-application/src/main/kotlin/com/piikii/application/domain/place/Place.kt @@ -21,7 +21,11 @@ data class Place( val longitude: Double?, val latitude: Double?, ) { - fun getCoordinate(): Coordinate? { + fun getCoordinate(): Coordinate { return Coordinate(this.longitude, this.latitude) } + + fun isInvalidRoomUid(roomUid: UuidTypeId): Boolean { + return this.roomUid != roomUid + } } diff --git a/piikii-application/src/main/kotlin/com/piikii/application/port/input/CourseUseCase.kt b/piikii-application/src/main/kotlin/com/piikii/application/port/input/CourseUseCase.kt index e5b269e5..896af2a8 100644 --- a/piikii-application/src/main/kotlin/com/piikii/application/port/input/CourseUseCase.kt +++ b/piikii-application/src/main/kotlin/com/piikii/application/port/input/CourseUseCase.kt @@ -1,5 +1,6 @@ package com.piikii.application.port.input +import com.piikii.application.domain.generic.LongTypeId import com.piikii.application.domain.generic.UuidTypeId import com.piikii.application.port.input.dto.response.CourseResponse @@ -7,4 +8,9 @@ interface CourseUseCase { fun isCourseExist(roomUid: UuidTypeId): Boolean fun retrieveCourse(roomUid: UuidTypeId): CourseResponse + + fun updateCoursePlace( + roomUid: UuidTypeId, + placeId: LongTypeId, + ) } diff --git a/piikii-application/src/main/kotlin/com/piikii/application/port/output/persistence/PlacePort.kt b/piikii-application/src/main/kotlin/com/piikii/application/port/output/persistence/PlacePort.kt index 1a5f4805..1abf6522 100644 --- a/piikii-application/src/main/kotlin/com/piikii/application/port/output/persistence/PlacePort.kt +++ b/piikii-application/src/main/kotlin/com/piikii/application/port/output/persistence/PlacePort.kt @@ -5,11 +5,13 @@ import com.piikii.application.domain.generic.UuidTypeId import com.piikii.application.domain.place.Place interface PlaceQueryPort { - fun findByPlaceId(placeId: LongTypeId): Place? + fun findByPlaceId(placeId: LongTypeId): Place fun findAllByPlaceIds(placeIds: List): List fun findAllByRoomUid(roomUid: UuidTypeId): List + + fun findConfirmedByScheduleId(scheduleId: LongTypeId): Place? } interface PlaceCommandPort { diff --git a/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/CourseApi.kt b/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/CourseApi.kt index 9e534ca3..85b6e18b 100644 --- a/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/CourseApi.kt +++ b/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/CourseApi.kt @@ -1,5 +1,6 @@ package com.piikii.input.http.controller +import com.piikii.application.domain.generic.LongTypeId import com.piikii.application.domain.generic.UuidTypeId import com.piikii.application.port.input.CourseUseCase import com.piikii.application.port.input.dto.response.CourseResponse @@ -10,6 +11,7 @@ import jakarta.validation.constraints.NotNull import org.springframework.http.HttpStatus import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.ResponseStatus @@ -41,4 +43,14 @@ class CourseApi( data = courseUseCase.retrieveCourse(UuidTypeId(roomUid)), ) } + + @ResponseStatus(HttpStatus.OK) + @PatchMapping("/place/{placeId}") + override fun updateCoursePlace( + @NotNull @PathVariable roomUid: UUID, + @NotNull @PathVariable placeId: Long, + ): ResponseForm { + courseUseCase.updateCoursePlace(UuidTypeId(roomUid), LongTypeId(placeId)) + return ResponseForm.EMPTY_RESPONSE + } } diff --git a/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/docs/CourseApiDocs.kt b/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/docs/CourseApiDocs.kt index 6bb53e63..f5570db3 100644 --- a/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/docs/CourseApiDocs.kt +++ b/piikii-input-http/src/main/kotlin/com/piikii/input/http/controller/docs/CourseApiDocs.kt @@ -53,10 +53,37 @@ interface CourseApiDocs { fun retrieveCourse( @Parameter( name = "roomUid", - description = "코스 생성 여부를 조회할 방 uuid", + description = "코스를 조회할 방 uuid", required = true, `in` = ParameterIn.PATH, ) @NotNull roomUid: UUID, ): ResponseForm + + @Operation(summary = "코스 장소 수정 API", description = "코스 대상 장소를 수정합니다.") + @ApiResponses( + value = [ + ApiResponse( + responseCode = "200", + description = "OK success", + content = [Content(schema = Schema(implementation = ResponseForm::class))], + ), + ], + ) + fun updateCoursePlace( + @Parameter( + name = "roomUid", + description = "코스를 수정할 방 uuid", + required = true, + `in` = ParameterIn.PATH, + ) + @NotNull roomUid: UUID, + @Parameter( + name = "placeId", + description = "코스 장소로 선정할 장소 id", + required = true, + `in` = ParameterIn.PATH, + ) + @NotNull placeId: Long, + ): ResponseForm } diff --git a/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/adapter/PlaceAdapter.kt b/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/adapter/PlaceAdapter.kt index 9b3a772e..ae2441af 100644 --- a/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/adapter/PlaceAdapter.kt +++ b/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/adapter/PlaceAdapter.kt @@ -70,4 +70,13 @@ class PlaceAdapter( override fun findAllByRoomUid(roomUid: UuidTypeId): List { return placeRepository.findAllByRoomUid(roomUid).map { it.toDomain() } } + + override fun findConfirmedByScheduleId(scheduleId: LongTypeId): Place? { + val places = placeRepository.findByScheduleIdAndConfirmed(scheduleId, true) + return places.singleOrNull()?.toDomain() + ?: throw PiikiiException( + exceptionCode = ExceptionCode.ACCESS_DENIED, + detailMessage = "중복된 장소 코스가 있습니다. 데이터를 확인하세요. Schedule ID : $scheduleId", + ) + } } diff --git a/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/persistence/repository/PlaceRepository.kt b/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/persistence/repository/PlaceRepository.kt index a4132e35..3e8d0227 100644 --- a/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/persistence/repository/PlaceRepository.kt +++ b/piikii-output-persistence/postgresql/src/main/kotlin/com/piikii/output/persistence/postgresql/persistence/repository/PlaceRepository.kt @@ -7,4 +7,9 @@ import org.springframework.data.jpa.repository.JpaRepository interface PlaceRepository : JpaRepository { fun findAllByRoomUid(roomUid: UuidTypeId): List + + fun findByScheduleIdAndConfirmed( + scheduleId: LongTypeId, + confirmed: Boolean, + ): List }