diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/domain/model/CafeRoom.kt b/src/main/kotlin/kr/nagaza/nagazaserver/domain/model/CafeRoom.kt index 0820394..4522f08 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/domain/model/CafeRoom.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/domain/model/CafeRoom.kt @@ -1,11 +1,16 @@ package kr.nagaza.nagazaserver.domain.model +import java.util.* + data class CafeRoom( val roomId: String, val cafeId: String, - val genre: String, + val title: String, + val description: String, + val genre: Set, val timeout: Int, val recommendedUserCnt: Int, val roomImgUrl: String?, - val description: String, + val createdAt: Date, + val updatedAt: Date, ) diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/domain/model/Genre.kt b/src/main/kotlin/kr/nagaza/nagazaserver/domain/model/Genre.kt new file mode 100644 index 0000000..74ff35f --- /dev/null +++ b/src/main/kotlin/kr/nagaza/nagazaserver/domain/model/Genre.kt @@ -0,0 +1,6 @@ +package kr.nagaza.nagazaserver.domain.model + +data class Genre( + val genreId: String, + val name: String, +) diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/domain/repository/CafeRoomRepository.kt b/src/main/kotlin/kr/nagaza/nagazaserver/domain/repository/CafeRoomRepository.kt index 40a0962..6dd6bd9 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/domain/repository/CafeRoomRepository.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/domain/repository/CafeRoomRepository.kt @@ -7,5 +7,11 @@ interface CafeRoomRepository { fun findByRoomId(roomId: String): CafeRoom? - fun getAllGenres(): List + fun search( + queryString: String?, + genre: String?, + address1: String?, + address2: String?, + cafeId: String?, + ): List } diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/domain/service/CafeRoomService.kt b/src/main/kotlin/kr/nagaza/nagazaserver/domain/service/CafeRoomService.kt index a9b5ea6..4b1be70 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/domain/service/CafeRoomService.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/domain/service/CafeRoomService.kt @@ -6,6 +6,7 @@ import kr.nagaza.nagazaserver.domain.exception.CafeRoomNotFoundException import kr.nagaza.nagazaserver.domain.model.CafeRoom import kr.nagaza.nagazaserver.domain.repository.CafeRepository import kr.nagaza.nagazaserver.domain.repository.CafeRoomRepository +import kr.nagaza.nagazaserver.presenter.restapi.dto.request.RoomSearchQuery import org.springframework.stereotype.Service @Service @@ -25,7 +26,13 @@ class CafeRoomService( return cafeRoomRepository.findByRoomId(roomId) ?: throw CafeRoomNotFoundException() } - fun getAllGenres(): List { - return cafeRoomRepository.getAllGenres() + fun search(cafeRoomSearchQuery: RoomSearchQuery): List { + return cafeRoomRepository.search( + queryString = cafeRoomSearchQuery.queryString, + genre = cafeRoomSearchQuery.genre, + address1 = cafeRoomSearchQuery.address1, + address2 = cafeRoomSearchQuery.address2, + cafeId = cafeRoomSearchQuery.cafeId, + ) } } diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/entity/CafeRoomEntity.kt b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/entity/CafeRoomEntity.kt index e4c4416..97dba0c 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/entity/CafeRoomEntity.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/entity/CafeRoomEntity.kt @@ -2,62 +2,75 @@ package kr.nagaza.nagazaserver.infrastructure.jpa.entity import jakarta.persistence.* import kr.nagaza.nagazaserver.domain.model.CafeRoom +import org.hibernate.annotations.CreationTimestamp +import java.util.* @Entity(name = "cafe_room") class CafeRoomEntity( @Id @Column(name = "room_id") val roomId: String, - @Column(name = "cafe_id") val cafeId: String, - - @Column(name = "genre") - val genre: String, - + @Column(name = "title") + val title: String, + @Column(name = "description") + val description: String, + @ManyToMany + @JoinTable( + name = "cafe_room_genre", + joinColumns = [JoinColumn(name = "room_id")], + inverseJoinColumns = [JoinColumn(name = "genre_id")], + ) + val genre: MutableSet = mutableSetOf(), @Column(name = "timeout") val timeout: Int, - @Column(name = "recommend_user") val recommendUserCnt: Int, - @Column(name = "room_img_url") val roomImgUrl: String?, - - @Column(name = "description") - val description: String, - @ManyToMany(fetch = FetchType.LAZY) @JoinTable( name = "cafe_room_like", joinColumns = [JoinColumn(name = "room_id")], - inverseJoinColumns = [JoinColumn(name = "user_id")] + inverseJoinColumns = [JoinColumn(name = "user_id")], ) val likedUsers: MutableSet = mutableSetOf(), - @OneToMany(fetch = FetchType.LAZY) @JoinColumn(name = "room_id") val reviews: MutableList = mutableListOf(), + @Column(name = "created_at") + @CreationTimestamp() + val createdAt: Date, + val updatedAt: Date, ) { - fun toModel() = CafeRoom( - roomId = roomId, - cafeId = cafeId, - genre = genre, - timeout = timeout, - recommendedUserCnt = recommendUserCnt, - roomImgUrl = roomImgUrl, - description = description, - ) + fun toModel() = + CafeRoom( + roomId = roomId, + cafeId = cafeId, + genre = genre.map { it.toModel() }.toSet(), + timeout = timeout, + recommendedUserCnt = recommendUserCnt, + roomImgUrl = roomImgUrl, + description = description, + title = title, + createdAt = createdAt, + updatedAt = updatedAt, + ) companion object { - fun fromModel(model: CafeRoom) = CafeRoomEntity( - roomId = model.roomId, - cafeId = model.cafeId, - genre = model.genre, - timeout = model.timeout, - recommendUserCnt = model.recommendedUserCnt, - roomImgUrl = model.roomImgUrl, - description = model.description, - ) + fun fromModel(model: CafeRoom) = + CafeRoomEntity( + roomId = model.roomId, + cafeId = model.cafeId, + genre = model.genre.map { GenreEntity.fromModel(it) }.toMutableSet(), + timeout = model.timeout, + recommendUserCnt = model.recommendedUserCnt, + roomImgUrl = model.roomImgUrl, + title = model.title, + description = model.description, + createdAt = model.createdAt, + updatedAt = model.updatedAt, + ) } } diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/entity/GenreEntity.kt b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/entity/GenreEntity.kt new file mode 100644 index 0000000..9ab11fd --- /dev/null +++ b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/entity/GenreEntity.kt @@ -0,0 +1,31 @@ +package kr.nagaza.nagazaserver.infrastructure.jpa.entity + +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.Id +import kr.nagaza.nagazaserver.domain.model.Genre + +@Entity(name = "genre") +class GenreEntity( + @Id + @Column(name = "genre_id") + val genreId: String, + @Column(name = "name") + val name: String, +) { + fun toModel(): Genre { + return Genre( + genreId = genreId, + name = name, + ) + } + + companion object { + fun fromModel(genre: Genre): GenreEntity { + return GenreEntity( + genreId = genre.genreId, + name = genre.name, + ) + } + } +} diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/CafeRoomRepositoryImpl.kt b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/CafeRoomRepositoryImpl.kt index 475f6be..faed2f5 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/CafeRoomRepositoryImpl.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/CafeRoomRepositoryImpl.kt @@ -1,18 +1,21 @@ package kr.nagaza.nagazaserver.infrastructure.jpa.repository +import com.querydsl.jpa.impl.JPAQuery +import jakarta.persistence.EntityManager +import jakarta.persistence.PersistenceContext import kr.nagaza.nagazaserver.domain.model.CafeRoom import kr.nagaza.nagazaserver.domain.repository.CafeRoomRepository +import kr.nagaza.nagazaserver.infrastructure.jpa.entity.QCafeEntity +import kr.nagaza.nagazaserver.infrastructure.jpa.entity.QCafeRoomEntity import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Repository @Repository class CafeRoomRepositoryImpl( private val jpaCafeRoomRepository: JpaCafeRoomRepository, + @PersistenceContext + private val entityManager: EntityManager, ) : CafeRoomRepository { - override fun getAllGenres(): List { - return jpaCafeRoomRepository.findAllGenres() - } - override fun getAllRoomByCafeId(cafeId: String): List { return jpaCafeRoomRepository.findAllByCafeId(cafeId) .map { @@ -23,4 +26,45 @@ class CafeRoomRepositoryImpl( override fun findByRoomId(roomId: String): CafeRoom? { return jpaCafeRoomRepository.findByIdOrNull(roomId)?.toModel() } + + override fun search( + queryString: String?, + genre: String?, + address1: String?, + address2: String?, + cafeId: String?, + ): List { + val cafeRoomEntity = QCafeRoomEntity.cafeRoomEntity + val cafeEntity = QCafeEntity.cafeEntity + val query = + JPAQuery(entityManager) + .select(cafeRoomEntity) + .from(cafeRoomEntity) + .leftJoin(cafeEntity).on(cafeRoomEntity.cafeId.eq(cafeEntity.cafeId)) + .leftJoin(cafeRoomEntity.genre) + + if (queryString != null) { + query.where(cafeRoomEntity.title.contains(queryString)) + } + + if (genre != null) { + query.where(cafeRoomEntity.genre.any().genreId.eq(genre)) + } + + if (address1 != null) { + query.where(cafeEntity.addressOne.eq(address1)) + } + + if (address2 != null) { + query.where(cafeEntity.addressTwo.eq(address2)) + } + + if (cafeId != null) { + query.where(cafeRoomEntity.cafeId.eq(cafeId)) + } + + return query.fetch().map { + it.toModel() + } + } } diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/JpaCafeRoomRepository.kt b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/JpaCafeRoomRepository.kt index 88b7e6d..abddbd9 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/JpaCafeRoomRepository.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/infrastructure/jpa/repository/JpaCafeRoomRepository.kt @@ -2,11 +2,7 @@ package kr.nagaza.nagazaserver.infrastructure.jpa.repository import kr.nagaza.nagazaserver.infrastructure.jpa.entity.CafeRoomEntity import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Query interface JpaCafeRoomRepository : JpaRepository { fun findAllByCafeId(cafeId: String): List - - @Query("SELECT DISTINCT cafe_room.genre FROM cafe_room") - fun findAllGenres(): List } diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/api/CafeRoomApi.kt b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/api/CafeRoomApi.kt index 5134f86..0b0f625 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/api/CafeRoomApi.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/api/CafeRoomApi.kt @@ -3,6 +3,7 @@ package kr.nagaza.nagazaserver.presenter.restapi.api import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.tags.Tag import kr.nagaza.nagazaserver.presenter.restapi.dto.response.CafeRoomResponse +import org.springframework.data.repository.query.Param import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping @@ -10,18 +11,21 @@ import org.springframework.web.bind.annotation.RestController @Tag(name = "카페 방 API", description = "카페 방 관련 API") @RestController -@RequestMapping("/v1/cafes/{cafeId}/rooms") +@RequestMapping("/v1/rooms") interface CafeRoomApi { @Operation(summary = "카페 전체 방 목록", description = "카페 내의 전체 방 목록을 반환합니다.") - @GetMapping - fun getCafeRooms( - @PathVariable cafeId: String, + @GetMapping("/search") + fun searchRooms( + @Param("queryString") queryString: String?, + @Param("genre") genre: String?, + @Param("address1") address1: String?, + @Param("address2") address2: String?, + @Param("cafeId") cafeId: String?, ): List @Operation(summary = "특정 방 조회", description = "카페 내의 특정 방 정보를 조회합니다.") @GetMapping("/{roomId}") fun getCafeRoom( - @PathVariable cafeId: String, @PathVariable roomId: String, ): CafeRoomResponse } diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/controller/CafeRoomController.kt b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/controller/CafeRoomController.kt index 137ee48..5e65c21 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/controller/CafeRoomController.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/controller/CafeRoomController.kt @@ -2,6 +2,7 @@ package kr.nagaza.nagazaserver.presenter.restapi.controller import kr.nagaza.nagazaserver.domain.service.CafeRoomService import kr.nagaza.nagazaserver.presenter.restapi.api.CafeRoomApi +import kr.nagaza.nagazaserver.presenter.restapi.dto.request.RoomSearchQuery import kr.nagaza.nagazaserver.presenter.restapi.dto.response.CafeRoomResponse import org.springframework.stereotype.Controller @@ -9,13 +10,26 @@ import org.springframework.stereotype.Controller class CafeRoomController( private val cafeRoomService: CafeRoomService, ) : CafeRoomApi { - override fun getCafeRooms(cafeId: String): List { - return cafeRoomService.getRoomsByCafeId(cafeId) - .map(CafeRoomResponse::fromModel) - } - - override fun getCafeRoom(cafeId: String, roomId: String): CafeRoomResponse { + override fun getCafeRoom(roomId: String): CafeRoomResponse { return cafeRoomService.getRoomByRoomId(roomId) .let(CafeRoomResponse::fromModel) } + + override fun searchRooms( + queryString: String?, + genre: String?, + address1: String?, + address2: String?, + cafeId: String?, + ): List { + return cafeRoomService.search( + RoomSearchQuery( + queryString = queryString, + genre = genre, + address1 = address1, + address2 = address2, + cafeId = cafeId, + ), + ).map(CafeRoomResponse::fromModel) + } } diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/dto/request/RoomSearchQuery.kt b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/dto/request/RoomSearchQuery.kt new file mode 100644 index 0000000..bec02f4 --- /dev/null +++ b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/dto/request/RoomSearchQuery.kt @@ -0,0 +1,9 @@ +package kr.nagaza.nagazaserver.presenter.restapi.dto.request + +class RoomSearchQuery( + val queryString: String?, + val address1: String?, + val address2: String?, + val genre: String?, + val cafeId: String?, +) diff --git a/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/dto/response/CafeRoomResponse.kt b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/dto/response/CafeRoomResponse.kt index bee1e95..a9b0ecd 100644 --- a/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/dto/response/CafeRoomResponse.kt +++ b/src/main/kotlin/kr/nagaza/nagazaserver/presenter/restapi/dto/response/CafeRoomResponse.kt @@ -7,11 +7,32 @@ import kr.nagaza.nagazaserver.domain.model.CafeRoom data class CafeRoomResponse( @Schema(description = "카페 방 아이디", example = "01HDNFJHCNS5E2W35YTB030TJ8") val roomId: String, + @Schema(description = "카페 아이디", example = "01HDNFJHCNS5E2W35YTB030TJ8") + val cafeId: String, + @Schema(description = "제목", example = "형주의 방탈출") + val title: String, + @Schema(description = "설명", example = "형주의 방탈출 설명") + val description: String, + @Schema(description = "장르 (장르 Id)", example = "['1', '2']") + val genre: Set, + @Schema(description = "추천인원", example = "4") + val recommendedUser: Int, + @Schema(description = "방 이미지", example = "https://nagaza.kr/room/01HDNFJHCNS5E2W35YTB030TJ8.jpg") + val roomImage: String, + @Schema(description = "탈출 제한 시간", example = "60") + val timeout: Int, ) { companion object { fun fromModel(model: CafeRoom): CafeRoomResponse { return CafeRoomResponse( roomId = model.roomId, + cafeId = model.cafeId, + title = model.title, + description = model.description, + genre = model.genre.map { it.genreId }.toSet(), + recommendedUser = model.recommendedUserCnt, + roomImage = model.roomImgUrl ?: "", + timeout = model.timeout, ) } }