From ffe3ff81cbfb9c7977ccf5d6b3c67048cd144aaa Mon Sep 17 00:00:00 2001 From: junyong Date: Wed, 30 Jul 2025 02:45:04 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=EC=A7=80=EB=8F=84=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PlaceCongestionController.kt | 12 +++-- .../domain/place/dto/PlaceMapResponseDTO.kt | 13 ++--- .../place/repository/PlaceRepository.kt | 19 +++++++ .../service/PlaceCongestionQueryService.kt | 50 +++++++++++++++++++ .../domain/place/service/PlaceRedisUtil.kt | 3 ++ 5 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt diff --git a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt index d735325..7b89f7c 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt @@ -2,6 +2,7 @@ package busanVibe.busan.domain.place.controller import busanVibe.busan.domain.place.dto.PlaceMapResponseDTO import busanVibe.busan.domain.place.enums.PlaceType +import busanVibe.busan.domain.place.service.PlaceCongestionQueryService import busanVibe.busan.global.apiPayload.exception.ApiResponse import io.swagger.v3.oas.annotations.Operation import org.springframework.web.bind.annotation.GetMapping @@ -12,16 +13,19 @@ import org.springframework.web.bind.annotation.RestController @RestController @RequestMapping("/api/congestion") -class PlaceCongestionController { +class PlaceCongestionController ( + private val placeCongestionQueryService: PlaceCongestionQueryService, +){ @GetMapping @Operation(summary = "지도 조회") fun map( - @RequestParam("type", required = false) type: PlaceType, + @RequestParam("type", required = false, defaultValue = "ALL") type: PlaceType, @RequestParam("latitude")latitude: Double, - @RequestParam("longtitude")longtitude: Double): ApiResponse?{ + @RequestParam("longitude")longtitude: Double): ApiResponse?{ - return null; + val places = placeCongestionQueryService.getMap(type, latitude, longtitude) + return ApiResponse.onSuccess(places); } @GetMapping("/place/{placeId}") diff --git a/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt b/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt index 01cb9d6..74cedae 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt @@ -3,6 +3,7 @@ package busanVibe.busan.domain.place.dto import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.databind.annotation.JsonNaming +import java.math.BigDecimal class PlaceMapResponseDTO { @@ -14,12 +15,12 @@ class PlaceMapResponseDTO { @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) data class PlaceMapInfoDto( - val placeId: Long, + val placeId: Long?, val name: String, val type: String, - val congestionLevel: Integer, - val latitude: Double, - val longitude: Double + val congestionLevel: Int, + val latitude: BigDecimal, + val longitude: BigDecimal ) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) @@ -29,8 +30,8 @@ class PlaceMapResponseDTO { val congestionLevel: Integer, val grade: Float, val reviewAmount: Integer, - val latitude: Double, - val longitude: Double, + val latitude: BigDecimal, + val longitude: BigDecimal, val address: String, @get:JsonProperty("is_open") val isOpen: Boolean, diff --git a/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt b/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt index 38e6a54..580d664 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt @@ -5,6 +5,7 @@ import busanVibe.busan.domain.place.enums.PlaceType import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.query.Param +import java.math.BigDecimal interface PlaceRepository: JpaRepository { @@ -28,4 +29,22 @@ interface PlaceRepository: JpaRepository { ) fun findByIdWithLIkeAndImage(@Param("placeId") placeId: Long): Place? + // ALL 이면 검사 안 함 + @Query( + """ + SELECT p FROM Place p + LEFT JOIN FETCH p.openTime + WHERE p.latitude BETWEEN :minLat AND :maxLat + AND p.longitude BETWEEN :minLng AND :maxLng + AND (:#{#type.name() == 'ALL'} = true OR p.type = :type) + """ + ) + fun findPlacesByLocationAndType( + @Param("minLat") minLat: BigDecimal, + @Param("maxLat") maxLat: BigDecimal, + @Param("minLng") minLng: BigDecimal, + @Param("maxLng") maxLng: BigDecimal, + @Param("type") type: PlaceType? + ): List + } \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt new file mode 100644 index 0000000..1e0df3e --- /dev/null +++ b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt @@ -0,0 +1,50 @@ +package busanVibe.busan.domain.place.service + +import busanVibe.busan.domain.place.dto.PlaceMapResponseDTO +import busanVibe.busan.domain.place.enums.PlaceType +import busanVibe.busan.domain.place.repository.PlaceRepository +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import java.math.BigDecimal +import java.math.RoundingMode + +@Service +class PlaceCongestionQueryService( + private val placeRepository: PlaceRepository, + private val placeRedisUtil: PlaceRedisUtil +) { + + private val latitudeRange: Double = 0.05 + private val longitudeRange: Double = 0.05 + + @Transactional(readOnly = true) + fun getMap(type: PlaceType?, latitude: Double, longitude: Double): PlaceMapResponseDTO.MapListDto{ + + // Place -> name, type, latitude, longitude + // Redis -> congestion level + + // 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), + type + ) + + // DTO 변환 + val placeDtoList :List = placeList.map { + PlaceMapResponseDTO.PlaceMapInfoDto( + placeId = it.id, + name = it.name, + type = it.type.capitalEnglish, + latitude = it.latitude, + longitude = it.longitude, + congestionLevel = placeRedisUtil.getRedisCongestion(it.id) + ) + } + + return PlaceMapResponseDTO.MapListDto(placeDtoList) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt index 567aa1d..4ec9ea3 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt @@ -1,7 +1,10 @@ package busanVibe.busan.domain.place.service import org.springframework.data.redis.core.StringRedisTemplate +import org.springframework.stereotype.Component + +@Component class PlaceRedisUtil( private val redisTemplate: StringRedisTemplate ) { From 4bb86aec33fa12cb8a41456c46c8f420fdc338a9 Mon Sep 17 00:00:00 2001 From: junyong Date: Wed, 30 Jul 2025 03:34:13 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=EB=AA=85=EC=86=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C(=ED=98=BC?= =?UTF-8?q?=EC=9E=A1=EB=8F=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PlaceCongestionController.kt | 8 ++-- .../busan/domain/place/domain/Place.kt | 5 +- .../domain/place/dto/PlaceMapResponseDTO.kt | 6 +-- .../place/repository/PlaceRepository.kt | 10 ++++ .../service/PlaceCongestionQueryService.kt | 46 ++++++++++++++++++- .../domain/repository/ReviewRepository.kt | 4 ++ 6 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt index 7b89f7c..fb9d78e 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt @@ -22,7 +22,7 @@ class PlaceCongestionController ( fun map( @RequestParam("type", required = false, defaultValue = "ALL") type: PlaceType, @RequestParam("latitude")latitude: Double, - @RequestParam("longitude")longtitude: Double): ApiResponse?{ + @RequestParam("longitude")longtitude: Double): ApiResponse{ val places = placeCongestionQueryService.getMap(type, latitude, longtitude) return ApiResponse.onSuccess(places); @@ -30,8 +30,10 @@ class PlaceCongestionController ( @GetMapping("/place/{placeId}") @Operation(summary = "명소 기본 정보 조회") - fun placeDefaultInfo(@PathVariable("placeId") placeId: Long): ApiResponse?{ - return null; + fun placeDefaultInfo(@PathVariable("placeId") placeId: Long): ApiResponse{ + + val place = placeCongestionQueryService.getPlaceDefault(placeId) + return ApiResponse.onSuccess(place) } @GetMapping("/place/{placeId}/read-time") diff --git a/src/main/kotlin/busanVibe/busan/domain/place/domain/Place.kt b/src/main/kotlin/busanVibe/busan/domain/place/domain/Place.kt index b90c860..5f9efcc 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/domain/Place.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/domain/Place.kt @@ -2,6 +2,7 @@ package busanVibe.busan.domain.place.domain import busanVibe.busan.domain.common.BaseEntity import busanVibe.busan.domain.place.enums.PlaceType +import busanVibe.busan.domain.review.domain.Review import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.EnumType @@ -46,8 +47,8 @@ class Place( // @JoinColumn(name = "region_id", nullable = false) // val region: Region, // -// @OneToMany(mappedBy = "place", fetch = FetchType.LAZY) -// val reviews: List = emptyList(), + @OneToMany(mappedBy = "place", fetch = FetchType.LAZY) + val reviews: List, @OneToMany(mappedBy = "place", fetch = FetchType.LAZY) val placeLikes: Set, diff --git a/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt b/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt index 74cedae..051ace3 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt @@ -25,11 +25,11 @@ class PlaceMapResponseDTO { @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) data class PlaceDefaultInfoDto( - val id: Long, + val id: Long?, val name: String, - val congestionLevel: Integer, + val congestionLevel: Int, val grade: Float, - val reviewAmount: Integer, + val reviewAmount: Int, val latitude: BigDecimal, val longitude: BigDecimal, val address: String, diff --git a/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt b/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt index 580d664..7d5acfe 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/repository/PlaceRepository.kt @@ -47,4 +47,14 @@ interface PlaceRepository: JpaRepository { @Param("type") type: PlaceType? ): List + @Query( + """ + SELECT p FROM Place p + LEFT JOIN FETCH p.openTime + LEFT JOIN FETCH p.placeImages + WHERE p.id = :placeId + """ + ) + fun findByIdWithReviewAndImage(@Param("placeId") placeId: Long): Place? + } \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt index 1e0df3e..27ac049 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt @@ -1,8 +1,14 @@ package busanVibe.busan.domain.place.service +import busanVibe.busan.domain.place.domain.Place +import busanVibe.busan.domain.place.domain.PlaceImage import busanVibe.busan.domain.place.dto.PlaceMapResponseDTO import busanVibe.busan.domain.place.enums.PlaceType import busanVibe.busan.domain.place.repository.PlaceRepository +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.handler.ExceptionHandler import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal @@ -11,7 +17,8 @@ import java.math.RoundingMode @Service class PlaceCongestionQueryService( private val placeRepository: PlaceRepository, - private val placeRedisUtil: PlaceRedisUtil + private val placeRedisUtil: PlaceRedisUtil, + private val reviewRepository: ReviewRepository, ) { private val latitudeRange: Double = 0.05 @@ -47,4 +54,41 @@ class PlaceCongestionQueryService( return PlaceMapResponseDTO.MapListDto(placeDtoList) } + @Transactional(readOnly = true) + fun getPlaceDefault(placeId: Long): PlaceMapResponseDTO.PlaceDefaultInfoDto{ + + // Place -> name, imageList, address, openTime + // Review -> grade, count + // Image -> list + // Redis -> congestion + + // 명소 조회 + val place: Place? = placeRepository.findByIdWithReviewAndImage(placeId) + place?: throw ExceptionHandler(ErrorStatus.PLACE_NOT_FOUND) + + // 이미지 조회 + val placeImageSet: Set = place.placeImages + val placeImageList = placeImageSet.toList() + .sortedBy { it.createdAt } + .map { it.imgUrl } + + // 리뷰 조회 + val reviewSet: Set = reviewRepository.findByPlace(place).toSet() + val grade = reviewSet.map { it.score }.average().toFloat() + + + return PlaceMapResponseDTO.PlaceDefaultInfoDto( + id = place.id, + name = place.name, + congestionLevel = placeRedisUtil.getRedisCongestion(place.id), + grade = grade, + reviewAmount = reviewSet.size, + latitude = place.latitude, + longitude = place.longitude, + address = place.address, + isOpen = true, + imgList = placeImageList + ) + } + } \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/domain/review/domain/repository/ReviewRepository.kt b/src/main/kotlin/busanVibe/busan/domain/review/domain/repository/ReviewRepository.kt index 551e871..a559bab 100644 --- a/src/main/kotlin/busanVibe/busan/domain/review/domain/repository/ReviewRepository.kt +++ b/src/main/kotlin/busanVibe/busan/domain/review/domain/repository/ReviewRepository.kt @@ -2,6 +2,7 @@ package busanVibe.busan.domain.review.domain.repository import busanVibe.busan.domain.place.domain.Place import busanVibe.busan.domain.review.domain.Review +import org.springframework.data.jpa.repository.EntityGraph import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.data.repository.query.Param @@ -17,4 +18,7 @@ interface ReviewRepository: JpaRepository { ) fun findForDetails(@Param("place")place: Place): List + @EntityGraph(attributePaths = ["user"]) + fun findByPlace(place: Place): List + } \ No newline at end of file From 19e78251a3d348f77ac488c3b62c51d34dfa5a36 Mon Sep 17 00:00:00 2001 From: junyong Date: Wed, 30 Jul 2025 04:52:51 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=EB=AA=85=EC=86=8C=20=EC=8B=A4?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=ED=98=BC=EC=9E=A1=EB=8F=84=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PlaceCongestionController.kt | 8 +-- .../domain/place/dto/PlaceMapResponseDTO.kt | 4 +- .../service/PlaceCongestionQueryService.kt | 37 ++++++++++++++ .../domain/place/service/PlaceRedisUtil.kt | 49 +++++++++++++++++-- 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt index fb9d78e..309af53 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt @@ -36,12 +36,12 @@ class PlaceCongestionController ( return ApiResponse.onSuccess(place) } - @GetMapping("/place/{placeId}/read-time") + @GetMapping("/place/{placeId}/real-time") @Operation(summary = "명소 실시간 혼잡도 조회") fun placeRealTimeCongestion( - @PathVariable("placeId") placeId: Long, - @RequestParam("standard-time") standardTime: Integer): ApiResponse?{ - return null; + @PathVariable("placeId") placeId: Long): ApiResponse{ + val congestion = placeCongestionQueryService.getCongestion(placeId) + return ApiResponse.onSuccess(congestion) } @GetMapping("/place/{placeId}/distribution") diff --git a/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt b/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt index 051ace3..e620e81 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt @@ -40,8 +40,8 @@ class PlaceMapResponseDTO { @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) data class PlaceCongestionDto( - val standardTime: String, - val realTimeCongestionLevel: Integer, + val standardTime: Int, + val realTimeCongestionLevel: Int, val byTimePercent: List ) diff --git a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt index 27ac049..2930279 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt @@ -9,10 +9,12 @@ 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.handler.ExceptionHandler +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import java.math.BigDecimal import java.math.RoundingMode +import java.time.LocalDateTime @Service class PlaceCongestionQueryService( @@ -24,6 +26,8 @@ class PlaceCongestionQueryService( private val latitudeRange: Double = 0.05 private val longitudeRange: Double = 0.05 + private val log = LoggerFactory.getLogger(PlaceCongestionQueryService::class.java) + @Transactional(readOnly = true) fun getMap(type: PlaceType?, latitude: Double, longitude: Double): PlaceMapResponseDTO.MapListDto{ @@ -91,4 +95,37 @@ class PlaceCongestionQueryService( ) } + @Transactional(readOnly = true) + fun getCongestion(placeId: Long): PlaceMapResponseDTO.PlaceCongestionDto { + + val current = LocalDateTime.now() + log.info("현재 시간: ${current}시") + + val roundedBase = (current.hour / 3) * 3 + + // 최근 6개 3시간 단위 시간 생성 (기준시간 포함 총 7개) + val hours = (6 downTo 0).map { i -> (roundedBase - i * 3 + 24) % 24 } + + log.info("배열 담기 시작") + + val byTimePercent: List = hours.map { hour -> + val adjustedDateTime = current.withHour(hour) + .withMinute(0).withSecond(0).withNano(0) + .let { + if (hour > current.hour) it.minusDays(1) else it + } + placeRedisUtil.getTimeCongestion(placeId, adjustedDateTime) + } + + log.info("배열 담기 끝") + + return PlaceMapResponseDTO.PlaceCongestionDto( + standardTime = roundedBase, + realTimeCongestionLevel = placeRedisUtil.getTimeCongestion(placeId, current).toInt(), + byTimePercent = byTimePercent + ) + } + + + } \ No newline at end of file diff --git a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt index 4ec9ea3..8a46fa0 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceRedisUtil.kt @@ -1,7 +1,10 @@ package busanVibe.busan.domain.place.service +import org.slf4j.LoggerFactory import org.springframework.data.redis.core.StringRedisTemplate import org.springframework.stereotype.Component +import java.time.Duration +import java.time.LocalDateTime @Component @@ -9,16 +12,56 @@ class PlaceRedisUtil( private val redisTemplate: StringRedisTemplate ) { + private val log = LoggerFactory.getLogger("busanVibe.busan.domain.place") + // 임의로 혼잡도 생성하여 반환. 레디스 키 값으로 저장함. fun getRedisCongestion(placeId: Long?): Int{ val key = "place:congestion:$placeId" - val randomCongestion: Int = (Math.random() * 5 + 1).toInt() + val randomCongestion = getRandomCongestion().toInt().toString() redisTemplate.opsForValue() - .set(key, randomCongestion.toString()) + .set(key, randomCongestion) + + return Integer.parseInt(randomCongestion) + } - return randomCongestion + // 시간 혼잡도 설정 + fun setPlaceTimeCongestion(placeId: Long, dateTime: LocalDateTime) { + val roundedHour = (dateTime.hour / 3) * 3 + val key = "place:congestion:$placeId-${dateTime.year}-${dateTime.monthValue}-${dateTime.dayOfMonth}-$roundedHour" + val congestion = getRandomCongestion().toString() + val success = redisTemplate.opsForValue().setIfAbsent(key, congestion, Duration.ofHours(24)) + + if (success == true) { + log.info("혼잡도 기록 저장 완료: $key, 저장된 혼잡도: $congestion") + } else { + val existing = redisTemplate.opsForValue().get(key) + log.info("이미 존재하는 혼잡도 기록: $key, 기존 혼잡도: $existing") + } } + // 지정 시간 혼잡도 조회 + fun getTimeCongestion(placeId: Long, dateTime: LocalDateTime): Float { + val roundedHour = (dateTime.hour / 3) * 3 + val key = "place:congestion:$placeId-${dateTime.year}-${dateTime.monthValue}-${dateTime.dayOfMonth}-$roundedHour" + + val value = redisTemplate.opsForValue().get(key) + + return if (value != null) { + log.info("이미 존재하는 혼잡도 기록: $key, 기존 혼잡도: $value") + value.toFloatOrNull() ?: 0f + } else { + setPlaceTimeCongestion(placeId, dateTime.withHour(roundedHour)) + val newValue = redisTemplate.opsForValue().get(key) + newValue?.toFloatOrNull() ?: 0f + } + } + + // 혼잡도 생성 (1.0 ~ 5.0 사이의 Float) + private fun getRandomCongestion(): Float { + return (Math.random() * 4 + 1).toFloat() + } + + } \ No newline at end of file From 562b97aa68eb8e9400d50bd026e52ce6e74fc5c6 Mon Sep 17 00:00:00 2001 From: junyong Date: Wed, 30 Jul 2025 05:06:04 +0900 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=B3=84=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../busan/domain/festival/controller/FestivalController.kt | 2 ++ .../busan/domain/place/controller/PlaceCongestionController.kt | 2 ++ .../busanVibe/busan/domain/place/controller/PlaceController.kt | 2 ++ .../busanVibe/busan/domain/user/controller/UserController.kt | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/main/kotlin/busanVibe/busan/domain/festival/controller/FestivalController.kt b/src/main/kotlin/busanVibe/busan/domain/festival/controller/FestivalController.kt index c4cb7a7..e7d0e4b 100644 --- a/src/main/kotlin/busanVibe/busan/domain/festival/controller/FestivalController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/festival/controller/FestivalController.kt @@ -7,12 +7,14 @@ import busanVibe.busan.domain.festival.enums.FestivalStatus import busanVibe.busan.domain.festival.service.FestivalQueryService import busanVibe.busan.global.apiPayload.exception.ApiResponse import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +@Tag(name = "축제 관련 API") @RestController @RequestMapping("/api/festivals") class FestivalController( diff --git a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt index 309af53..d44e3c2 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt @@ -5,12 +5,14 @@ import busanVibe.busan.domain.place.enums.PlaceType import busanVibe.busan.domain.place.service.PlaceCongestionQueryService import busanVibe.busan.global.apiPayload.exception.ApiResponse import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +@Tag(name = "혼잡도+지도 관련 API") @RestController @RequestMapping("/api/congestion") class PlaceCongestionController ( diff --git a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceController.kt b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceController.kt index 12966f0..0696764 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceController.kt @@ -6,12 +6,14 @@ import busanVibe.busan.domain.place.enums.PlaceSortType import busanVibe.busan.domain.place.service.PlaceQueryService import busanVibe.busan.global.apiPayload.exception.ApiResponse import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +@Tag(name = "명소 관련 API") @RestController @RequestMapping("/api/places") class PlaceController( diff --git a/src/main/kotlin/busanVibe/busan/domain/user/controller/UserController.kt b/src/main/kotlin/busanVibe/busan/domain/user/controller/UserController.kt index 417de35..9748412 100644 --- a/src/main/kotlin/busanVibe/busan/domain/user/controller/UserController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/user/controller/UserController.kt @@ -3,6 +3,7 @@ package busanVibe.busan.domain.user.controller import busanVibe.busan.domain.user.data.dto.UserResponseDTO import busanVibe.busan.domain.user.service.UserCommandService import busanVibe.busan.global.apiPayload.exception.ApiResponse +import io.swagger.v3.oas.annotations.tags.Tag import org.slf4j.LoggerFactory import org.springframework.http.HttpHeaders import org.springframework.http.ResponseEntity @@ -11,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController +@Tag(name = "유저 관련 API", description = "로그인과 분리할수도 있습니다") @RestController @RequestMapping("/users") class UserController ( From 0c2f2786f8759b1687264cf6685241c33b926ba1 Mon Sep 17 00:00:00 2001 From: Junyong <93406666+ggamnunq@users.noreply.github.com> Date: Wed, 30 Jul 2025 05:10:48 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=EC=A0=90=EC=88=98=20NaN=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../busan/domain/place/service/PlaceCongestionQueryService.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt index 2930279..827c96b 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt @@ -78,8 +78,8 @@ class PlaceCongestionQueryService( // 리뷰 조회 val reviewSet: Set = reviewRepository.findByPlace(place).toSet() - val grade = reviewSet.map { it.score }.average().toFloat() - + val grade = if (reviewSet.isEmpty()) 0f + else reviewSet.map { it.score }.average().toFloat() return PlaceMapResponseDTO.PlaceDefaultInfoDto( id = place.id,