Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: SchedulePlace 중간 테이블 생성 #167

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ 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.place.SchedulePlace
import com.piikii.application.domain.schedule.Schedule
import com.piikii.application.port.input.CourseUseCase
import com.piikii.application.port.input.dto.response.CourseResponse
import com.piikii.application.port.output.persistence.CourseQueryPort
import com.piikii.application.port.output.persistence.PlaceCommandPort
import com.piikii.application.port.output.persistence.PlaceQueryPort
import com.piikii.application.port.output.persistence.RoomQueryPort
import com.piikii.application.port.output.persistence.SchedulePlaceCommandPort
import com.piikii.application.port.output.persistence.SchedulePlaceQueryPort
import com.piikii.application.port.output.persistence.ScheduleQueryPort
import com.piikii.application.port.output.persistence.VoteQueryPort
import com.piikii.application.port.output.web.NavigationPort
Expand All @@ -21,16 +22,18 @@ import org.springframework.transaction.annotation.Transactional
@Service
@Transactional(readOnly = true)
class CourseService(
private val courseQueryPort: CourseQueryPort,
private val roomQueryPort: RoomQueryPort,
private val scheduleQueryPort: ScheduleQueryPort,
private val placeQueryPort: PlaceQueryPort,
private val placeCommandPort: PlaceCommandPort,
private val schedulePlaceQueryPort: SchedulePlaceQueryPort,
private val schedulePlaceCommandPort: SchedulePlaceCommandPort,
private val voteQueryPort: VoteQueryPort,
private val navigationPort: NavigationPort,
) : CourseUseCase {
override fun isCourseExist(roomUid: UuidTypeId): Boolean {
return courseQueryPort.isCourseExist(roomUid)
val scheduleIds = scheduleQueryPort.findAllByRoomUid(roomUid)
val confirmedSchedulePlaces = schedulePlaceQueryPort.findAllConfirmedByRoomId(roomUid)
return scheduleIds.size == confirmedSchedulePlaces.size
}

@Transactional
Expand All @@ -45,15 +48,16 @@ class CourseService(
}

val schedules = scheduleQueryPort.findAllByRoomUid(roomUid)
val places = placeQueryPort.findAllByRoomUid(roomUid)
val placeById = placeQueryPort.findAllByRoomUid(roomUid).associateBy { it.id }
val schedulePlaces = schedulePlaceQueryPort.findAllByRoomUid(roomUid)

val placeIds = places.map { it.id }
val votes = voteQueryPort.findAllByPlaceIds(placeIds)
val agreeCountByPlaceId = voteQueryPort.findAgreeCountByPlaceId(votes)
val schedulePlaceIds = schedulePlaces.map { it.id }
val votes = voteQueryPort.findAllBySchedulePlaceIds(schedulePlaceIds)
val agreeCountBySchedulePlaceId = voteQueryPort.findAgreeCountBySchedulePlaceId(votes)

return CourseResponse.from(
room = room,
placeBySchedule = getPlaceBySchedule(schedules, places, agreeCountByPlaceId),
placeBySchedule = getPlaceBySchedule(schedules, placeById, schedulePlaces, agreeCountBySchedulePlaceId),
)
}

Expand All @@ -62,33 +66,36 @@ class CourseService(
roomUid: UuidTypeId,
placeId: LongTypeId,
) {
val place = placeQueryPort.findByPlaceId(placeId)
val schedulePlace = schedulePlaceQueryPort.findById(placeId)

if (place.isInvalidRoomUid(roomUid)) {
if (schedulePlace.isInvalidRoomUid(roomUid)) {
throw PiikiiException(
exceptionCode = ExceptionCode.ILLEGAL_ARGUMENT_EXCEPTION,
detailMessage = "Room UUID: $roomUid, Room UUID in Place: ${place.roomUid}",
detailMessage = "Room UUID: $roomUid, Room UUID in Place: ${schedulePlace.roomUid}",
)
}

placeQueryPort.findConfirmedByScheduleId(place.scheduleId)?.let { confirmedPlace ->
placeCommandPort.update(confirmedPlace.id, confirmedPlace.copy(confirmed = false))
schedulePlaceQueryPort.findConfirmedByScheduleId(schedulePlace.scheduleId)?.let { confirmedPlace ->
schedulePlaceCommandPort.update(confirmedPlace.id, confirmedPlace.copy(confirmed = false))
}
placeCommandPort.update(place.id, place.copy(confirmed = true))

schedulePlaceCommandPort.update(placeId, schedulePlace.copy(confirmed = true))
}

private fun getPlaceBySchedule(
schedules: List<Schedule>,
places: List<Place>,
agreeCountByPlaceId: Map<Long, Int>,
placeById: Map<LongTypeId, Place>,
schedulePlaces: List<SchedulePlace>,
agreeCountBySchedulePlaceId: Map<Long, Int>,
): Map<Schedule, CoursePlace> {
// initial 값 설정: null과 빈 Map의 쌍으로 초기화
val initial: Map<Schedule, CoursePlace> = emptyMap()

return mapPlacesBySchedule(schedules, places)
.entries.fold(initial) { prePlaceBySchedule, (schedule, places) ->
return mapPlacesBySchedule(schedules, schedulePlaces)
.entries.fold(initial) { prePlaceBySchedule, (schedule, schedulePlacesIn) ->
// 현재 CoursePlace 생성
val confirmedPlace = getConfirmedPlace(schedule, places, agreeCountByPlaceId)
val confirmedPlace =
getConfirmedPlace(schedule, placeById, schedulePlacesIn, agreeCountBySchedulePlaceId)

if (confirmedPlace != null) {
val preCoursePlace = prePlaceBySchedule.values.lastOrNull()
Expand All @@ -105,11 +112,12 @@ class CourseService(

private fun mapPlacesBySchedule(
schedules: List<Schedule>,
places: List<Place>,
): Map<Schedule, List<Place>> {
val placesByScheduleId = places.groupBy { it.scheduleId }
schedulePlaces: List<SchedulePlace>,
): Map<Schedule, List<SchedulePlace>> {
val schedulePlaceByScheduleId = schedulePlaces.groupBy { it.scheduleId }

return schedules.associateWith { schedule ->
placesByScheduleId[schedule.id]
schedulePlaceByScheduleId[schedule.id]
?: throw PiikiiException(
exceptionCode = ExceptionCode.ILLEGAL_ARGUMENT_EXCEPTION,
detailMessage = "$EMPTY_CONFIRMED_PLACE Schedule ID: ${schedule.id}",
Comment on lines 113 to 123
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 메서드는 schedule 당 place를 가지는거니깐
SchedulePlace가 아닌 List를 가지는게 더 말이 되지 않나요?

Expand Down Expand Up @@ -139,10 +147,11 @@ class CourseService(

private fun getConfirmedPlace(
schedule: Schedule,
places: List<Place>,
agreeCountByPlaceId: Map<Long, Int>,
placeById: Map<LongTypeId, Place>,
schedulePlaces: List<SchedulePlace>,
agreeCountBySchedulePlaceId: Map<Long, Int>,
): Place? {
val confirmedPlaces = places.filter { it.confirmed }
val confirmedPlaces = schedulePlaces.filter { it.confirmed }

if (confirmedPlaces.size > 1) {
throw PiikiiException(
Expand All @@ -151,27 +160,41 @@ class CourseService(
)
}

return confirmedPlaces.firstOrNull() ?: confirmPlace(places, agreeCountByPlaceId)
return confirmedPlaces.firstOrNull()
?.let { findPlaceInPlaceById(it.placeId, placeById) }
?: confirmSchedulePlace(schedulePlaces, agreeCountBySchedulePlaceId)
?.let { findPlaceInPlaceById(it.placeId, placeById) }
}

private fun confirmPlace(
places: List<Place>,
agreeCountByPlaceId: Map<Long, Int>,
): Place? {
return places
.mapNotNull { place ->
// place.id에 해당하는 agree count가 존재하면 place와 count를 페어로 매핑
agreeCountByPlaceId[place.id.getValue()]?.let { count -> place to count }
private fun findPlaceInPlaceById(
targetPlaceId: LongTypeId,
placeById: Map<LongTypeId, Place>,
): Place {
return placeById[targetPlaceId]
?: throw PiikiiException(
exceptionCode = ExceptionCode.NOT_FOUNDED,
detailMessage = "Place not found for Place ID: $targetPlaceId",
)
}

private fun confirmSchedulePlace(
schedulePlaces: List<SchedulePlace>,
agreeCountBySchedulePlaceId: Map<Long, Int>,
): SchedulePlace? {
return schedulePlaces
.mapNotNull { schedulePlace ->
// place.id에 해당하는 agree count가 존재하면 schedulePlace와 count를 페어로 매핑
agreeCountBySchedulePlaceId[schedulePlace.id.getValue()]?.let { count -> schedulePlace to count }
}
// agree count 내림차순 정렬, (count 동일)place.id 오름차순 정렬
// agree count 내림차순 정렬, (count 동일)schedulePlace.id 오름차순 정렬
.maxWithOrNull(compareBy({ it.second }, { -it.first.id.getValue() }))
?.let { (selectedPlace, _) ->
// 최다 찬성 득표 수 place: confirmed 상태로 변경
placeCommandPort.update(
targetPlaceId = selectedPlace.id,
place = selectedPlace.copy(confirmed = true),
?.let { (selectedSchedulePlace, _) ->
// 최다 찬성 득표 수 schedulePlace: confirmed 상태로 변경
schedulePlaceCommandPort.update(
targetId = selectedSchedulePlace.id,
schedulePlace = selectedSchedulePlace.copy(confirmed = true),
)
selectedPlace
selectedSchedulePlace
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import com.piikii.application.domain.generic.UuidTypeId
data class Place(
val id: LongTypeId,
val roomUid: UuidTypeId,
val scheduleId: LongTypeId,
val name: String,
val url: String?,
val thumbnailLinks: ThumbnailLinks,
Expand All @@ -17,7 +16,6 @@ data class Place(
val starGrade: Float?,
val origin: Origin,
val memo: String?,
val confirmed: Boolean,
val reviewCount: Int?,
val longitude: Double?,
val latitude: Double?,
Expand All @@ -26,8 +24,4 @@ data class Place(
fun getCoordinate(): Coordinate {
return Coordinate(this.longitude, this.latitude)
}

fun isInvalidRoomUid(roomUid: UuidTypeId): Boolean {
return this.roomUid != roomUid
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.piikii.application.port.output.objectstorage.ObjectStoragePort
import com.piikii.application.port.output.persistence.PlaceCommandPort
import com.piikii.application.port.output.persistence.PlaceQueryPort
import com.piikii.application.port.output.persistence.RoomQueryPort
import com.piikii.application.port.output.persistence.SchedulePlaceCommandPort
import com.piikii.application.port.output.persistence.SchedulePlaceQueryPort
import com.piikii.application.port.output.persistence.ScheduleQueryPort
import com.piikii.common.exception.ExceptionCode
import com.piikii.common.exception.PiikiiException
Expand All @@ -26,6 +28,8 @@ class PlaceService(
private val scheduleQueryPort: ScheduleQueryPort,
private val placeQueryPort: PlaceQueryPort,
private val placeCommandPort: PlaceCommandPort,
private val schedulePlaceQueryPort: SchedulePlaceQueryPort,
private val schedulePlaceCommandPort: SchedulePlaceCommandPort,
private val objectStoragePort: ObjectStoragePort,
) : PlaceUseCase {
@Transactional
Expand All @@ -43,33 +47,30 @@ class PlaceService(
} ?: listOf()

val room = roomQueryPort.findById(targetRoomUid)
val schedules = scheduleQueryPort.findAllByIds(addPlaceRequest.scheduleIds.map(::LongTypeId))
val place = addPlaceRequest.toDomain(room.roomUid, imageUrls)
val scheduleIds = scheduleQueryPort.findAllByIds(addPlaceRequest.scheduleIds.map(::LongTypeId)).map { it.id }

val places =
schedules.map { schedule ->
addPlaceRequest.toDomain(
roomUid = room.roomUid,
scheduleId = schedule.id,
imageUrls = imageUrls,
)
}

return placeCommandPort.saveAll(
roomUid = room.roomUid,
scheduleIds = schedules.map { it.id },
places = places,
).map(::PlaceResponse)
return placeCommandPort.save(room.roomUid, scheduleIds, place).map { PlaceResponse(it, place) }
}

override fun findAllByRoomUidGroupByPlaceType(roomUid: UuidTypeId): List<ScheduleTypeGroupResponse> {
val schedulePlaces = schedulePlaceQueryPort.findAllByRoomUid(roomUid)
val scheduleById = scheduleQueryPort.findAllByRoomUid(roomUid).associateBy { it.id }
return placeQueryPort.findAllByRoomUid(roomUid).groupBy { it.scheduleId }
.map { (scheduleId, places) ->
val placeById = placeQueryPort.findAllByRoomUid(roomUid).associateBy { it.id }

return schedulePlaces.groupBy { it.scheduleId }
.map { (scheduleId, schedulePlaces) ->
val schedule = scheduleById[scheduleId] ?: throw PiikiiException(ExceptionCode.NOT_FOUNDED)
ScheduleTypeGroupResponse(
scheduleId = scheduleId.getValue(),
scheduleName = schedule.name,
places = places.map { place -> PlaceResponse(place = place) },
places =
schedulePlaces.map { schedulePlace ->
val place =
placeById[schedulePlace.placeId]
?: throw PiikiiException(ExceptionCode.NOT_FOUNDED)
PlaceResponse(schedulePlace, place)
},
)
}
}
Expand All @@ -90,28 +91,34 @@ class PlaceService(
).get()
} ?: listOf()

val place = isPlaceNullOrGet(targetPlaceId)
return PlaceResponse(
val schedulePlace = schedulePlaceQueryPort.findById(targetPlaceId)
val place = isPlaceNullOrGet(schedulePlace.placeId)

val updatedPlace =
placeCommandPort.update(
targetPlaceId = targetPlaceId,
targetPlaceId = schedulePlace.placeId,
place =
modifyPlaceRequest.toDomain(
targetPlaceId,
targetRoomUid,
LongTypeId(modifyPlaceRequest.scheduleId),
filterDuplicateUrls(updatedUrls, place),
targetPlaceId = schedulePlace.placeId,
roomUid = targetRoomUid,
updatedUrls = filterDuplicateUrls(updatedUrls, place),
),
),
)
)

return PlaceResponse(schedulePlace, updatedPlace)
}

@Transactional
override fun delete(targetPlaceId: LongTypeId) {
val schedulePlace = schedulePlaceQueryPort.findById(targetPlaceId)
val place = isPlaceNullOrGet(schedulePlace.placeId)

objectStoragePort.deleteAllByUrls(
bucketFolderType = BUCKET_TYPE,
deleteTargetUrls = isPlaceNullOrGet(targetPlaceId).thumbnailLinks.convertToList,
deleteTargetUrls = place.thumbnailLinks.convertToList,
)
placeCommandPort.delete(targetPlaceId)
schedulePlaceCommandPort.deleteAllByPlaceId(place.id)
placeCommandPort.delete(place.id)
}

private fun isPlaceNullOrGet(targetPlaceId: LongTypeId): Place {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.piikii.application.domain.place

import com.piikii.application.domain.generic.LongTypeId
import com.piikii.application.domain.generic.UuidTypeId

data class SchedulePlace(
val id: LongTypeId,
val roomUid: UuidTypeId,
val scheduleId: LongTypeId,
val placeId: LongTypeId,
var confirmed: Boolean,
) {
fun isInvalidRoomUid(roomUid: UuidTypeId): Boolean {
return this.roomUid != roomUid
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.piikii.application.domain.generic.UuidTypeId
data class Vote(
val id: LongTypeId,
val userUid: UuidTypeId,
val placeId: LongTypeId,
val schedulePlaceId: LongTypeId,
val result: VoteResult,
)

Expand Down
Loading
Loading