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
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.routebox.routebox.application.popular_route

import com.routebox.routebox.domain.popular_route.PopularRouteService
import com.routebox.routebox.domain.purchased_route.PurchasedRouteService
import org.springframework.stereotype.Component

@Component
class UpdatePopularRouteUseCase(
private val popularRouteService: PopularRouteService,
private val purchasedRouteService: PurchasedRouteService,
) {
operator fun invoke(): Int {
// 많이 담은 루트 조회
val popularRoutes = purchasedRouteService.getPurchasedRouteCountByRoute()

// 인기 루트에 저장
popularRouteService.createPopularRoutes(popularRoutes)
return 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.routebox.routebox.application.popular_route.dto

data class PopularRouteCountDto(
val id: Long,
val count: Long,
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,28 @@ import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table
import java.time.LocalDate

@Table(name = "popular_routes")
@Entity
class PopularRoute(
id: Long = 0,
routeId: Long,
count: Long,
date: LocalDate,
) : TimeTrackedBaseEntity() {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "popular_route_id")
val id: Long = id

@Column
val routeId: Long = routeId

@Column
val count: Long = count

@Column
val date: LocalDate = date
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.routebox.routebox.domain.popular_route

import com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto
import com.routebox.routebox.application.popular_route.dto.PopularRouteDto
import com.routebox.routebox.infrastructure.popular_route.PopularRouteRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate

@Service
class PopularRouteService(
Expand All @@ -15,6 +17,24 @@ class PopularRouteService(
*/
@Transactional(readOnly = true)
fun getPopularRoutes(): List<PopularRouteDto> {
return popularRouteRepository.findAllPopularRoutes()
// 가장 최근에 업데이트된 인기루트 조회
return popularRouteRepository.findRecentPopularRoutes()
}

/**
* 인기 루트 추가
*/
@Transactional
fun createPopularRoutes(routes: List<PopularRouteCountDto>) {
val today = LocalDate.now()
val popularRoutes = routes.map { route ->
PopularRoute(
routeId = route.id,
count = route.count,
date = today,
)
}

popularRouteRepository.saveAll(popularRoutes)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.routebox.routebox.domain.purchased_route

import com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto
import com.routebox.routebox.exception.purchased_route.PurchasedRouteNotFoundException
import com.routebox.routebox.infrastructure.purchased_route.PurchasedRouteRepository
import org.springframework.data.domain.Page
Expand Down Expand Up @@ -27,4 +28,10 @@ class PurchasedRouteService(
@Transactional(readOnly = true)
fun getPurchasedRouteCount(buyerId: Long): Int =
purchasedRouteRepository.countByBuyer_Id(buyerId)

@Transactional(readOnly = true)
fun getPurchasedRouteCountByRoute(): List<PopularRouteCountDto> {
val pageable = PageRequest.of(0, 5)
return purchasedRouteRepository.findTop5PopularRoutes(pageable)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ interface PopularRouteRepository : JpaRepository<PopularRoute, Long> {
FROM PopularRoute pr
JOIN Route r ON pr.routeId = r.id
WHERE r.isPublic = true
AND pr.date = (SELECT MAX(p.date) FROM PopularRoute p)
ORDER BY pr.count DESC
""",
)
fun findAllPopularRoutes(): List<PopularRouteDto>
fun findRecentPopularRoutes(): List<PopularRouteDto>
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
package com.routebox.routebox.infrastructure.purchased_route

import com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto
import com.routebox.routebox.domain.purchased_route.PurchasedRoute
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query

@Suppress("ktlint:standard:function-naming")
interface PurchasedRouteRepository : JpaRepository<PurchasedRoute, Long> {
fun findByBuyer_IdOrderByCreatedAtDesc(buyerId: Long, pageable: Pageable): Page<PurchasedRoute>
fun countByBuyer_Id(buyerId: Long): Int

@Query(
"""
SELECT NEW com.routebox.routebox.application.popular_route.dto.PopularRouteCountDto(
r.id, COALESCE(COUNT(pr), 0)
)
FROM Route r
LEFT JOIN PopularRoute pr ON r.id = pr.routeId
GROUP BY r.id
ORDER BY COUNT(pr) DESC
""",
)
fun findTop5PopularRoutes(pageable: Pageable): List<PopularRouteCountDto>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.routebox.routebox.scheduler

import com.routebox.routebox.application.popular_route.UpdatePopularRouteUseCase
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component

@Component
class PopularRouteScheduler(
private val updatePopularRouteUseCase: UpdatePopularRouteUseCase,
) {

private val logger = LoggerFactory.getLogger(this::class.java)

/**
* 매일 00:00:00에 실행
*/
@Scheduled(cron = "0 0 0 * * *")
fun updatePopularRoute() {
logger.info("Scheduler is running")
updatePopularRouteUseCase()
}
}
14 changes: 14 additions & 0 deletions src/main/kotlin/com/routebox/routebox/scheduler/SchedulerConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.routebox.routebox.scheduler

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.annotation.EnableScheduling

@Configuration
@EnableScheduling
@ConditionalOnProperty(
name = ["routebox.scheduler.enabled"],
havingValue = "true",
matchIfMissing = true,
)
class SchedulerConfig
2 changes: 2 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ routebox:
jwt:
secret-key: ${JWT_SECRET_KEY}
app-version: 0.0.1
scheduler:
enabled: true # 스케줄러 활성화 여부

spring:
application:
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ CREATE TABLE popular_routes
(
popular_route_id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '인기 루트 ID',
route_id BIGINT NOT NULL COMMENT '루트 ID',
count BIGINT NOT NULL,
date DATE NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 시간',
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '업데이트 시간'
);
Expand Down