From 835b5e3dc6ae6b92bba3f488f9938d1088a63ab9 Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 17:46:19 +0900 Subject: [PATCH 1/8] =?UTF-8?q?#236=20[feat]=20=EC=9E=AC=EA=B0=80=EC=9E=85?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=20=EC=83=81=ED=83=9C=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=98=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/com/photi/server/domain/user/Contact.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/kotlin/com/photi/server/domain/user/Contact.kt b/src/main/kotlin/com/photi/server/domain/user/Contact.kt index f8678a63..f23c93f7 100644 --- a/src/main/kotlin/com/photi/server/domain/user/Contact.kt +++ b/src/main/kotlin/com/photi/server/domain/user/Contact.kt @@ -54,4 +54,9 @@ class Contact( isDeleted = true deletedDate = LocalDateTime.now() } + + fun updateReRegisterStatus() { + isDeleted = false + deletedDate = null + } } \ No newline at end of file From 6db37157fb71304f1b89131442cf8cac6a1c94c2 Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 17:47:47 +0900 Subject: [PATCH 2/8] =?UTF-8?q?#236=20[feat]=20=EC=9E=AC=EA=B0=80=EC=9E=85?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=20=EC=83=81=ED=83=9C=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EB=B0=B0=EC=B9=98=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/job/ContactReRegisterJobConfig.kt | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt diff --git a/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt b/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt new file mode 100644 index 00000000..94d2626e --- /dev/null +++ b/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt @@ -0,0 +1,89 @@ +package com.photi.server.config.batch.job + +import com.photi.server.domain.user.Contact +import jakarta.persistence.EntityManagerFactory +import org.springframework.batch.core.Job +import org.springframework.batch.core.Step +import org.springframework.batch.core.configuration.annotation.JobScope +import org.springframework.batch.core.configuration.annotation.StepScope +import org.springframework.batch.core.job.builder.JobBuilder +import org.springframework.batch.core.repository.JobRepository +import org.springframework.batch.core.step.builder.StepBuilder +import org.springframework.batch.item.ItemProcessor +import org.springframework.batch.item.database.JpaItemWriter +import org.springframework.batch.item.database.JpaPagingItemReader +import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.transaction.PlatformTransactionManager +import java.time.LocalDate + +@Configuration +class ContactReRegisterJobConfig( + + @Value("\${spring.batch.chunk-size}") + private val chunkSize: Int, + private val entityManagerFactory: EntityManagerFactory, + private val transactionManager: PlatformTransactionManager, + private val jobRepository: JobRepository, +) { + + @Bean(CONTACT_RE_REGISTER_JOB_NAME) + fun job(): Job { + return JobBuilder(CONTACT_RE_REGISTER_JOB_NAME, jobRepository) + .start(step()) + .build() + } + + @Bean(BEAN_PREFIX + "step") + @JobScope + fun step(): Step { + return StepBuilder(BEAN_PREFIX + "step", jobRepository) + .chunk(chunkSize, transactionManager) + .reader(itemReader(null)) + .processor(itemProcessor()) + .writer(itemWriter()) + .build() + } + + @Bean(BEAN_PREFIX + "itemReader") + @StepScope + fun itemReader(@Value("#{jobParameters[date]}") date: LocalDate?): JpaPagingItemReader { + val minusMonthDate = date?.minusMonths(1) + return JpaPagingItemReaderBuilder() + .name(BEAN_PREFIX + "itemReader") + .entityManagerFactory(entityManagerFactory) + .pageSize(chunkSize) + .queryString( + """ + SELECT c FROM Contact c + WHERE c.isDeleted = true + AND c.deletedDate <= :date + ORDER BY c.id ASC + """.trimIndent() + ) + .parameterValues(mapOf("date" to minusMonthDate)) + .build() + } + + @Bean(BEAN_PREFIX + "itemProcessor") + fun itemProcessor(): ItemProcessor { + return ItemProcessor { + it.updateReRegisterStatus() + it + } + } + + @Bean(BEAN_PREFIX + "itemWriter") + fun itemWriter(): JpaItemWriter { + return JpaItemWriter().apply { + setEntityManagerFactory(entityManagerFactory) + } + } + + companion object { + const val CONTACT_RE_REGISTER_JOB_NAME = "회원재가입가능상태" + const val BEAN_PREFIX = CONTACT_RE_REGISTER_JOB_NAME + "_" + } +} From b76532d3c1715568b7381ca524f46454d651f2fa Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 17:48:40 +0900 Subject: [PATCH 3/8] =?UTF-8?q?#236=20[refactor]=20=EC=B1=8C=EB=A6=B0?= =?UTF-8?q?=EC=A7=80=20=EC=A2=85=EB=A3=8C=20=EC=83=81=ED=83=9C=20job=20nam?= =?UTF-8?q?e=20=EB=B3=80=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/batch/job/ChallengeStatusEndJobConfig.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/photi/server/config/batch/job/ChallengeStatusEndJobConfig.kt b/src/main/kotlin/com/photi/server/config/batch/job/ChallengeStatusEndJobConfig.kt index 4e95bc90..0b33a6fe 100644 --- a/src/main/kotlin/com/photi/server/config/batch/job/ChallengeStatusEndJobConfig.kt +++ b/src/main/kotlin/com/photi/server/config/batch/job/ChallengeStatusEndJobConfig.kt @@ -29,9 +29,9 @@ class ChallengeStatusEndJobConfig( private val jobRepository: JobRepository, ) { - @Bean(JOB_NAME) + @Bean(CHALLENGE_END_JOB_NAME) fun job(): Job { - return JobBuilder(JOB_NAME, jobRepository) + return JobBuilder(CHALLENGE_END_JOB_NAME, jobRepository) .start(step()) .build() } @@ -82,7 +82,7 @@ class ChallengeStatusEndJobConfig( } companion object { - const val JOB_NAME = "챌린지종료상태" - const val BEAN_PREFIX = JOB_NAME + "_" + const val CHALLENGE_END_JOB_NAME = "챌린지종료상태" + const val BEAN_PREFIX = CHALLENGE_END_JOB_NAME + "_" } } From c801a130393da21ec56ace8c5febc60d0700ad1e Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 17:49:36 +0900 Subject: [PATCH 4/8] =?UTF-8?q?#236=20[feat]=20=EC=9E=AC=EA=B0=80=EC=9E=85?= =?UTF-8?q?=20=EA=B0=80=EB=8A=A5=20=EC=83=81=ED=83=9C=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/batch/scheduler/JobScheduler.kt | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt b/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt index 2c541231..2dbc400d 100644 --- a/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt +++ b/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt @@ -1,5 +1,7 @@ package com.photi.server.config.batch.scheduler +import com.photi.server.config.batch.job.ChallengeStatusEndJobConfig.Companion.CHALLENGE_END_JOB_NAME +import com.photi.server.config.batch.job.ContactReRegisterJobConfig.Companion.CONTACT_RE_REGISTER_JOB_NAME import org.springframework.batch.core.JobParametersBuilder import org.springframework.batch.core.JobParametersInvalidException import org.springframework.batch.core.configuration.JobRegistry @@ -19,10 +21,32 @@ class JobScheduler( ) { @Scheduled(cron = "0 * 3 * * *") - fun runJob() { + fun runChallengeEndJob() { val date = LocalDate.now().toString() try { - val job = jobRegistry.getJob(JOB_NAME) + val job = jobRegistry.getJob(CHALLENGE_END_JOB_NAME) + val jobParameters = JobParametersBuilder() + .addString(JOB_PARAMETER, date) + .toJobParameters() + jobLauncher.run(job, jobParameters) + } catch (e: NoSuchJobException) { + throw RuntimeException(e) + } catch (e: JobInstanceAlreadyCompleteException) { + throw RuntimeException(e) + } catch (e: JobExecutionAlreadyRunningException) { + throw RuntimeException(e) + } catch (e: JobParametersInvalidException) { + throw RuntimeException(e) + } catch (e: JobRestartException) { + throw RuntimeException(e) + } + } + + @Scheduled(cron = "0 * 4 * * *") + fun runContactReRegisterJob() { + val date = LocalDate.now().toString() + try { + val job = jobRegistry.getJob(CONTACT_RE_REGISTER_JOB_NAME) val jobParameters = JobParametersBuilder() .addString(JOB_PARAMETER, date) .toJobParameters() @@ -41,7 +65,6 @@ class JobScheduler( } companion object { - const val JOB_NAME = "챌린지종료상태" const val JOB_PARAMETER = "date" } } From bdee6a0fb31b24b31eabe0f53d7bcc16da22a8c1 Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 17:52:06 +0900 Subject: [PATCH 5/8] =?UTF-8?q?#236=20[refactor]=20=EC=8A=A4=EC=BC=80?= =?UTF-8?q?=EC=A4=84=EB=9F=AC=20=EA=B3=B5=ED=86=B5=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/batch/scheduler/JobScheduler.kt | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt b/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt index 2dbc400d..5e36aa39 100644 --- a/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt +++ b/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt @@ -22,31 +22,18 @@ class JobScheduler( @Scheduled(cron = "0 * 3 * * *") fun runChallengeEndJob() { - val date = LocalDate.now().toString() - try { - val job = jobRegistry.getJob(CHALLENGE_END_JOB_NAME) - val jobParameters = JobParametersBuilder() - .addString(JOB_PARAMETER, date) - .toJobParameters() - jobLauncher.run(job, jobParameters) - } catch (e: NoSuchJobException) { - throw RuntimeException(e) - } catch (e: JobInstanceAlreadyCompleteException) { - throw RuntimeException(e) - } catch (e: JobExecutionAlreadyRunningException) { - throw RuntimeException(e) - } catch (e: JobParametersInvalidException) { - throw RuntimeException(e) - } catch (e: JobRestartException) { - throw RuntimeException(e) - } + runJob(CHALLENGE_END_JOB_NAME) } @Scheduled(cron = "0 * 4 * * *") fun runContactReRegisterJob() { + runJob(CONTACT_RE_REGISTER_JOB_NAME) + } + + fun runJob(jobName: String) { val date = LocalDate.now().toString() try { - val job = jobRegistry.getJob(CONTACT_RE_REGISTER_JOB_NAME) + val job = jobRegistry.getJob(jobName) val jobParameters = JobParametersBuilder() .addString(JOB_PARAMETER, date) .toJobParameters() From 744d66f93af97f3c4d53e70d5a79587fc92a51eb Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 18:06:07 +0900 Subject: [PATCH 6/8] =?UTF-8?q?#236=20[refactor]=20month=20=EC=83=81?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/config/batch/job/ContactReRegisterJobConfig.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt b/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt index 94d2626e..a3b7a81d 100644 --- a/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt +++ b/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt @@ -50,7 +50,7 @@ class ContactReRegisterJobConfig( @Bean(BEAN_PREFIX + "itemReader") @StepScope fun itemReader(@Value("#{jobParameters[date]}") date: LocalDate?): JpaPagingItemReader { - val minusMonthDate = date?.minusMonths(1) + val minusMonthDate = date?.minusMonths(MONTH_TO_SUBTRACT) return JpaPagingItemReaderBuilder() .name(BEAN_PREFIX + "itemReader") .entityManagerFactory(entityManagerFactory) @@ -85,5 +85,6 @@ class ContactReRegisterJobConfig( companion object { const val CONTACT_RE_REGISTER_JOB_NAME = "회원재가입가능상태" const val BEAN_PREFIX = CONTACT_RE_REGISTER_JOB_NAME + "_" + const val MONTH_TO_SUBTRACT = 1L } } From a3137f1345671cc35e40e2dfbe557b0a3d28bf78 Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 18:57:12 +0900 Subject: [PATCH 7/8] =?UTF-8?q?#236=20[fix]=20date=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20localdatetime=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/config/batch/job/ContactReRegisterJobConfig.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt b/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt index a3b7a81d..de9659c6 100644 --- a/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt +++ b/src/main/kotlin/com/photi/server/config/batch/job/ContactReRegisterJobConfig.kt @@ -50,7 +50,7 @@ class ContactReRegisterJobConfig( @Bean(BEAN_PREFIX + "itemReader") @StepScope fun itemReader(@Value("#{jobParameters[date]}") date: LocalDate?): JpaPagingItemReader { - val minusMonthDate = date?.minusMonths(MONTH_TO_SUBTRACT) + val minusMonthDateTime = date?.minusMonths(MONTH_TO_SUBTRACT)?.atStartOfDay() return JpaPagingItemReaderBuilder() .name(BEAN_PREFIX + "itemReader") .entityManagerFactory(entityManagerFactory) @@ -63,7 +63,7 @@ class ContactReRegisterJobConfig( ORDER BY c.id ASC """.trimIndent() ) - .parameterValues(mapOf("date" to minusMonthDate)) + .parameterValues(mapOf("date" to minusMonthDateTime)) .build() } From 1b7931e4cd7b4e5f1794f837d0dfbf130fc33ec0 Mon Sep 17 00:00:00 2001 From: YuGyeong98 Date: Thu, 3 Jul 2025 18:59:13 +0900 Subject: [PATCH 8/8] =?UTF-8?q?#236=20[refactor]=20cron=EC=8B=9D=20?= =?UTF-8?q?=ED=95=9C=20=EB=B2=88=EB=A7=8C=20=EC=8B=A4=ED=96=89=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/photi/server/config/batch/scheduler/JobScheduler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt b/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt index 5e36aa39..31a5dded 100644 --- a/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt +++ b/src/main/kotlin/com/photi/server/config/batch/scheduler/JobScheduler.kt @@ -20,12 +20,12 @@ class JobScheduler( private val jobRegistry: JobRegistry, ) { - @Scheduled(cron = "0 * 3 * * *") + @Scheduled(cron = "0 0 3 * * *") fun runChallengeEndJob() { runJob(CHALLENGE_END_JOB_NAME) } - @Scheduled(cron = "0 * 4 * * *") + @Scheduled(cron = "0 0 4 * * *") fun runContactReRegisterJob() { runJob(CONTACT_RE_REGISTER_JOB_NAME) }