Skip to content
Merged
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
3 changes: 0 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@ dependencies {
// xml
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.0")

implementation 'org.springframework.boot:spring-boot-starter-webflux'



}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@Tag(name = "채팅 관련 API")
Expand All @@ -30,9 +31,15 @@ class ChatRestController(
}

@GetMapping("/history")
@Operation(summary = "채팅 조회 API", description = "채팅 목록을 조회합니다.")
fun getHistory(): ApiResponse<ChatMessageResponseDTO.ListDto> {
val chatList = chatMongoService.getChatHistory()
@Operation(summary = "채팅 조회 API",
description = """
채팅 목록을 조회합니다.
- cursor 페이지네이션 처리를 합니다.
처음 조회할 때는 cursor-id에 null이 들어가고, 이후에는 cursor-id에 응답받은 cursor_id를 넣어 요청하세요.
- page-size의 기본값은 30이며 최대값은 30입니다.
""")
fun getHistory(@RequestParam("cursor-id", required = false) cursorId: String?, @RequestParam("page-size", required = false, defaultValue = "30") size: Int): ApiResponse<ChatMessageResponseDTO.ListDto> {
val chatList = chatMongoService.getChatHistory(cursorId, size)
return ApiResponse.onSuccess(chatList)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ class ChatMessageResponseDTO {

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class ListDto(
val chatList: List<ChatInfoDto>
val chatList: List<ChatInfoDto>,
val nextCursor: String? = null
)

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class ChatInfoDto(
val userName: String,
val userName: String?,
val userImage: String?,
val content: String,
val dateTime: LocalDateTime?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ data class ChatMessageSendDTO(
// var userId: Long? = null,
var type: MessageType? = null, // 메시지 타입
var message: String? = null, // 메시지
var time: LocalDateTime? = null, // 전송 시간
// var time: LocalDateTime? = null, // 전송 시간
){

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package busanVibe.busan.domain.chat.repository

import busanVibe.busan.domain.chat.domain.ChatMessage
import org.springframework.data.domain.Pageable
import org.springframework.data.mongodb.repository.MongoRepository

interface ChatMongoRepository: MongoRepository<ChatMessage, String> {
fun findAllByOrderByTime(): List<ChatMessage>
fun findAllByOrderByTimeDesc(pageable: Pageable): List<ChatMessage>
fun findByIdLessThanOrderByTimeDesc(id: String, pageable: Pageable): List<ChatMessage>
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import busanVibe.busan.domain.chat.dto.websocket.ChatMessageResponseDTO
import busanVibe.busan.domain.chat.dto.websocket.ChatMessageSendDTO
import busanVibe.busan.domain.chat.enums.MessageType
import busanVibe.busan.domain.chat.repository.ChatMongoRepository
import busanVibe.busan.domain.user.data.User
import busanVibe.busan.domain.user.repository.UserRepository
import busanVibe.busan.domain.user.service.login.AuthService
import busanVibe.busan.global.apiPayload.code.ErrorReasonDTO
import busanVibe.busan.global.apiPayload.code.status.ErrorStatus
import busanVibe.busan.global.apiPayload.exception.GeneralException
import org.springframework.data.domain.Pageable
import org.springframework.data.redis.listener.ChannelTopic
import org.springframework.http.HttpStatus
import org.springframework.messaging.simp.SimpMessagingTemplate
Expand Down Expand Up @@ -50,7 +53,7 @@ class ChatMongoService(
type = chatMessage.type?: MessageType.CHAT,
userId = currentUser.id,
message = chatMessage.message?:"",
time = chatMessage.time?: LocalDateTime.now(),
time = LocalDateTime.now(),
)

// 채팅 저장
Expand All @@ -68,26 +71,45 @@ class ChatMongoService(
redisPublisher.publish(topic, receiveDto)
}

fun getChatHistory(): ChatMessageResponseDTO.ListDto {
fun getChatHistory(cursorId: String?, pageSize: Int = 15): ChatMessageResponseDTO.ListDto {

if(pageSize < 0){
throw GeneralException(ErrorStatus.INVALID_PAGE_SIZE_MINUS)
}else if (pageSize > 30) {
throw GeneralException(ErrorStatus.INVALID_PAGE_SIZE_BIG)
}

// 현재 로그인한 유저 조회
val currentUser = AuthService().getCurrentUser()

val chatHistory = chatMongoRepository.findAllByOrderByTime()
// 조회 -> List<ChatMessage> 변수 선언 및 초기화
val chatHistory: List<ChatMessage> = if (cursorId != null) { // 처음이면 cursorId 없이 조회
chatMongoRepository.findByIdLessThanOrderByTimeDesc(cursorId, Pageable.ofSize(pageSize))
} else { // 처음 아니면 cursorId로 조회
chatMongoRepository.findAllByOrderByTimeDesc(Pageable.ofSize(pageSize))
}

val dtoList = chatHistory.mapNotNull { chat ->
val user = chat.userId?.let { userRepository.findById(it).orElse(null) }
// userId List 저장. 바로 뒤에 userId로 User 정보들을 먼저 찾고, 그 뒤에 DTO 변환
val userIdList: List<Long> = chatHistory.mapNotNull { it -> it.userId }.toList()

if (user == null) return@mapNotNull null
// <userId, User> -> Map<Long, User>로 저장
val userMap: Map<Long, User> = userRepository.findUsersByIdIn(userIdList).associateBy { it.id as Long }

// DTO 변환
val dtoList = chatHistory.map { chat ->
val user: User? = userMap[chat.userId ?: -1]
ChatMessageResponseDTO.ChatInfoDto(
userName = user.nickname,
userImage = user.profileImageUrl,
content = chat.message,
userName = user?.nickname ?: "알 수 없음",
userImage = user?.profileImageUrl,
dateTime = chat.time,
isMy = user.id == currentUser.id
content = chat.message,
isMy = user?.id == currentUser.id
)
}

return ChatMessageResponseDTO.ListDto(dtoList)
val nextCursor = chatHistory.lastOrNull()?.id

return ChatMessageResponseDTO.ListDto(dtoList, nextCursor)
}


Expand Down
12 changes: 8 additions & 4 deletions src/main/kotlin/busanVibe/busan/domain/common/dto/InfoType.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package busanVibe.busan.domain.common.dto

import busanVibe.busan.domain.place.enums.PlaceType

enum class InfoType(
val kr: String,
val en: String,
val placeType: PlaceType? = null
) {
ALL("전체", "ALL"),
ALL("전체", "ALL", PlaceType.ALL),
// 명소
SIGHT("명소", "SIGHT"),
RESTAURANT("식당", "RESTAURANT"),
CAFE("카페", "CAFE"),
SIGHT("명소", "SIGHT", PlaceType.SIGHT),
RESTAURANT("식당", "RESTAURANT", PlaceType.RESTAURANT),
CAFE("카페", "CAFE", PlaceType.CAFE),
CULTURE("문화시설", "CULTURE", PlaceType.CULTURE),
// 축제
FESTIVAL("축제", "FESTIVAL"),
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ package busanVibe.busan.domain.festival.repository
import busanVibe.busan.domain.festival.domain.Festival
import busanVibe.busan.domain.festival.domain.FestivalLike
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param

interface FestivalLikesRepository: JpaRepository<FestivalLike, String> {

fun findAllByFestivalIn(festivalList: List<Festival>): List<FestivalLike>
fun findAllByFestivalIn(festivalList: Set<Festival>): List<FestivalLike>

@Query("SELECT fl FROM FestivalLike fl WHERE fl.festival IN :festivals")
fun findLikeByFestival(@Param("festivals") festivalList: List<Festival>): List<FestivalLike>

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package busanVibe.busan.domain.festival.repository

import busanVibe.busan.domain.festival.domain.Festival
import busanVibe.busan.domain.festival.enums.FestivalStatus
import busanVibe.busan.domain.user.data.User
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
Expand Down Expand Up @@ -33,4 +34,15 @@ interface FestivalRepository: JpaRepository<Festival, Long> {
)
fun findAllWithFetch(): List<Festival>

@Query(
"""
SELECT f FROM Festival f
LEFT JOIN FETCH f.festivalImages
LEFT JOIN FETCH f.festivalLikes fl
LEFT JOIN FETCH fl.user
WHERE fl.user = :user
"""
)
fun findLikeFestivals(@Param("user") user: User): List<Festival>

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@ class PlaceCongestionController (
){

@GetMapping
@Operation(summary = "지도 조회")
@Operation(summary = "지도 조회",
description =
"""
지도에 띄울 장소를 조회합니다.
좌측 상단의 좌표와 우측 하단의 위경도를 요청 파라미터로 포함하세요.
좌측상단 ( lat1, lng1 ), 우측하단 ( lat2, lng2 )
lat1 > lat2
lng1 < lng2
"""
)
fun map(
@RequestParam("type", required = false, defaultValue = "ALL") type: PlaceType,
@RequestParam("latitude")latitude: Double,
@RequestParam("longitude")longitude: Double): ApiResponse<PlaceMapResponseDTO.MapListDto>{
@RequestParam("lat1")lat1: Double,
@RequestParam("lng1")lng1: Double,
@RequestParam("lat2")lat2: Double,
@RequestParam("lng2")lng2: Double
): ApiResponse<PlaceMapResponseDTO.MapListDto>{

val places = placeCongestionQueryService.getMap(type, latitude, longitude)
val places = placeCongestionQueryService.getMap(type, lat1, lng1, lat2, lng2)
return ApiResponse.onSuccess(places);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ package busanVibe.busan.domain.place.repository
import busanVibe.busan.domain.place.domain.Place
import busanVibe.busan.domain.place.domain.PlaceLike
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param

interface PlaceLikeRepository: JpaRepository<PlaceLike, Long> {
fun findAllByPlaceIn(placeList: List<Place>): List<PlaceLike>
fun findByPlace(place: Place): List<PlaceLike>

@Query("SELECT pl FROM PlaceLike pl WHERE pl.place IN :places")
fun findLikeByPlace(@Param("places") placeList: List<Place>): List<PlaceLike>

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package busanVibe.busan.domain.place.repository

import busanVibe.busan.domain.place.domain.Place
import busanVibe.busan.domain.place.enums.PlaceType
import busanVibe.busan.domain.user.data.User
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 @@ -102,4 +103,28 @@ interface PlaceRepository: JpaRepository<Place, Long> {
)
fun findAllWithFetch(): List<Place>

@Query(
"""
SELECT DISTINCT p FROM Place p
LEFT JOIN FETCH p.placeLikes pl
LEFT JOIN FETCH p.placeImages
LEFT JOIN FETCH p.openTime
LEFT JOIN FETCH pl.user
WHERE pl.user = :user
"""
)
fun findLikePlace(@Param("user") user: User): List<Place>

@Query(
"""
SELECT DISTINCT p FROM Place p
LEFT JOIN FETCH p.placeLikes pl
LEFT JOIN FETCH p.placeImages
LEFT JOIN FETCH p.openTime
LEFT JOIN FETCH pl.user
WHERE p.type = :type AND pl.user = :user
"""
)
fun findLikePlaceByType(@Param("type") type: PlaceType, @Param("user") user: User): List<Place>

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import busanVibe.busan.domain.place.util.PlaceRedisUtil
import busanVibe.busan.domain.review.domain.Review
import busanVibe.busan.domain.review.domain.repository.ReviewRepository
import busanVibe.busan.global.apiPayload.code.status.ErrorStatus
import busanVibe.busan.global.apiPayload.exception.GeneralException
import busanVibe.busan.global.apiPayload.exception.handler.ExceptionHandler
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
Expand All @@ -32,18 +33,33 @@ class PlaceCongestionQueryService(

private val log = LoggerFactory.getLogger(PlaceCongestionQueryService::class.java)

/**
* 두 좌표를 받아 계산 및 조회 후 반환
* 좌측상단 좌표 ( lat1, ln1 ), 우측하단 좌표 ( lat2, lng2 )
* lat1 > lat2
* lng1 < lng2
*/
@Transactional(readOnly = true)
fun getMap(type: PlaceType?, latitude: Double, longitude: Double): PlaceMapResponseDTO.MapListDto{
fun getMap(type: PlaceType?, lat1: Double, lng1: Double, lat2: Double, lng2: Double): PlaceMapResponseDTO.MapListDto{

// Place -> name, type, latitude, longitude
// Redis -> congestion level

if (lat1 !in -90.0..90.0 || lat2 !in -90.0..90.0)
throw GeneralException(ErrorStatus.MAP_LATITUDE_OUT_OF_RANGE)

if (lng1 !in -180.0..180.0 || lng2 !in -180.0..180.0)
throw GeneralException(ErrorStatus.MAP_LONGITUDE_OUT_OF_RANGE)

if (lat1 <= lat2 || lng1 >= lng2)
throw GeneralException(ErrorStatus.MAP_INVALID_COORDINATE_ORDER)

// Place 목록 조회
val placeList = placeRepository.findPlacesByLocationAndType(
BigDecimal(latitude - latitudeRange).setScale(6, RoundingMode.HALF_UP),
BigDecimal(latitude + latitudeRange).setScale(6, RoundingMode.HALF_UP),
BigDecimal(longitude - longitudeRange).setScale(6, RoundingMode.HALF_UP),
BigDecimal(longitude + longitudeRange).setScale(6, RoundingMode.HALF_UP),
BigDecimal(lat2).setScale(6, RoundingMode.HALF_UP),
BigDecimal(lat1).setScale(6, RoundingMode.HALF_UP),
BigDecimal(lng1).setScale(6, RoundingMode.HALF_UP),
BigDecimal(lng2).setScale(6, RoundingMode.HALF_UP),
type
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class SearchResultDTO {

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class ListDto(
val sort: String,
val resultList: List<InfoDto>
)

Expand All @@ -26,7 +27,8 @@ class SearchResultDTO {
val startDate: String? = null,
val endDate: String? = null,
@get:JsonProperty("is_end")
val isEnd: Boolean?
val isEnd: Boolean?,
val likeCount: Int = 0
)

}
Loading