Skip to content

Commit

Permalink
feat: 음식점 조회 시 셀럽 필터 추가 (#75)
Browse files Browse the repository at this point in the history
* feat: 음식점 검색 조건으로 방문 셀럽 조회 유즈케이스 정의

* feat: 음식점 검색 조건에 대한 전체 결과를 조회하는 쿼리 추가

* feat: 음식점으로 셀럽 조회 쿼리 추가

* feat: 음식점 검색 조건에 해당하는 음식점에 다녀간 셀럽 조회 유즈케이스 구현

* feat: 음식점 검색 조건에 해당하는 음식점에 다녀간 셀럽 조회 API 추가

* feat: 음식점 검색 조건에 셀럽 Id 추가
  • Loading branch information
TaeyeonRoyce authored Oct 13, 2024
1 parent fd46a5b commit b194030
Show file tree
Hide file tree
Showing 39 changed files with 309 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import com.celuveat.auth.adapter.`in`.rest.AuthContext
import com.celuveat.celeb.adapter.`in`.rest.response.BestCelebrityResponse
import com.celuveat.celeb.adapter.`in`.rest.response.CelebrityResponse
import com.celuveat.celeb.adapter.`in`.rest.response.CelebrityWithInterestedResponse
import com.celuveat.celeb.adapter.`in`.rest.response.SimpleCelebrityResponse
import com.celuveat.restaurant.adapter.`in`.rest.request.ReadRestaurantsRequest
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.enums.ParameterIn
import io.swagger.v3.oas.annotations.security.SecurityRequirement
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping

Expand Down Expand Up @@ -70,4 +73,10 @@ interface CelebrityApi {
)
@PathVariable celebrityId: Long,
): CelebrityWithInterestedResponse

@Operation(summary = "필터용 셀럽 조회")
@GetMapping("/in/restaurants/condition")
fun readCelebritiesInRestaurantsCondition(
@ModelAttribute request: ReadRestaurantsRequest,
): List<SimpleCelebrityResponse>
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ import com.celuveat.auth.adapter.`in`.rest.AuthContext
import com.celuveat.celeb.adapter.`in`.rest.response.BestCelebrityResponse
import com.celuveat.celeb.adapter.`in`.rest.response.CelebrityResponse
import com.celuveat.celeb.adapter.`in`.rest.response.CelebrityWithInterestedResponse
import com.celuveat.celeb.adapter.`in`.rest.response.SimpleCelebrityResponse
import com.celuveat.celeb.application.port.`in`.AddInterestedCelebrityUseCase
import com.celuveat.celeb.application.port.`in`.DeleteInterestedCelebrityUseCase
import com.celuveat.celeb.application.port.`in`.ReadBestCelebritiesUseCase
import com.celuveat.celeb.application.port.`in`.ReadCelebritiesInRestaurantConditionUseCase
import com.celuveat.celeb.application.port.`in`.ReadCelebrityUseCase
import com.celuveat.celeb.application.port.`in`.ReadInterestedCelebritiesUseCase
import com.celuveat.celeb.application.port.`in`.command.AddInterestedCelebrityCommand
import com.celuveat.celeb.application.port.`in`.command.DeleteInterestedCelebrityCommand
import com.celuveat.celeb.application.port.`in`.query.ReadCelebritiesInRestaurantConditionQuery
import com.celuveat.celeb.application.port.`in`.query.ReadCelebrityQuery
import com.celuveat.common.utils.geometry.SquarePolygon
import com.celuveat.restaurant.adapter.`in`.rest.request.ReadRestaurantsRequest
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
Expand All @@ -28,6 +34,7 @@ class CelebrityController(
private val addInterestedCelebrityUseCase: AddInterestedCelebrityUseCase,
private val deleteInterestedCelebrityUseCase: DeleteInterestedCelebrityUseCase,
private val readCelebrityUseCase: ReadCelebrityUseCase,
private val readCelebritiesInRestaurantConditionUseCase: ReadCelebritiesInRestaurantConditionUseCase,
) : CelebrityApi {
@GetMapping("/interested")
override fun readInterestedCelebrities(
Expand Down Expand Up @@ -77,4 +84,23 @@ class CelebrityController(
val celebrityResult = readCelebrityUseCase.readCelebrity(query)
return CelebrityWithInterestedResponse.from(celebrityResult)
}

@GetMapping("/in/restaurants/condition")
override fun readCelebritiesInRestaurantsCondition(
@ModelAttribute request: ReadRestaurantsRequest,
): List<SimpleCelebrityResponse> {
val query = ReadCelebritiesInRestaurantConditionQuery(
category = request.category,
region = request.region,
searchArea = SquarePolygon.ofNullable(
lowLongitude = request.lowLongitude,
highLongitude = request.highLongitude,
lowLatitude = request.lowLatitude,
highLatitude = request.highLatitude,
),
)

val results = readCelebritiesInRestaurantConditionUseCase.readCelebritiesInRestaurantCondition(query)
return results.map { SimpleCelebrityResponse.from(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.celuveat.celeb.adapter.out.persistence

import com.celuveat.celeb.adapter.out.persistence.entity.CelebrityJpaRepository
import com.celuveat.celeb.adapter.out.persistence.entity.CelebrityPersistenceMapper
import com.celuveat.celeb.adapter.out.persistence.entity.CelebrityRestaurantJpaRepository
import com.celuveat.celeb.adapter.out.persistence.entity.CelebrityYoutubeContentJpaRepository
import com.celuveat.celeb.adapter.out.persistence.entity.RestaurantInVideoJpaRepository
import com.celuveat.celeb.adapter.out.persistence.entity.YoutubeContentJpaEntity
Expand All @@ -14,6 +15,7 @@ class CelebrityPersistenceAdapter(
private val celebrityJpaRepository: CelebrityJpaRepository,
private val celebrityYoutubeContentJpaRepository: CelebrityYoutubeContentJpaRepository,
private val restaurantInVideoJpaRepository: RestaurantInVideoJpaRepository,
private val celebrityRestaurantJpaRepository: CelebrityRestaurantJpaRepository,
private val celebrityPersistenceMapper: CelebrityPersistenceMapper,
) : ReadCelebritiesPort {
override fun readVisitedCelebritiesByRestaurants(restaurantIds: List<Long>): Map<Long, List<Celebrity>> {
Expand All @@ -31,6 +33,13 @@ class CelebrityPersistenceAdapter(
}
}

override fun readByRestaurants(restaurantIds: List<Long>): List<Celebrity> {
return celebrityRestaurantJpaRepository.findAllByRestaurantIdIn(restaurantIds)
.map { it.celebrity }
.distinct()
.map { celebrityPersistenceMapper.toDomainWithoutYoutubeContent(it) }
}

private fun celebritiesToContentMap(celebrityIds: List<Long>): Map<Long, List<YoutubeContentJpaEntity>> =
celebrityYoutubeContentJpaRepository.findByCelebrityIdIn(celebrityIds)
.groupBy { it.celebrity.id }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class CelebrityJpaEntity(
val profileImageUrl: String,
val introduction: String,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class CelebrityRestaurantJpaEntity(
@JoinColumn(name = "restaurant_id", foreignKey = ForeignKey(ConstraintMode.NO_CONSTRAINT))
val restaurant: RestaurantJpaEntity,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.celuveat.celeb.adapter.out.persistence.entity
import com.celuveat.restaurant.adapter.out.persistence.entity.RestaurantJpaEntity
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Slice
import org.springframework.data.jpa.repository.EntityGraph
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query

Expand Down Expand Up @@ -38,4 +39,7 @@ interface CelebrityRestaurantJpaRepository : JpaRepository<CelebrityRestaurantJp
""",
)
fun findMostVisitedRestaurantsTop10(): List<RestaurantJpaEntity>

@EntityGraph(attributePaths = ["celebrity"])
fun findAllByRestaurantIdIn(restaurantIds: List<Long>): List<CelebrityRestaurantJpaEntity>
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CelebrityYoutubeContentJpaEntity(
@JoinColumn(name = "youtube_content_id", foreignKey = ForeignKey(ConstraintMode.NO_CONSTRAINT))
val youtubeContent: YoutubeContentJpaEntity,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class InterestedCelebrityJpaEntity(
@JoinColumn(name = "celebrity_id", foreignKey = ForeignKey(ConstraintMode.NO_CONSTRAINT))
val celebrity: CelebrityJpaEntity,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class RestaurantInVideoJpaEntity(
@JoinColumn(name = "video_id", foreignKey = ForeignKey(ConstraintMode.NO_CONSTRAINT))
val video: VideoJpaEntity,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class VideoJpaEntity(
@JoinColumn(name = "youtube_content_id", foreignKey = ForeignKey(ConstraintMode.NO_CONSTRAINT))
val youtubeContent: YoutubeContentJpaEntity,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class YoutubeContentJpaEntity(
val restaurantCount: Int,
val subscriberCount: Long,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.celuveat.celeb.application

import com.celuveat.celeb.application.port.`in`.ReadBestCelebritiesUseCase
import com.celuveat.celeb.application.port.`in`.ReadCelebritiesInRestaurantConditionUseCase
import com.celuveat.celeb.application.port.`in`.ReadCelebrityUseCase
import com.celuveat.celeb.application.port.`in`.ReadInterestedCelebritiesUseCase
import com.celuveat.celeb.application.port.`in`.query.ReadCelebritiesInRestaurantConditionQuery
import com.celuveat.celeb.application.port.`in`.query.ReadCelebrityQuery
import com.celuveat.celeb.application.port.`in`.result.BestCelebrityResult
import com.celuveat.celeb.application.port.`in`.result.CelebrityResult
Expand All @@ -22,7 +24,10 @@ class CelebrityQueryService(
private val readRestaurantPort: ReadRestaurantPort,
private val readInterestedCelebritiesPort: ReadInterestedCelebritiesPort,
private val readInterestedRestaurantPort: ReadInterestedRestaurantPort,
) : ReadInterestedCelebritiesUseCase, ReadBestCelebritiesUseCase, ReadCelebrityUseCase {
) : ReadInterestedCelebritiesUseCase,
ReadBestCelebritiesUseCase,
ReadCelebrityUseCase,
ReadCelebritiesInRestaurantConditionUseCase {
override fun getInterestedCelebrities(memberId: Long): List<CelebrityResult> {
val celebrities = readInterestedCelebritiesPort.readInterestedCelebrities(memberId)
return celebrities.map { CelebrityResult.from(it.celebrity) }
Expand Down Expand Up @@ -77,4 +82,15 @@ class CelebrityQueryService(
isInterested = interested,
)
}

override fun readCelebritiesInRestaurantCondition(query: ReadCelebritiesInRestaurantConditionQuery): List<SimpleCelebrityResult> {
val restaurants = readRestaurantPort.readRestaurantsByCondition(
category = query.category,
region = query.region,
searchArea = query.searchArea,
celebrityId = null, // celebrityId is not used in this case
)
return readCelebritiesPort.readByRestaurants(restaurants.map { it.id })
.map { SimpleCelebrityResult.from(it) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.celuveat.celeb.application.port.`in`

import com.celuveat.celeb.application.port.`in`.query.ReadCelebritiesInRestaurantConditionQuery
import com.celuveat.celeb.application.port.`in`.result.SimpleCelebrityResult

interface ReadCelebritiesInRestaurantConditionUseCase {

fun readCelebritiesInRestaurantCondition(query: ReadCelebritiesInRestaurantConditionQuery): List<SimpleCelebrityResult>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.celuveat.celeb.application.port.`in`.query

import com.celuveat.common.utils.geometry.SquarePolygon

data class ReadCelebritiesInRestaurantConditionQuery(
val category: String?,
val region: String?,
val searchArea: SquarePolygon?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.celuveat.celeb.domain.Celebrity
interface ReadCelebritiesPort {
fun readVisitedCelebritiesByRestaurants(restaurantIds: List<Long>): Map<Long, List<Celebrity>>

fun readByRestaurants(restaurantIds: List<Long>): List<Celebrity>

fun readVisitedCelebritiesByRestaurant(restaurantId: Long): List<Celebrity>

fun readBestCelebrities(): List<Celebrity>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package com.celuveat.common.adapter.out.persistence.entity

import jakarta.persistence.EntityListeners
import jakarta.persistence.MappedSuperclass
import java.time.LocalDateTime
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.annotation.LastModifiedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime

@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
Expand All @@ -16,18 +16,18 @@ abstract class RootEntity<ID> {
@LastModifiedDate
lateinit var updatedAt: LocalDateTime

abstract fun id(): ID
abstract fun readId(): ID

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as RootEntity<*>

return id() == other.id()
return readId() == other.readId()
}

override fun hashCode(): Int {
return id()?.hashCode() ?: 0
return readId()?.hashCode() ?: 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class MemberJpaEntity(
val socialId: String,
var refreshToken: String,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class NotificationJpaEntity(
val content: String,
var isRead: Boolean,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class RegionJpaEntity(
val latitude: Double,
val longitude: Double,
) : RootEntity<Long>() {
override fun id(): Long {
override fun readId(): Long {
return this.id
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ class RestaurantController(
lowLatitude = request.lowLatitude,
highLatitude = request.highLatitude,
),
celebrityId = request.celebrityId,
)
return readAmountOfRestaurantsUseCase.readAmountOfRestaurants(query)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ data class ReadRestaurantsRequest(
required = false,
)
val highLatitude: Double?,
@Parameter(
`in` = ParameterIn.QUERY,
description = "셀럽 ID",
example = "1",
required = false,
)
val celebrityId: Long?,
) {
fun toQuery(
memberId: Long?,
Expand All @@ -64,6 +71,7 @@ data class ReadRestaurantsRequest(
lowLatitude = lowLatitude,
highLatitude = highLatitude,
),
celebrityId = celebrityId,
page = page,
size = size,
)
Expand Down
Loading

0 comments on commit b194030

Please sign in to comment.