From a77c0aa5c9f394c4e61310d7f98cd453a61e72db Mon Sep 17 00:00:00 2001 From: junyong Date: Wed, 10 Sep 2025 03:54:41 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EB=AA=85=EC=86=8C=20=ED=98=BC?= =?UTF-8?q?=EC=9E=A1=EB=8F=84=20=EC=A0=95=EB=B3=B4=20=EC=9A=94=EC=9D=BC/?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EB=B3=84=EB=A1=9C=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=20=20-=20=EC=9D=91=EB=8B=B5=EA=B0=92=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20=EA=B8=B0=EC=A4=80=20=EC=9A=94=EC=9D=BC,=20?= =?UTF-8?q?=EC=9A=94=EC=9D=BC=EB=B3=84=20=ED=98=BC=EC=9E=A1=EB=8F=84,=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=EB=8C=80=EB=B3=84=20=ED=98=BC=EC=9E=A1?= =?UTF-8?q?=EB=8F=84=20=EC=B6=94=EA=B0=80=20=20=20-=20=ED=98=BC=EC=9E=A1?= =?UTF-8?q?=EB=8F=84=20endpoint=20=EB=B3=80=EA=B2=BD=20=20=20-=20redis?= =?UTF-8?q?=EC=97=90=20=EC=A0=80=EC=9E=A5=ED=95=A0=20key=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20-=20=EC=9A=94=EC=9D=BC,=EC=8B=9C=EA=B0=84=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=91=9C=EC=8B=9C=ED=95=98=EB=8F=84=EB=A1=9D=20=20?= =?UTF-8?q?=20-=20=EC=9A=94=EC=9D=BC,=20=EC=8B=9C=EA=B0=84=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=98=BC=EC=9E=A1=EB=8F=84=20=EA=B5=AC=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=20=20-=20redis=20key=20=EA=B5=AC=ED=95=98=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20extract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PlaceCongestionController.kt | 19 ++++++- .../domain/place/dto/PlaceMapResponseDTO.kt | 4 +- .../service/PlaceCongestionQueryService.kt | 42 ++++++++------ .../busan/domain/place/util/PlaceRedisUtil.kt | 55 ++++++++++--------- 4 files changed, 75 insertions(+), 45 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 cb1df41..ecb4690 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/controller/PlaceCongestionController.kt @@ -50,8 +50,23 @@ class PlaceCongestionController ( return ApiResponse.onSuccess(place) } - @GetMapping("/place/{placeId}/real-time") - @Operation(summary = "명소 실시간 혼잡도 조회") + @GetMapping("/place/{placeId}/congestions") + @Operation(summary = "명소 혼잡도 조회", + description = + """ + * congestions_by_day : 각 요일별 현재시간 기준 혼잡도 + - index [ 0:월, 1:화 ... 5:토, 6:일 ] + + * congestions_by_time : 각 요일별 시간별 혼잡도 ( 0~23시 제공 ) + - 명소의 모든 요일의 혼잡도 제공 + - 총 7개의 배열, 각 배열에는 24개의 혼잡도 정보 담김 + + * standard_day와 standard_time 활용해서 현재시간 판단 가능합니다. + 이거도 마찬가지로 0:월 ~ 6:일 + + * 현재(실시간) 혼잡도는 real_time_congestion_level 이용하면 됩니다. + """ + ) fun placeRealTimeCongestion( @PathVariable("placeId") placeId: Long): ApiResponse{ val congestion = placeCongestionQueryService.getCongestion(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 940f0d9..330c631 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/dto/PlaceMapResponseDTO.kt @@ -39,9 +39,11 @@ class PlaceMapResponseDTO { @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) data class PlaceCongestionDto( + val standardDay: Int, val standardTime: Int, val realTimeCongestionLevel: Int, - val byTimePercent: List + val congestionsByDay: List, + val congestionsByTime: List> ) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class) 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 09f43be..7ad7234 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/service/PlaceCongestionQueryService.kt @@ -119,28 +119,36 @@ 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 = (-3 .. 3).map { i -> (roundedBase - i * 3 + 24) % 24 } + // 현재시간 기준 LocalDateTime + val currentDateTime = LocalDateTime.now() + + // 혼잡도 리스트 + val congestionsByDay: MutableList = mutableListOf() // 요일별 + val congestionsByTime: MutableList> = mutableListOf() // 시간대별 + + // 시간대별 혼잡도 ( 월~일, 0~23시 ) + for( day in 1..7){ + val congestions = mutableListOf() // 각 요일의 혼잡도 정보 담을 List + for( hour in 0..23){ + congestions.add(placeRedisUtil.getTimeCongestion(placeId, day, hour)) + } + congestionsByTime.add(congestions) + } - 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) + // 요일별 현재 시간의 혼잡도 + for( day in 1..7){ + congestionsByDay.add(placeRedisUtil.getTimeCongestion(placeId, day, currentDateTime.hour)) } + // DTO 생성 및 반환 return PlaceMapResponseDTO.PlaceCongestionDto( - standardTime = roundedBase, - realTimeCongestionLevel = placeRedisUtil.getTimeCongestion(placeId, current).toInt(), - byTimePercent = byTimePercent + standardDay = currentDateTime.dayOfWeek.value-1, // 원래 1:월~7:일인데, 0:월~6일로 변경해서 반환 + standardTime = currentDateTime.hour, // 0~23 + realTimeCongestionLevel = placeRedisUtil.getTimeCongestion(placeId).toInt(), + congestionsByDay = congestionsByDay, + congestionsByTime = congestionsByTime ) + } @Transactional(readOnly = false) diff --git a/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt b/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt index 04aee1b..912506b 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt @@ -13,46 +13,49 @@ class PlaceRedisUtil( private val log = LoggerFactory.getLogger("busanVibe.busan.domain.place") - // 임의로 혼잡도 생성하여 반환. 레디스 키 값으로 저장함. -// fun getRedisCongestion(placeId: Long?): Int{ -// -// val key = "place:congestion:$placeId" -// val randomCongestion = getRandomCongestion().toInt().toString() -// -// redisTemplate.opsForValue() -// .set(key, randomCongestion) -// -// return Integer.parseInt(randomCongestion) -// } + // 현재 시간 기준 혼잡도 구하는 메서드 + // 현재에 해당하는 요일과 시간의 혼잡도 반환 + fun getTimeCongestion(placeId: Long?):Float{ + return getTimeCongestion(placeId, LocalDateTime.now()) + } + + // [요일, 시간] 으로 혼잡도 구하는 메서드 + // day: 1~7 + fun getTimeCongestion(placeId: Long?, day: Int, hour: Int): Float{ + val now = LocalDateTime.now() + return getTimeCongestion( + placeId, + LocalDateTime.of( + now.year, + now.month, + day, + hour, + now.minute + ) + ) + } // 지정 시간 혼잡도 조회 - // null이면 현재시간 기준 fun getTimeCongestion(placeId: Long?, dateTime: LocalDateTime?): Float { + // DateTime val dateTime = dateTime ?: LocalDateTime.now() - val roundedHour = (dateTime.hour / 3) * 3 - val key = "place:congestion:$placeId-${dateTime.year}-${dateTime.monthValue}-${dateTime.dayOfMonth}-$roundedHour" - + val key = getCongestionRedisKey(placeId, dateTime) val value = redisTemplate.opsForValue().get(key) return (if (value != null) { value.toFloatOrNull() ?: 0 } else { - setPlaceTimeCongestion(placeId, dateTime.withHour(roundedHour)) + setPlaceTimeCongestion(placeId, dateTime) val newValue = redisTemplate.opsForValue().get(key) newValue?.toFloatOrNull() ?: 0 }) as Float } - fun getTimeCongestion(placeId: Long?):Float{ - return getTimeCongestion(placeId, LocalDateTime.now()) - } - // 시간 혼잡도 설정 private 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 key = getCongestionRedisKey(placeId, dateTime) val congestion = getRandomCongestion().toString() val success = redisTemplate.opsForValue().setIfAbsent(key, congestion, Duration.ofHours(24)) @@ -65,9 +68,11 @@ class PlaceRedisUtil( } // 혼잡도 생성 (1.0 ~ 5.0 사이의 Float) - private fun getRandomCongestion(): Float { - return (Math.random() * 4 + 1).toFloat() - } + private fun getRandomCongestion(): Float = (Math.random() * 4 + 1).toFloat() + + // redis에 저장할 key 생성 + private fun getCongestionRedisKey(placeId: Long?, dateTime: LocalDateTime): String + = "place:congestion:${placeId}-${dateTime.dayOfWeek}-${dateTime.hour}" } \ No newline at end of file From e31b1ed45effc148677a4e1b21b65318d407584e Mon Sep 17 00:00:00 2001 From: junyong Date: Wed, 10 Sep 2025 04:22:51 +0900 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20=ED=98=BC=EC=9E=A1=EB=8F=84=20?= =?UTF-8?q?=EA=B5=AC=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EB=B3=80=EA=B2=BD=20&=20return=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=BA=90=EC=8A=A4=ED=8C=85=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../busan/domain/place/util/PlaceRedisUtil.kt | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt b/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt index 912506b..b032596 100644 --- a/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt +++ b/src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.kt @@ -19,8 +19,12 @@ class PlaceRedisUtil( return getTimeCongestion(placeId, LocalDateTime.now()) } - // [요일, 시간] 으로 혼잡도 구하는 메서드 - // day: 1~7 + /** + * [요일(1=Mon..7=Sun), 시간]으로 혼잡도 구하는 메서드 + * @param placeId 조회 대상 장소 ID + * @param dayOfWeek 1..7 (1=월요일, 7=일요일) + * @param hour 0..23 + */ fun getTimeCongestion(placeId: Long?, day: Int, hour: Int): Float{ val now = LocalDateTime.now() return getTimeCongestion( @@ -44,13 +48,15 @@ class PlaceRedisUtil( val key = getCongestionRedisKey(placeId, dateTime) val value = redisTemplate.opsForValue().get(key) - return (if (value != null) { - value.toFloatOrNull() ?: 0 - } else { - setPlaceTimeCongestion(placeId, dateTime) - val newValue = redisTemplate.opsForValue().get(key) - newValue?.toFloatOrNull() ?: 0 - }) as Float + val parsed = value?.toFloatOrNull() + + // 값이 존재하교 유효하다면 + if (parsed != null)return parsed // 그대로 반환 + + // 값이 없다면 + setPlaceTimeCongestion(placeId, dateTime) // 새로 만들어서 할당 + val newValue = redisTemplate.opsForValue().get(key)?.toFloatOrNull() // 새로 설정한 값 조회 + return newValue ?: 0f // 반환, 값이 이상하다면 0 반환 } // 시간 혼잡도 설정