diff --git a/src/main/kotlin/kr/wooco/woocobe/place/domain/gateway/PlaceOneLineReviewStorageGateway.kt b/src/main/kotlin/kr/wooco/woocobe/place/domain/gateway/PlaceOneLineReviewStorageGateway.kt new file mode 100644 index 0000000..d31947f --- /dev/null +++ b/src/main/kotlin/kr/wooco/woocobe/place/domain/gateway/PlaceOneLineReviewStorageGateway.kt @@ -0,0 +1,7 @@ +package kr.wooco.woocobe.place.domain.gateway + +import kr.wooco.woocobe.place.domain.model.PlaceOneLineReviewStat + +interface PlaceOneLineReviewStorageGateway { + fun getOneLineReviewStats(placeId: Long): List +} diff --git a/src/main/kotlin/kr/wooco/woocobe/place/domain/model/Place.kt b/src/main/kotlin/kr/wooco/woocobe/place/domain/model/Place.kt index 1c0cb9f..8e25823 100644 --- a/src/main/kotlin/kr/wooco/woocobe/place/domain/model/Place.kt +++ b/src/main/kotlin/kr/wooco/woocobe/place/domain/model/Place.kt @@ -11,20 +11,29 @@ class Place( var reviewCount: Long, // 한줄평 통계 로직 고려 중 ) { - fun increaseReviewCount() = - apply { - reviewCount++ - } + fun addReview(newRating: Double) { + averageRating = ((averageRating * reviewCount) + newRating) / (reviewCount + 1) + reviewCount++ + } - fun decreaseReviewCount() = - apply { - reviewCount-- + fun updateReview( + oldRating: Double, + newRating: Double, + ) { + if (reviewCount > 0) { + averageRating += (newRating - oldRating) / reviewCount } + } - fun updateAverageRating(rating: Double) = - apply { - averageRating = (averageRating * reviewCount + rating) / (reviewCount) + fun deleteReview(oldRating: Double) { + if (reviewCount > 1) { + averageRating = ((averageRating * reviewCount) - oldRating) / (reviewCount - 1) + reviewCount-- + } else { + averageRating = 0.0 + reviewCount = 0 } + } companion object { fun register( diff --git a/src/main/kotlin/kr/wooco/woocobe/place/domain/model/PlaceOneLineReviewStat.kt b/src/main/kotlin/kr/wooco/woocobe/place/domain/model/PlaceOneLineReviewStat.kt new file mode 100644 index 0000000..58da45b --- /dev/null +++ b/src/main/kotlin/kr/wooco/woocobe/place/domain/model/PlaceOneLineReviewStat.kt @@ -0,0 +1,6 @@ +package kr.wooco.woocobe.place.domain.model + +class PlaceOneLineReviewStat( + val content: String, + val count: Long, +) diff --git a/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/AddPlaceReviewUseCase.kt b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/AddPlaceReviewUseCase.kt index fefd200..ba391af 100644 --- a/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/AddPlaceReviewUseCase.kt +++ b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/AddPlaceReviewUseCase.kt @@ -37,8 +37,8 @@ class AddPlaceReviewUseCase( oneLineReview = input.oneLineReviews, imageUrls = input.imageUrls, ).also(placeReviewStorageGateway::save) - place.increaseReviewCount() + place.addReview(input.rating) placeStorageGateway.save(place) } } diff --git a/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/DeletePlaceReviewUseCase.kt b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/DeletePlaceReviewUseCase.kt index da091ee..100051a 100644 --- a/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/DeletePlaceReviewUseCase.kt +++ b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/DeletePlaceReviewUseCase.kt @@ -27,8 +27,10 @@ class DeletePlaceReviewUseCase( placeReviewStorageGateway.deleteByPlaceReviewId(placeReviewId = placeReview.id) - placeReview.place.decreaseReviewCount() + val place = placeReview.place - placeStorageGateway.save(placeReview.place) + place.deleteReview(oldRating = placeReview.rating) + + placeStorageGateway.save(place) } } diff --git a/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/GetOneLineReviewStatsUseCase.kt b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/GetOneLineReviewStatsUseCase.kt new file mode 100644 index 0000000..4490b32 --- /dev/null +++ b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/GetOneLineReviewStatsUseCase.kt @@ -0,0 +1,26 @@ +package kr.wooco.woocobe.place.domain.usecase + +import kr.wooco.woocobe.common.domain.usecase.UseCase +import kr.wooco.woocobe.place.domain.gateway.PlaceOneLineReviewStorageGateway +import kr.wooco.woocobe.place.domain.model.PlaceOneLineReviewStat + +data class GetOneLineReviewStatsInput( + val placeId: Long, +) + +data class GetOneLineReviewStatsOutput( + val placeOneLineReviewRank: List, +) + +class GetOneLineReviewStatsUseCase( + private val placeOneLineReviewStorageGateway: PlaceOneLineReviewStorageGateway, +) : UseCase { + override fun execute(input: GetOneLineReviewStatsInput): GetOneLineReviewStatsOutput { + val placeOneLineReviewRank = + placeOneLineReviewStorageGateway.getOneLineReviewStats(input.placeId) + + return GetOneLineReviewStatsOutput( + placeOneLineReviewRank = placeOneLineReviewRank, + ) + } +} diff --git a/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/UpdatePlaceReviewUseCase.kt b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/UpdatePlaceReviewUseCase.kt index d41165f..029969e 100644 --- a/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/UpdatePlaceReviewUseCase.kt +++ b/src/main/kotlin/kr/wooco/woocobe/place/domain/usecase/UpdatePlaceReviewUseCase.kt @@ -2,6 +2,7 @@ package kr.wooco.woocobe.place.domain.usecase import kr.wooco.woocobe.common.domain.usecase.UseCase import kr.wooco.woocobe.place.domain.gateway.PlaceReviewStorageGateway +import kr.wooco.woocobe.place.domain.gateway.PlaceStorageGateway import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -17,6 +18,7 @@ data class UpdatePlaceReviewInput( @Service class UpdatePlaceReviewUseCase( private val placeReviewStorageGateway: PlaceReviewStorageGateway, + private val placeStorageGateway: PlaceStorageGateway, ) : UseCase { @Transactional override fun execute(input: UpdatePlaceReviewInput) { @@ -34,5 +36,9 @@ class UpdatePlaceReviewUseCase( oneLineReviews = input.oneLineReviews, imageUrls = input.imageUrls, ).also(placeReviewStorageGateway::save) + + val place = placeReview.place + place.updateReview(oldRating = placeReview.rating, newRating = input.rating) + placeStorageGateway.save(place) } } diff --git a/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/gateway/JpaPlaceOneLineReviewStorageGateway.kt b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/gateway/JpaPlaceOneLineReviewStorageGateway.kt new file mode 100644 index 0000000..52477cd --- /dev/null +++ b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/gateway/JpaPlaceOneLineReviewStorageGateway.kt @@ -0,0 +1,24 @@ +package kr.wooco.woocobe.place.infrastructure.gateway + +import kr.wooco.woocobe.place.domain.gateway.PlaceOneLineReviewStorageGateway +import kr.wooco.woocobe.place.domain.model.PlaceOneLineReviewStat +import kr.wooco.woocobe.place.infrastructure.storage.PlaceOneLineReviewJpaRepository +import org.springframework.stereotype.Component + +@Component +class JpaPlaceOneLineReviewStorageGateway( + private val placeOneLineReviewRepository: PlaceOneLineReviewJpaRepository, +) : PlaceOneLineReviewStorageGateway { + override fun getOneLineReviewStats(placeId: Long): List { + val stats = placeOneLineReviewRepository.findPlaceOneLineReviewStatsByPlaceId(placeId) + return stats.map { row -> + val content = row["content"] ?: throw RuntimeException() + val count = row["count"] ?: throw RuntimeException() + + PlaceOneLineReviewStat( + content = content.toString(), + count = count, + ) + } + } +} diff --git a/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/gateway/JpaPlaceReviewStorageGateway.kt b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/gateway/JpaPlaceReviewStorageGateway.kt index 91a8ea2..87a2c92 100644 --- a/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/gateway/JpaPlaceReviewStorageGateway.kt +++ b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/gateway/JpaPlaceReviewStorageGateway.kt @@ -24,6 +24,7 @@ class JpaPlaceReviewStorageGateway( placeReview.oneLineReviews .map { PlaceOneLineReviewEntity.of( + placeId = placeReviewEntity.placeId, placeReviewId = placeReviewEntity.id!!, content = it.content, ) @@ -55,7 +56,9 @@ class JpaPlaceReviewStorageGateway( map { placeReviewEntity -> placeReviewEntity.toDomain( user = userEntities.find { placeReviewEntity.userId == it.id }!!.toDomain(), - place = placeEntities.find { placeReviewEntity.placeId == it.id }!!.toDomain(), + place = placeEntities + .find { placeReviewEntity.placeId == it.id }!! + .toDomain(), placeOneLineReview = placeOneLineReviewEntities .filter { placeReviewEntity.id == it.placeReviewId } .map { it.toDomain() }, @@ -74,7 +77,9 @@ class JpaPlaceReviewStorageGateway( map { placeReviewEntity -> placeReviewEntity.toDomain( user = userEntity.find { placeReviewEntity.userId == it.id }!!.toDomain(), - place = placeJpaRepository.findByIdOrNull(placeReviewEntity.placeId)!!.toDomain(), + place = placeJpaRepository + .findByIdOrNull(placeReviewEntity.placeId)!! + .toDomain(), placeOneLineReview = placeOneLineReviewEntity .filter { placeReviewEntity.id == it.placeReviewId } .map { it.toDomain() }, @@ -93,7 +98,9 @@ class JpaPlaceReviewStorageGateway( map { placeReviewEntity -> placeReviewEntity.toDomain( user = userEntity.toDomain(), - place = placeJpaRepository.findByIdOrNull(placeReviewEntity.placeId)!!.toDomain(), + place = placeJpaRepository + .findByIdOrNull(placeReviewEntity.placeId)!! + .toDomain(), placeOneLineReview = placeOneLineReviewEntity .filter { placeReviewEntity.id == it.placeReviewId } .map { it.toDomain() }, diff --git a/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewEntity.kt b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewEntity.kt index 88284d5..8e15bbb 100644 --- a/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewEntity.kt +++ b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewEntity.kt @@ -13,8 +13,10 @@ import kr.wooco.woocobe.place.domain.model.PlaceOneLineReview class PlaceOneLineReviewEntity( @Column(name = "content") val content: String, - @Column(name = "place_id") + @Column(name = "place_Review_id") val placeReviewId: Long, + @Column(name = "place_id") + val placeId: Long, @Id @Tsid @Column(name = "place_one_line_review_id") val id: Long? = 0L, @@ -23,10 +25,12 @@ class PlaceOneLineReviewEntity( companion object { fun of( + placeId: Long, placeReviewId: Long, content: String, ): PlaceOneLineReviewEntity = PlaceOneLineReviewEntity( + placeId = placeId, placeReviewId = placeReviewId, content = content, ) diff --git a/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewJpaRepository.kt b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewJpaRepository.kt index 7bec58c..0af639a 100644 --- a/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewJpaRepository.kt +++ b/src/main/kotlin/kr/wooco/woocobe/place/infrastructure/storage/PlaceOneLineReviewJpaRepository.kt @@ -1,9 +1,21 @@ package kr.wooco.woocobe.place.infrastructure.storage import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query interface PlaceOneLineReviewJpaRepository : JpaRepository { fun findAllByPlaceReviewIdOrderByCreatedAt(placeReviewId: Long): List fun findAllByPlaceReviewIdInOrderByCreatedAt(placeReviewId: List): List + + @Query( + """ + select r.content as content, count(r.content) as count + from PlaceOneLineReviewEntity r + where r.placeId = :placeId + group by r.content + order by count desc + """, + ) + fun findPlaceOneLineReviewStatsByPlaceId(placeId: Long): List> }