Skip to content

Commit

Permalink
Merge pull request #70 from COW-dev/main
Browse files Browse the repository at this point in the history
  • Loading branch information
dojinyou authored Dec 5, 2023
2 parents f1c7be2 + bfe0390 commit 16098ac
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.mjucow.eatda.scheduled

import com.mjucow.eatda.jooq.tables.Banner.BANNER
import com.mjucow.eatda.jooq.tables.records.ExpiredBannerRecord
import org.jooq.DSLContext
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import org.springframework.transaction.support.TransactionTemplate
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId

@Component
class ExpiredBannerBatchJob(
private val db: DSLContext,
private val transactionTemplate: TransactionTemplate,
) {
@Scheduled(cron = "0 0 22 * * *", zone = "Asia/Seoul") // 매일 오후 10시 동작
fun scheduleTaskUsingCronExpression() {
val now = Instant.now()
transactionTemplate.execute {
val expiredBanners = findAllExpiredBanners(now)
if (expiredBanners.isEmpty()) {
return@execute
}

expiredBannerBulkInsert(expiredBanners)

bulkDeleteBanners(expiredBanners)
}
}

private fun findAllExpiredBanners(now: Instant): List<ExpiredTargetBanner> {
return db
.select(BANNER.ID, BANNER.LINK, BANNER.IMAGE_ADDRESS, BANNER.EXPIRED_AT)
.from(BANNER)
.where(
BANNER.EXPIRED_AT.isNotNull
.and(BANNER.EXPIRED_AT.lessOrEqual(LocalDateTime.ofInstant(now, ZONE_ID)))
)
.fetch()
.into(ExpiredTargetBanner::class.java)
.toList()
}

private fun expiredBannerBulkInsert(expireTargetBanners: List<ExpiredTargetBanner>) {
val expiredBanners = expireTargetBanners.map { banner ->
ExpiredBannerRecord().apply {
this.link = banner.link
this.imageAddress = banner.imageAddress
this.expiredAt = banner.expiredAt
}
}
db.batchInsert(expiredBanners).execute()
}

private fun bulkDeleteBanners(expiredBanners: List<ExpiredTargetBanner>) {
val deletedBannerIds = expiredBanners.map { it.id }
db.deleteFrom(BANNER).where(BANNER.ID.`in`(deletedBannerIds)).execute()
}

data class ExpiredTargetBanner(
val id: Long,
val link: String,
val imageAddress: String,
val expiredAt: LocalDateTime,
)

companion object {
val ZONE_ID: ZoneId = ZoneId.of("Asia/Seoul")
}
}
17 changes: 17 additions & 0 deletions src/main/kotlin/com/mjucow/eatda/scheduled/ScheduleJobConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.mjucow.eatda.scheduled

import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.annotation.EnableScheduling

@Configuration
@EnableScheduling
class ScheduleJobConfig {
// @Bean
// fun taskScheduler(): TaskScheduler {
// val threadPoolTaskScheduler = ThreadPoolTaskScheduler().apply {
// poolSize = 5
// setThreadNamePrefix("ThreadPoolTaskScheduler")
// }
// return threadPoolTaskScheduler
// }
}
8 changes: 8 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ spring:
generate-ddl: false
hibernate:
ddl-auto: validate
task:
scheduling:
pool:
size: 1

web.resources.add-mappings: false
liquibase:
Expand Down Expand Up @@ -56,6 +60,10 @@ spring:
generate-ddl: false
hibernate:
ddl-auto: none
task:
scheduling:
pool:
size: 5

datasource:
driver-class-name: org.postgresql.Driver
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.mjucow.eatda.scheduled

import com.mjucow.eatda.domain.AbstractDataTest
import com.mjucow.eatda.domain.banner.entity.objectmother.BannerMother
import com.mjucow.eatda.jooq.tables.ExpiredBanner.EXPIRED_BANNER
import com.mjucow.eatda.jooq.tables.records.BannerRecord
import com.mjucow.eatda.jooq.tables.records.ExpiredBannerRecord
import com.mjucow.eatda.persistence.banner.BannerRepository
import org.assertj.core.api.Assertions
import org.jooq.DSLContext
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Import
import java.time.LocalDateTime
import java.util.stream.IntStream

@Import(value = [ExpiredBannerBatchJob::class])
class ExpiredBannerBatchJobTest : AbstractDataTest() {
@Autowired
lateinit var db: DSLContext

@Autowired
lateinit var bannerRepository: BannerRepository

@Autowired
lateinit var expiredBannerBatchJob: ExpiredBannerBatchJob

@DisplayName("배치할 거 없으면 따로 동작 안하기")
@Test
fun test1() {
// given

// when
expiredBannerBatchJob.scheduleTaskUsingCronExpression()

// then
val createdExpiredBanners = findAllExpiredBanner()
Assertions.assertThat(createdExpiredBanners).isEmpty()
}

@DisplayName("배치할 거 있으면 expiredBanner로 데이터 복사하기")
@Test
fun test2() {
// given
val now = LocalDateTime.now(ZONE_ID)
val expiredAt = now.minusDays(1)
val count = 3
db.batchInsert(
IntStream
.range(0, count)
.mapToObj {
BannerRecord().apply {
this.imageAddress = BannerMother.IMAGE_ADDRESS
this.link = BannerMother.LINK
this.expiredAt = expiredAt
this.createdAt = now
this.updatedAt = now
}
}.toList()
).execute()

// when
expiredBannerBatchJob.scheduleTaskUsingCronExpression()

// then
val createdExpiredBanners = findAllExpiredBanner()
Assertions.assertThat(createdExpiredBanners).hasSize(count)
Assertions.assertThat(bannerRepository.findAll()).isEmpty()
}

private fun findAllExpiredBanner(): List<ExpiredBannerRecord> {
return db.selectQuery(EXPIRED_BANNER).toList()
}

companion object {
val ZONE_ID = ExpiredBannerBatchJob.ZONE_ID
}
}

0 comments on commit 16098ac

Please sign in to comment.