-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 명소 혼잡도 정보 요일/시간별로 조회할 수 있도록 구현 #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<Float> = mutableListOf() // 요일별 | ||
| val congestionsByTime: MutableList<List<Float>> = mutableListOf() // 시간대별 | ||
|
|
||
| // 시간대별 혼잡도 ( 월~일, 0~23시 ) | ||
| for( day in 1..7){ | ||
| val congestions = mutableListOf<Float>() // 각 요일의 혼잡도 정보 담을 List | ||
| for( hour in 0..23){ | ||
| congestions.add(placeRedisUtil.getTimeCongestion(placeId, day, hour)) | ||
| } | ||
| congestionsByTime.add(congestions) | ||
| } | ||
|
|
||
| val byTimePercent: List<Float> = 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 | ||
|
Comment on lines
+145
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainClarify day indexing contract and align all arrays. standardDay is 0=월..6=일. Ensure congestionsByDay[standardDay] and congestionsByTime[standardDay][standardTime] follow the same zero-based mapping; verify PlaceRedisUtil(day, hour) expects 1..7 or 0..6 to avoid off-by-one. Run to confirm signatures and usages: 🏁 Script executed: #!/bin/bash
# Verify PlaceRedisUtil day/hour semantics and DTO usage
rg -nP -C2 'getTimeCongestion\s*\(.*day\s*:\s*Int.*hour\s*:\s*Int' --type=kt
rg -nP -C2 'getTimeCongestion\s*\(.*placeId' --type=kt
rg -nP -C2 'PlaceCongestionDto' --type=ktLength of output: 80 🏁 Script executed: #!/bin/bash
# Locate PlaceRedisUtil implementation and inspect getTimeCongestion signature
rg -l 'class PlaceRedisUtil' -g '*.kt'
rg -n 'class PlaceRedisUtil' -g '*.kt' -C3
rg -n 'fun getTimeCongestion' -g '*.kt' -C3
# Locate PlaceCongestionDto and its fields
rg -l 'data class PlaceCongestionDto' -g '*.kt'
rg -n 'PlaceCongestionDto' -g '*.kt' -C3Length of output: 6165 🏁 Script executed: #!/bin/bash
sed -n '1,80p' src/main/kotlin/busanVibe/busan/domain/place/util/PlaceRedisUtil.ktLength of output: 2445 🏁 Script executed: #!/bin/bash
rg -n 'congestionsByDay' -g '*.kt'
rg -n 'congestionsByTime' -g '*.kt'Length of output: 1174 Refactor getTimeCongestion(day,hour) to use true weekday semantics |
||
| ) | ||
|
|
||
| } | ||
|
|
||
| @Transactional(readOnly = false) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainBug: day parameter is treated as day-of-month, not day-of-week. LocalDateTime.of(now.year, now.month, day, ...) interprets day as day-of-month and will not map 0..6 (Mon..Sun). It can also throw for invalid days. Use day-of-week indexing (0=Mon..6=Sun) and validate ranges. Apply: - // [요일, 시간] 으로 혼잡도 구하는 메서드
- // 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
- )
- )
- }
+ // [요일(0=월..6=일), 시간] 으로 혼잡도 구하는 메서드
+ fun getTimeCongestion(placeId: Long?, day: Int, hour: Int): Float {
+ require(day in 0..6) { "day must be 0..6 (0=Mon, 6=Sun)" }
+ require(hour in 0..23) { "hour must be 0..23" }
+ val now = LocalDateTime.now()
+ val target = now
+ .with(java.time.temporal.ChronoField.DAY_OF_WEEK, (day + 1).toLong()) // ISO: Mon=1..Sun=7
+ .withHour(hour).withMinute(0).withSecond(0).withNano(0)
+ return getTimeCongestion(placeId, target)
+ }Run a quick grep to confirm all call sites pass day in 0..6: 🏁 Script executed: #!/bin/bash
rg -nP -C2 'getTimeCongestion\(\s*[^,]+,\s*[^,]+,\s*[^)]+\)' --type=kotlinLength of output: 1854 Fix getTimeCongestion to use day‐of‐week (1=Mon..7=Sun) instead of day‐of‐month - // [요일, 시간] 으로 혼잡도 구하는 메서드
- // 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
- )
- )
- }
+ /**
+ * [요일(1=Mon..7=Sun), 시간]으로 혼잡도 구하는 메서드
+ * @param placeId 조회 대상 장소 ID
+ * @param dayOfWeek 1..7 (1=월요일, 7=일요일)
+ * @param hour 0..23
+ */
+ fun getTimeCongestion(placeId: Long?, dayOfWeek: Int, hour: Int): Float {
+ require(dayOfWeek in 1..7) { "dayOfWeek must be 1..7 (1=Mon, 7=Sun)" }
+ require(hour in 0..23) { "hour must be 0..23" }
+ val now = LocalDateTime.now()
+ val target = now
+ .with(ChronoField.DAY_OF_WEEK, dayOfWeek.toLong())
+ .withHour(hour)
+ .withMinute(0)
+ .withSecond(0)
+ .withNano(0)
+ return getTimeCongestion(placeId, target)
+ }— Update call sites remain passing 1..7 and need no changes. 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 지정 시간 혼잡도 조회 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix timezone source: use KST or inject Clock.
LocalDateTime.now() depends on server default TZ; day/hour can drift if infra runs in UTC. Use Asia/Seoul or inject Clock for testability.
Alternative (preferred for tests):
🤖 Prompt for AI Agents