diff --git a/build.gradle b/build.gradle index 056e7d7..e7ec0ec 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE' implementation platform("org.springframework.cloud:spring-cloud-dependencies:2021.0.5") + implementation 'org.springframework.boot:spring-boot-starter-batch' + implementation 'org.springframework.boot:spring-boot-starter-quartz' + testImplementation 'org.springframework.batch:spring-batch-test' + // queryDSL 설정 implementation "com.querydsl:querydsl-jpa" implementation "com.querydsl:querydsl-core" diff --git a/src/main/java/zipdabang/server/ZipdabangServerApplication.java b/src/main/java/zipdabang/server/ZipdabangServerApplication.java index d9dc085..7f0e405 100644 --- a/src/main/java/zipdabang/server/ZipdabangServerApplication.java +++ b/src/main/java/zipdabang/server/ZipdabangServerApplication.java @@ -1,15 +1,19 @@ package zipdabang.server; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableFeignClients @EnableJpaAuditing @EnableRedisRepositories +@EnableBatchProcessing +@EnableScheduling public class ZipdabangServerApplication { public static void main(String[] args) { diff --git a/src/main/java/zipdabang/server/batch/config/WeeklyBestRecipeBatchConfig.java b/src/main/java/zipdabang/server/batch/config/WeeklyBestRecipeBatchConfig.java new file mode 100644 index 0000000..b53ca77 --- /dev/null +++ b/src/main/java/zipdabang/server/batch/config/WeeklyBestRecipeBatchConfig.java @@ -0,0 +1,127 @@ +package zipdabang.server.batch.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; +import org.springframework.batch.core.configuration.annotation.StepScope; +import org.springframework.batch.item.ItemProcessor; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.data.RepositoryItemReader; +import org.springframework.batch.item.data.builder.RepositoryItemReaderBuilder; +import org.springframework.batch.item.support.ListItemReader; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.domain.Sort; +import zipdabang.server.domain.recipe.Recipe; +import zipdabang.server.domain.recipe.WeeklyBestRecipe; +import zipdabang.server.repository.recipeRepositories.RecipeRepository; +import zipdabang.server.repository.recipeRepositories.WeeklyBestRecipeRepository; + +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +@Slf4j +@Configuration +@EnableBatchProcessing +@RequiredArgsConstructor +public class WeeklyBestRecipeBatchConfig { + + private final JobBuilderFactory jobBuilderFactory; + private final StepBuilderFactory stepBuilderFactory; + private final RecipeRepository recipeRepository; + private final WeeklyBestRecipeRepository weeklyBestRecipeRepository; + + @Value("10") + private Integer chunkSize; + + @Bean + public Job job() { + Job job = jobBuilderFactory.get("WeeklyBestRecipeJob") + .start(step1()) + .next(step2()) + .next(step3()) + .build(); + + return job; + } + + @Bean + public Step step1() { + return stepBuilderFactory.get("WeeklyBestRecipeStep1") + .chunk(5) + .reader(step1ItemReader()) + .writer(step1ItemWriter()) + .build(); + } + + @Bean + public Step step2() { + return stepBuilderFactory.get("WeeklyBestRecipeStep2") + .chunk(chunkSize) + .reader(step2ItemReader()) + .processor(step2ItemProcessor()) + .writer(step2ItemWriter()) + .build(); + } + + @Bean + public Step step3() { + return stepBuilderFactory.get("WeeklyBestRecipeStep3") + .chunk(chunkSize) + .reader(step3ItemReader()) + .writer(step3ItemWriter()) + .build(); + } + + @StepScope + @Bean + public ListItemReader step1ItemReader() { + return new ListItemReader(weeklyBestRecipeRepository.findAll()); + } + + @StepScope + @Bean + public ItemWriter step1ItemWriter() { + return bestRecipes -> bestRecipes.forEach(bestRecipe -> weeklyBestRecipeRepository.delete(bestRecipe)); + } + + @StepScope + @Bean + public ListItemReader step2ItemReader() { + return new ListItemReader(recipeRepository.findTop5ByOrderByWeekLikeDescWeekScrapDescTotalLikeDescTotalScrapDesc()); + } + + @StepScope + @Bean + public ItemProcessor step2ItemProcessor() { + AtomicInteger index = new AtomicInteger(1); + + return recipe -> WeeklyBestRecipe.builder() + .ranking(index.getAndIncrement()) + .recipe(recipe) + .build(); + } + + @StepScope + @Bean + public ItemWriter step2ItemWriter() { + return bestRecipes -> bestRecipes.forEach(bestRecipe -> weeklyBestRecipeRepository.save(bestRecipe)); + } + + @StepScope + @Bean + public ListItemReader step3ItemReader() { + return new ListItemReader(recipeRepository.findAll()); + } + + @StepScope + @Bean + public ItemWriter step3ItemWriter() { + return recipes -> recipes.forEach(recipe -> recipeRepository.updateWeeklyData(recipe)); + } +} diff --git a/src/main/java/zipdabang/server/batch/schedular/BatchScheduler.java b/src/main/java/zipdabang/server/batch/schedular/BatchScheduler.java new file mode 100644 index 0000000..71b0d93 --- /dev/null +++ b/src/main/java/zipdabang/server/batch/schedular/BatchScheduler.java @@ -0,0 +1,40 @@ +package zipdabang.server.batch.schedular; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.batch.core.JobParameter; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersInvalidException; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; +import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import zipdabang.server.batch.config.WeeklyBestRecipeBatchConfig; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Component +@RequiredArgsConstructor +public class BatchScheduler { + + private final JobLauncher jobLauncher; + private final WeeklyBestRecipeBatchConfig weeklyBestRecipeBatchConfig; + +// @Scheduled(cron = "0 0 0 ? ? 1") + @Scheduled(cron = "0 0 0 * * 1") + public void runWeeklyBestRecipeJob() { + Map confMap = new HashMap<>(); + confMap.put("time", new JobParameter(System.currentTimeMillis())); + JobParameters jobParameters = new JobParameters(confMap); + + try { + jobLauncher.run(weeklyBestRecipeBatchConfig.job(), jobParameters); + } catch (JobExecutionAlreadyRunningException | JobInstanceAlreadyCompleteException + | JobParametersInvalidException | org.springframework.batch.core.repository.JobRestartException e) { + log.error(e.getMessage()); + } + } +} diff --git a/src/main/java/zipdabang/server/domain/recipe/WeeklyBestRecipe.java b/src/main/java/zipdabang/server/domain/recipe/WeeklyBestRecipe.java index 0929b97..9f8e939 100644 --- a/src/main/java/zipdabang/server/domain/recipe/WeeklyBestRecipe.java +++ b/src/main/java/zipdabang/server/domain/recipe/WeeklyBestRecipe.java @@ -25,5 +25,5 @@ public class WeeklyBestRecipe extends BaseEntity { private Recipe recipe; private Integer ranking; - private Integer preRanking; +// private Integer preRanking; } diff --git a/src/main/java/zipdabang/server/repository/recipeRepositories/RecipeRepository.java b/src/main/java/zipdabang/server/repository/recipeRepositories/RecipeRepository.java index 623f8be..e26b3c0 100644 --- a/src/main/java/zipdabang/server/repository/recipeRepositories/RecipeRepository.java +++ b/src/main/java/zipdabang/server/repository/recipeRepositories/RecipeRepository.java @@ -4,6 +4,8 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import zipdabang.server.domain.member.Member; import zipdabang.server.domain.recipe.Recipe; @@ -20,4 +22,10 @@ public interface RecipeRepository extends JpaRepository { List findTop5ByMemberOrderByCreatedAtDesc(Member member); Page findByMember(Member member, PageRequest pageRequest); + + List findTop5ByOrderByWeekLikeDescWeekScrapDescTotalLikeDescTotalScrapDesc(); + + @Modifying + @Query("UPDATE Recipe r SET r.weekLike = 0, r.weekScrap = 0, r.weekView = 0 WHERE r = :recipe") + void updateWeeklyData(Recipe recipe); }