Skip to content

Commit

Permalink
[KAN-61-KAN-66-KAN-68-KAN-69-KAN-70-KAN-71] Main Merge PR (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
goathoon authored May 20, 2024
1 parent 06f77e4 commit 17dabfa
Show file tree
Hide file tree
Showing 21 changed files with 881 additions and 75 deletions.
14 changes: 14 additions & 0 deletions src/main/kotlin/com/restaurant/be/common/config/SwaggerConfig.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.restaurant.be.common.config

import io.swagger.annotations.ApiModel
import io.swagger.annotations.ApiModelProperty
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
Expand Down Expand Up @@ -76,4 +78,16 @@ class SwaggerConfig {
return ApiInfoBuilder().title("REST API Document.")
.description("work in progress").termsOfServiceUrl("localhost").version("1.0").build()
}

@ApiModel
class PageModel {
@ApiModelProperty(value = "페이지 번호(0..N)", example = "0")
val page: Int = 0

@ApiModelProperty(value = "페이지 크기", allowableValues = "range[0, 100]", example = "0")
val size: Int = 0

@ApiModelProperty(value = "정렬(사용법: 컬럼명,ASC|DESC)")
val sort: List<String> = listOf()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ data class NotFoundUserEmailException(
override val message: String = "존재 하지 않는 유저 이메일 입니다."
) : ServerException(400, message)

data class NotFoundUserIdException(
override val message: String = "유저 고유 ID가 존재하지 않습니다"
) : ServerException(500, message)

data class WithdrawalUserException(
override val message: String = "탈퇴한 유저 입니다."
) : ServerException(400, message)
Expand Down Expand Up @@ -56,3 +60,23 @@ data class NotFoundUserException(
data class NotFoundReviewException(
override val message: String = "존재하지 않은 리뷰 입니다."
) : ServerException(400, message)

data class UnAuthorizedUpdateException(
override val message: String = "해당 게시글을 수정할 권한이 없습니다."
) : ServerException(401, message)

data class UnAuthorizedDeleteException(
override val message: String = "해당 게시글을 삭제할 권한이 없습니다."
) : ServerException(401, message)

data class DuplicateLikeException(
override val message: String = "같은 게시글을 두번 좋아요할 수 없습니다."
) : ServerException(400, message)

data class NotFoundLikeException(
override val message: String = "해당 게시글은 이미 좋아하지 않습니다."
) : ServerException(400, message)

data class InvalidLikeCountException(
override val message: String = "좋아요가 0보다 작아질 순 없습니다."
) : ServerException(500, message)
48 changes: 44 additions & 4 deletions src/main/kotlin/com/restaurant/be/review/domain/entity/Review.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.restaurant.be.review.domain.entity

import com.restaurant.be.common.entity.BaseEntity
import com.restaurant.be.common.exception.InvalidLikeCountException
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.UpdateReviewRequest
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.user.domain.entity.QUser.user
import com.restaurant.be.user.domain.entity.User
import kotlinx.serialization.json.JsonNull.content
import javax.persistence.CascadeType
Expand Down Expand Up @@ -32,31 +36,67 @@ class Review(
val restaurantId: Long,

@Column(nullable = false)
val content: String,
var content: String,

@Column(nullable = false)
val rating: Double,
var rating: Double,

@Column(name = "like_count", nullable = false)
var likeCount: Long = 0,

@Column(name = "view_count", nullable = false)
var viewCount: Long = 0,

// 부모 (Review Entity)가 주인이되어 Image참조 가능. 반대는 불가능
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true)
@JoinColumn(name = "review_id")
val images: MutableList<ReviewImage> = mutableListOf()
var images: MutableList<ReviewImage> = mutableListOf()

) : BaseEntity() {
fun addImage(reviewImage: ReviewImage) {
images.add(reviewImage)
}

fun updateReview(request: UpdateReviewRequest) {
val updateRequest = request.review
this.content = updateRequest.content
this.rating = updateRequest.rating
this.images.clear()
updateRequest.imageUrls.forEach {
this.addImage(
ReviewImage(
imageUrl = it
)
)
}
}

fun incrementViewCount() {
this.viewCount++
}
fun incrementLikeCount() {
this.likeCount++
}
fun decrementLikeCount() {
if (this.likeCount == 0L) {
throw InvalidLikeCountException()
}
this.likeCount--
}

fun toResponseDTO(doesUserLike: Boolean): ReviewResponseDto {
return ReviewResponseDto(
id = id ?: 0,
userId = user.id ?: 0,
username = user.nickname,
profileImageUrl = user.profileImageUrl,
restaurantId = restaurantId,
rating = rating,
content = content,
imageUrls = images.map { it.imageUrl },
isLike = doesUserLike
isLike = doesUserLike,
viewCount = viewCount,
likeCount = likeCount
)
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

package com.restaurant.be.review.domain.entity

import javax.persistence.Column
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.common.exception.UnAuthorizedDeleteException
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.repository.UserRepository
import org.springframework.stereotype.Service
import javax.transaction.Transactional
import kotlin.jvm.optionals.getOrNull

@Service
class DeleteReviewService(
private val reviewRepository: ReviewRepository,
private val userRepository: UserRepository
) {
@Transactional
fun deleteReview(reviewId: Long, email: String) {
val user = userRepository.findByEmail(email) ?: throw NotFoundUserEmailException()

var review = reviewRepository.findById(reviewId).getOrNull() ?: throw NotFoundReviewException()

if (user.id != review.user.id) throw UnAuthorizedDeleteException()

reviewRepository.deleteById(reviewId)
}
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,75 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.review.presentation.dto.GetMyReviewsResponse
import com.restaurant.be.review.presentation.dto.GetReviewResponse
import com.restaurant.be.review.repository.ReviewLikesRepository
import com.restaurant.be.review.presentation.dto.GetReviewsResponse
import com.restaurant.be.review.presentation.dto.ReviewWithLikesDto
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.repository.UserRepository
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class GetReviewService(
private val userRepository: UserRepository,
private val reviewRepository: ReviewRepository,
private val reviewLikesRepository: ReviewLikesRepository
private val reviewRepository: ReviewRepository

) {
fun getReviewListOf(page: Int, size: Int, email: String): GetReviewResponse {
val pageable = PageRequest.of(page, size)
val reviews = reviewRepository.findAll(pageable).content
@Transactional(readOnly = true)
fun getReviews(pageable: Pageable, email: String): GetReviewsResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()

val reviewsWithLikes = reviewRepository.findReviews(user, pageable)

val responseDtos = convertResponeDto(reviewsWithLikes)

return GetReviewsResponse(responseDtos)
}

@Transactional
fun getReview(reviewId: Long, email: String): GetReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()

return GetReviewResponse(
reviews.map {
it
.toResponseDTO(doesUserLike = isReviewLikedByUser(user.id, it.id))
}
val reviewWithLikes = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()

if (reviewWithLikes.review.user.id != user.id) {
reviewWithLikes.review.incrementViewCount()
}

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes.review,
reviewWithLikes.isLikedByUser
)

return GetReviewResponse(responseDto)
}

@Transactional(readOnly = true)
fun getMyReviews(pageable: Pageable, email: String): GetMyReviewsResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()

val reviewsWithLikes = reviewRepository.findMyReviews(user, pageable)

val reviewResponses = convertResponeDto(reviewsWithLikes)

return GetMyReviewsResponse(reviewResponses)
}

fun isReviewLikedByUser(userId: Long?, reviewId: Long?): Boolean {
if (userId != 0L) {
return reviewLikesRepository.existsByReviewIdAndUserId(userId, reviewId)
private fun convertResponeDto(reviewsWithLikes: List<ReviewWithLikesDto>): List<ReviewResponseDto> {
val reviewResponses = reviewsWithLikes.map {
ReviewResponseDto.toDto(
it.review,
it.isLikedByUser
)
}
return false
return reviewResponses
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.DuplicateLikeException
import com.restaurant.be.common.exception.NotFoundLikeException
import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.common.exception.NotFoundUserIdException
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.LikeReviewRequest
import com.restaurant.be.review.presentation.dto.LikeReviewResponse
import com.restaurant.be.review.presentation.dto.ReviewWithLikesDto
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewLikesRepository
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.domain.entity.User
import com.restaurant.be.user.repository.UserRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class LikeReviewService(
val userRepository: UserRepository,
val reviewLikesRepository: ReviewLikesRepository,
val reviewRepository: ReviewRepository
) {
@Transactional
fun likeReview(reviewId: Long, request: LikeReviewRequest, email: String): LikeReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()

val userId = user.id ?: throw NotFoundUserIdException()

likeReviewWhetherAlreadyLikeOrNot(request, reviewId, user, userId)

val reviewWithLikes: ReviewWithLikesDto? = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes!!.review,
reviewWithLikes.isLikedByUser
)

return LikeReviewResponse(responseDto)
}

private fun likeReviewWhetherAlreadyLikeOrNot(
request: LikeReviewRequest,
reviewId: Long,
user: User,
userId: Long
) {
if (request.isLike) {
if (isAlreadyLike(reviewId, user)) {
throw DuplicateLikeException()
}
reviewLikesRepository.save(request.toEntity(userId, reviewId))
val review = reviewRepository.findById(reviewId)
review.get().incrementLikeCount()
} else {
if (!isAlreadyLike(reviewId, user)) {
throw NotFoundLikeException()
}
reviewLikesRepository.deleteByReviewIdAndUserId(reviewId, userId)
val review = reviewRepository.findById(reviewId)
review.get().decrementLikeCount()
}
}

private fun isAlreadyLike(reviewId: Long, user: User) =
reviewLikesRepository.existsByReviewIdAndUserId(reviewId, user.id)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.restaurant.be.review.domain.service

import com.restaurant.be.common.exception.NotFoundReviewException
import com.restaurant.be.common.exception.NotFoundUserEmailException
import com.restaurant.be.common.exception.UnAuthorizedUpdateException
import com.restaurant.be.review.domain.entity.QReview.review
import com.restaurant.be.review.presentation.dto.UpdateReviewRequest
import com.restaurant.be.review.presentation.dto.UpdateReviewResponse
import com.restaurant.be.review.presentation.dto.common.ReviewResponseDto
import com.restaurant.be.review.repository.ReviewRepository
import com.restaurant.be.user.repository.UserRepository
import org.springframework.stereotype.Service
import javax.transaction.Transactional
import kotlin.jvm.optionals.getOrNull

@Service
class UpdateReviewService(
private val reviewRepository: ReviewRepository,
private val userRepository: UserRepository
) {
@Transactional
fun updateReview(restaurantId: Long, reviewId: Long, reviewRequest: UpdateReviewRequest, email: String): UpdateReviewResponse {
val user = userRepository.findByEmail(email)
?: throw NotFoundUserEmailException()

val review = reviewRepository.findById(reviewId)
.getOrNull()
?: throw NotFoundReviewException()

if (user.id != review.user.id) throw UnAuthorizedUpdateException()

review.updateReview(reviewRequest)

val reviewWithLikes = reviewRepository.findReview(user, reviewId)
?: throw NotFoundReviewException()

val responseDto = ReviewResponseDto.toDto(
reviewWithLikes.review,
reviewWithLikes.isLikedByUser
)

return UpdateReviewResponse(responseDto)
}
}
Loading

0 comments on commit 17dabfa

Please sign in to comment.