Skip to content

Commit 89414e1

Browse files
authored
Merge pull request #139 from Route-Box/feature/#93
Feat: 인기 루트 업데이트 스케줄러
2 parents 8d67c3a + a52744d commit 89414e1

11 files changed

Lines changed: 123 additions & 2 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.routebox.routebox.application.popular_route
2+
3+
import com.routebox.routebox.domain.popular_route.PopularRouteService
4+
import com.routebox.routebox.domain.purchased_route.PurchasedRouteService
5+
import org.springframework.stereotype.Component
6+
7+
@Component
8+
class UpdatePopularRouteUseCase(
9+
private val popularRouteService: PopularRouteService,
10+
private val purchasedRouteService: PurchasedRouteService,
11+
) {
12+
operator fun invoke(): Int {
13+
// 많이 담은 루트 조회
14+
val popularRoutes = purchasedRouteService.getPurchasedRouteCountByRoute()
15+
16+
// 인기 루트에 저장
17+
popularRouteService.createPopularRoutes(popularRoutes)
18+
return 0
19+
}
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.routebox.routebox.application.popular_route.dto
2+
3+
data class PopularRouteCountDto(
4+
val id: Long,
5+
val count: Long,
6+
)

src/main/kotlin/com/routebox/routebox/domain/popular_route/PopularRoute.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,28 @@ import jakarta.persistence.GeneratedValue
77
import jakarta.persistence.GenerationType
88
import jakarta.persistence.Id
99
import jakarta.persistence.Table
10+
import java.time.LocalDate
1011

1112
@Table(name = "popular_routes")
1213
@Entity
1314
class PopularRoute(
1415
id: Long = 0,
1516
routeId: Long,
17+
count: Long,
18+
date: LocalDate,
1619
) : TimeTrackedBaseEntity() {
1720

1821
@Id
1922
@GeneratedValue(strategy = GenerationType.IDENTITY)
2023
@Column(name = "popular_route_id")
2124
val id: Long = id
2225

26+
@Column
2327
val routeId: Long = routeId
28+
29+
@Column
30+
val count: Long = count
31+
32+
@Column
33+
val date: LocalDate = date
2434
}

src/main/kotlin/com/routebox/routebox/domain/popular_route/PopularRouteService.kt

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.routebox.routebox.domain.popular_route
22

3+
import com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto
34
import com.routebox.routebox.application.popular_route.dto.PopularRouteDto
45
import com.routebox.routebox.infrastructure.popular_route.PopularRouteRepository
56
import org.springframework.stereotype.Service
67
import org.springframework.transaction.annotation.Transactional
8+
import java.time.LocalDate
79

810
@Service
911
class PopularRouteService(
@@ -15,6 +17,24 @@ class PopularRouteService(
1517
*/
1618
@Transactional(readOnly = true)
1719
fun getPopularRoutes(): List<PopularRouteDto> {
18-
return popularRouteRepository.findAllPopularRoutes()
20+
// 가장 최근에 업데이트된 인기루트 조회
21+
return popularRouteRepository.findRecentPopularRoutes()
22+
}
23+
24+
/**
25+
* 인기 루트 추가
26+
*/
27+
@Transactional
28+
fun createPopularRoutes(routes: List<PopularRouteCountDto>) {
29+
val today = LocalDate.now()
30+
val popularRoutes = routes.map { route ->
31+
PopularRoute(
32+
routeId = route.id,
33+
count = route.count,
34+
date = today,
35+
)
36+
}
37+
38+
popularRouteRepository.saveAll(popularRoutes)
1939
}
2040
}

src/main/kotlin/com/routebox/routebox/domain/purchased_route/PurchasedRouteService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.routebox.routebox.domain.purchased_route
22

3+
import com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto
34
import com.routebox.routebox.exception.purchased_route.PurchasedRouteNotFoundException
45
import com.routebox.routebox.infrastructure.purchased_route.PurchasedRouteRepository
56
import org.springframework.data.domain.Page
@@ -27,4 +28,10 @@ class PurchasedRouteService(
2728
@Transactional(readOnly = true)
2829
fun getPurchasedRouteCount(buyerId: Long): Int =
2930
purchasedRouteRepository.countByBuyer_Id(buyerId)
31+
32+
@Transactional(readOnly = true)
33+
fun getPurchasedRouteCountByRoute(): List<PopularRouteCountDto> {
34+
val pageable = PageRequest.of(0, 5)
35+
return purchasedRouteRepository.findTop5PopularRoutes(pageable)
36+
}
3037
}

src/main/kotlin/com/routebox/routebox/infrastructure/popular_route/PopularRouteRepository.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ interface PopularRouteRepository : JpaRepository<PopularRoute, Long> {
1212
FROM PopularRoute pr
1313
JOIN Route r ON pr.routeId = r.id
1414
WHERE r.isPublic = true
15+
AND pr.date = (SELECT MAX(p.date) FROM PopularRoute p)
16+
ORDER BY pr.count DESC
1517
""",
1618
)
17-
fun findAllPopularRoutes(): List<PopularRouteDto>
19+
fun findRecentPopularRoutes(): List<PopularRouteDto>
1820
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
11
package com.routebox.routebox.infrastructure.purchased_route
22

3+
import com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto
34
import com.routebox.routebox.domain.purchased_route.PurchasedRoute
45
import org.springframework.data.domain.Page
56
import org.springframework.data.domain.Pageable
67
import org.springframework.data.jpa.repository.JpaRepository
8+
import org.springframework.data.jpa.repository.Query
79

810
@Suppress("ktlint:standard:function-naming")
911
interface PurchasedRouteRepository : JpaRepository<PurchasedRoute, Long> {
1012
fun findByBuyer_IdOrderByCreatedAtDesc(buyerId: Long, pageable: Pageable): Page<PurchasedRoute>
1113
fun countByBuyer_Id(buyerId: Long): Int
14+
15+
@Query(
16+
"""
17+
SELECT NEW com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto(
18+
r.id, COALESCE(COUNT(pr), 0)
19+
)
20+
FROM Route r
21+
LEFT JOIN PopularRoute pr ON r.id = pr.routeId
22+
GROUP BY r.id
23+
ORDER BY COUNT(pr) DESC
24+
""",
25+
)
26+
fun findTop5PopularRoutes(pageable: Pageable): List<PopularRouteCountDto>
1227
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.routebox.routebox.scheduler
2+
3+
import com.routebox.routebox.application.popular_route.UpdatePopularRouteUseCase
4+
import org.slf4j.LoggerFactory
5+
import org.springframework.scheduling.annotation.Scheduled
6+
import org.springframework.stereotype.Component
7+
8+
@Component
9+
class PopularRouteScheduler(
10+
private val updatePopularRouteUseCase: UpdatePopularRouteUseCase,
11+
) {
12+
13+
private val logger = LoggerFactory.getLogger(this::class.java)
14+
15+
/**
16+
* 매일 00:00:00에 실행
17+
*/
18+
@Scheduled(cron = "0 0 0 * * *")
19+
fun updatePopularRoute() {
20+
logger.info("Scheduler is running")
21+
updatePopularRouteUseCase()
22+
}
23+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.routebox.routebox.scheduler
2+
3+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
4+
import org.springframework.context.annotation.Configuration
5+
import org.springframework.scheduling.annotation.EnableScheduling
6+
7+
@Configuration
8+
@EnableScheduling
9+
@ConditionalOnProperty(
10+
name = ["routebox.scheduler.enabled"],
11+
havingValue = "true",
12+
matchIfMissing = true,
13+
)
14+
class SchedulerConfig

src/main/resources/application.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ routebox:
22
jwt:
33
secret-key: ${JWT_SECRET_KEY}
44
app-version: 0.0.1
5+
scheduler:
6+
enabled: true # 스케줄러 활성화 여부
57

68
spring:
79
application:

0 commit comments

Comments
 (0)