From 07ee778db7881c5500bcf023aada2562b402992d Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Wed, 6 Aug 2025 02:44:23 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20daily=5Fretrun=20=EB=B0=B0=EC=B9=98?= =?UTF-8?q?=20(=20=EC=8A=A4=EC=BC=80=EC=A4=84=EB=9F=AC=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/job/DailyReturnBatchJob.java | 68 +++++++++++++++++++ .../batch/writer/DailyReturnWriter.java | 28 ++++++++ .../domain/mapper/DailyReturnMapper.java | 12 ++++ .../planitbatch/domain/vo/DailyReturn.java | 18 +++++ .../planitbatch/domain/vo/ProductDto.java | 21 ++++++ .../resources/mapper/DailyReturnMapper.xml | 23 +++++++ 6 files changed, 170 insertions(+) create mode 100644 src/main/java/woojooin/planitbatch/batch/job/DailyReturnBatchJob.java create mode 100644 src/main/java/woojooin/planitbatch/batch/writer/DailyReturnWriter.java create mode 100644 src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java create mode 100644 src/main/java/woojooin/planitbatch/domain/vo/DailyReturn.java create mode 100644 src/main/java/woojooin/planitbatch/domain/vo/ProductDto.java create mode 100644 src/main/resources/mapper/DailyReturnMapper.xml diff --git a/src/main/java/woojooin/planitbatch/batch/job/DailyReturnBatchJob.java b/src/main/java/woojooin/planitbatch/batch/job/DailyReturnBatchJob.java new file mode 100644 index 0000000..e24ee3b --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/job/DailyReturnBatchJob.java @@ -0,0 +1,68 @@ +package woojooin.planitbatch.batch.job; + +import lombok.RequiredArgsConstructor; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.batch.MyBatisPagingItemReader; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +//import woojooin.planitbatch.batch.reader.DailyReturnReader; +import woojooin.planitbatch.batch.writer.DailyReturnWriter; +import woojooin.planitbatch.domain.mapper.DailyReturnMapper; +import woojooin.planitbatch.domain.vo.DailyReturn; +import org.springframework.beans.factory.annotation.Value; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; + +@EnableBatchProcessing +@RequiredArgsConstructor +@Configuration +public class DailyReturnBatchJob { + + private final JobBuilderFactory jobs; + private final StepBuilderFactory steps; +// private final DailyReturnReader dailyReturnReader; + private final DailyReturnWriter writer; + private final SqlSessionFactory sqlSessionFactory; + + + @Bean + @StepScope + public MyBatisPagingItemReader reader( + @Value("#{jobParameters['date']}") String dateStr) { + MyBatisPagingItemReader reader = new MyBatisPagingItemReader<>(); + + reader.setSqlSessionFactory(sqlSessionFactory); + reader.setQueryId("woojooin.planitbatch.domain.mapper.DailyReturnMapper.findAllForToday"); + + Map parameters = new HashMap<>(); + parameters.put("date", LocalDate.parse(dateStr)); + reader.setParameterValues(parameters); + reader.setPageSize(100); + + return reader; + } + + + @Bean + public Job dailyReturnJob(Step dailyReturnStep) { + return jobs.get("dailyReturnJob") + .start(dailyReturnStep) + .build(); + } + + @Bean + public Step dailyReturnStep(MyBatisPagingItemReader reader) { + return steps.get("dailyReturnStep") + .chunk(100) + .reader(reader) + .writer(writer) + .build(); + } +} diff --git a/src/main/java/woojooin/planitbatch/batch/writer/DailyReturnWriter.java b/src/main/java/woojooin/planitbatch/batch/writer/DailyReturnWriter.java new file mode 100644 index 0000000..b69ca6b --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/writer/DailyReturnWriter.java @@ -0,0 +1,28 @@ +package woojooin.planitbatch.batch.writer; + +import lombok.RequiredArgsConstructor; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.batch.item.ItemWriter; +import org.springframework.stereotype.Component; +import woojooin.planitbatch.domain.mapper.DailyReturnMapper; +import woojooin.planitbatch.domain.vo.DailyReturn; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class DailyReturnWriter implements ItemWriter { + private final SqlSessionFactory sqlSessionFactory; + + @Override + public void write(List items) { + try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { + for (DailyReturn item : items) { + session.insert("woojooin.planitbatch.domain.mapper.DailyReturnMapper.insert", item); + } + session.flushStatements(); + } + } +} \ No newline at end of file diff --git a/src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java b/src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java new file mode 100644 index 0000000..433dd83 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java @@ -0,0 +1,12 @@ +package woojooin.planitbatch.domain.mapper; + +import org.apache.ibatis.annotations.Param; +import woojooin.planitbatch.domain.vo.DailyReturn; + +import java.time.LocalDate; +import java.util.List; + +public interface DailyReturnMapper { + List findAllForToday(@Param("date")LocalDate date); + void insert(DailyReturn dailyReturn); +} diff --git a/src/main/java/woojooin/planitbatch/domain/vo/DailyReturn.java b/src/main/java/woojooin/planitbatch/domain/vo/DailyReturn.java new file mode 100644 index 0000000..e02d0d3 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/domain/vo/DailyReturn.java @@ -0,0 +1,18 @@ +package woojooin.planitbatch.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDate; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DailyReturn { + private Long memberId; + private String itemCode; + private LocalDate returnDate; + private BigDecimal amount; +} diff --git a/src/main/java/woojooin/planitbatch/domain/vo/ProductDto.java b/src/main/java/woojooin/planitbatch/domain/vo/ProductDto.java new file mode 100644 index 0000000..102845c --- /dev/null +++ b/src/main/java/woojooin/planitbatch/domain/vo/ProductDto.java @@ -0,0 +1,21 @@ +package woojooin.planitbatch.domain.vo; + +import java.math.BigDecimal; +import java.time.LocalDate; + +public class ProductDto { + + /** 상품코드 */ + private String productIdentifier; + + /** 상품명 */ + private String productName; + + /** 기준일자 */ + private LocalDate baseDate; + + /** 종가 */ + private BigDecimal closingPrice; + + +} diff --git a/src/main/resources/mapper/DailyReturnMapper.xml b/src/main/resources/mapper/DailyReturnMapper.xml new file mode 100644 index 0000000..2e674ae --- /dev/null +++ b/src/main/resources/mapper/DailyReturnMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + From f813bbe644a749a277753d22adcd11c6cc6dd0b8 Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Tue, 19 Aug 2025 01:42:09 +0900 Subject: [PATCH 2/8] =?UTF-8?q?fix:=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DailyReturnBatchJob.java | 2 +- .../investreturn/DailyReturnScheduler.java | 32 +++++++++++++++++++ .../domain/mapper/DailyReturnMapper.java | 5 +++ .../resources/mapper/DailyReturnMapper.xml | 21 +++++++++--- 4 files changed, 55 insertions(+), 5 deletions(-) rename src/main/java/woojooin/planitbatch/batch/job/{ => investreturn}/DailyReturnBatchJob.java (97%) create mode 100644 src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyReturnScheduler.java diff --git a/src/main/java/woojooin/planitbatch/batch/job/DailyReturnBatchJob.java b/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyReturnBatchJob.java similarity index 97% rename from src/main/java/woojooin/planitbatch/batch/job/DailyReturnBatchJob.java rename to src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyReturnBatchJob.java index e24ee3b..1c7eece 100644 --- a/src/main/java/woojooin/planitbatch/batch/job/DailyReturnBatchJob.java +++ b/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyReturnBatchJob.java @@ -1,4 +1,4 @@ -package woojooin.planitbatch.batch.job; +package woojooin.planitbatch.batch.job.investreturn; import lombok.RequiredArgsConstructor; import org.apache.ibatis.session.SqlSessionFactory; diff --git a/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyReturnScheduler.java b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyReturnScheduler.java new file mode 100644 index 0000000..de243aa --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyReturnScheduler.java @@ -0,0 +1,32 @@ +package woojooin.planitbatch.batch.scheduler.investreturn; + + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; + +@Component +@RequiredArgsConstructor +public class DailyReturnScheduler { + + private final JobLauncher jobLauncher; + private final Job dailyReturnJob; + + @Scheduled(cron = "0 10 00 * * ?") + public void runDailyReturnJob() throws Exception { + JobParameters jobParameters = new JobParametersBuilder() + .addString("date", LocalDate.now().toString()) +// .addString("date", "2025-08-05") // 목데이터 날짜 + .addLong("time", System.currentTimeMillis()) + .toJobParameters(); + + jobLauncher.run(dailyReturnJob, jobParameters); + } +} + diff --git a/src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java b/src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java index 433dd83..e15e8a8 100644 --- a/src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java +++ b/src/main/java/woojooin/planitbatch/domain/mapper/DailyReturnMapper.java @@ -8,5 +8,10 @@ public interface DailyReturnMapper { List findAllForToday(@Param("date")LocalDate date); + + List findpast(@Param("startDate") LocalDate startDate, + @Param("endDate") LocalDate endDate); + + void insert(DailyReturn dailyReturn); } diff --git a/src/main/resources/mapper/DailyReturnMapper.xml b/src/main/resources/mapper/DailyReturnMapper.xml index 2e674ae..13930bf 100644 --- a/src/main/resources/mapper/DailyReturnMapper.xml +++ b/src/main/resources/mapper/DailyReturnMapper.xml @@ -7,11 +7,24 @@ SELECT mp.member_id AS memberId, mp.product_id AS itemCode, - p.bas_dt AS returnDate, - CAST(p.clpr AS DECIMAL(20,2)) * mp.quantity AS amount + p.base_date AS returnDate, + sum(CAST(p.closing_price AS DECIMAL(20,2)) * mp.quantity )AS amount FROM member_product mp - JOIN product p ON mp.product_id COLLATE utf8mb4_unicode_ci = p.isin_cd COLLATE utf8mb4_unicode_ci - WHERE DATE(p.bas_dt) = #{date} + JOIN product p ON mp.product_id COLLATE utf8mb4_unicode_ci = p.shorten_code COLLATE utf8mb4_unicode_ci + WHERE DATE(p.base_date) = #{date} + GROUP BY mp.member_id, mp.product_id, p.base_date + + + From 7f3172fbbc83fcf414c36ca4f0feff4d56d31a0e Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:31:42 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20daily=20=EB=B0=B0=EC=B9=98=20,=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A5=B4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/job/investreturn/DailyBatch.java | 69 +++++++++++++++++++ .../investreturn/DailyScheduler.java | 61 ++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyBatch.java create mode 100644 src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyScheduler.java diff --git a/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyBatch.java b/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyBatch.java new file mode 100644 index 0000000..dc5285a --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyBatch.java @@ -0,0 +1,69 @@ +package woojooin.planitbatch.batch.job.investreturn; + +import lombok.RequiredArgsConstructor; + +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.batch.MyBatisPagingItemReader; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import woojooin.planitbatch.batch.writer.InvestMentWriter; +import woojooin.planitbatch.domain.vo.InvestmentReturn; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; +import org.springframework.beans.factory.annotation.Value; + +@Slf4j +@Configuration +@EnableBatchProcessing +@RequiredArgsConstructor +public class DailyBatch { + + private final JobBuilderFactory jobs; + private final StepBuilderFactory steps; + private final SqlSessionFactory sqlSessionFactory; + private final InvestMentWriter writer; + + + @Bean + @StepScope + public MyBatisPagingItemReader dailyReturnReader( + @Value("#{jobParameters['today']}") String todayStr) + { + MyBatisPagingItemReader reader = new MyBatisPagingItemReader<>(); + reader.setSqlSessionFactory(sqlSessionFactory); + reader.setQueryId("woojooin.planitbatch.domain.mapper.InvestmentReturnMapper.findDailyReturn"); + Map parameters = new HashMap<>(); + parameters.put("today", LocalDate.parse(todayStr)); + + reader.setParameterValues(parameters); + reader.setPageSize(100); + + return reader; + } + + @Bean + public Step dailybatchStep(MyBatisPagingItemReader dailyReturnReader) { + return steps.get("dailybatchStep") + .chunk(100) + .reader(dailyReturnReader) + .writer(writer) + .build(); + } + + @Bean + public Job dailybatchJob(Step dailybatchStep) { + return jobs.get("dailybatchJob") + .start(dailybatchStep) + .build(); + } + +} + diff --git a/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyScheduler.java b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyScheduler.java new file mode 100644 index 0000000..5153b9f --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/DailyScheduler.java @@ -0,0 +1,61 @@ +package woojooin.planitbatch.batch.scheduler.investreturn; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; + +import static woojooin.planitbatch.batch.scheduler.investreturn.PastScheduler.FORMATTER; + +@Component +@RequiredArgsConstructor +public class DailyScheduler { + + private final JobLauncher jobLauncher; + + @Qualifier("dailybatchJob") + private final Job dailybatchJob; + + @Scheduled(cron = "0 20 00 * * ?") + public void runDailyReturnJob() throws Exception { + LocalDate today = LocalDate.now(); + + JobParameters jobParameters = new JobParametersBuilder() + .addString("today", today.toString()) // 예: "2025-08-06" + .addLong("time", System.currentTimeMillis()) + .toJobParameters(); + + jobLauncher.run(dailybatchJob, jobParameters); + +// 과거 etf 그래프에서 만든 자료 + +// LocalDate startDate = LocalDate.of(2024, 8, 5); +// LocalDate endDate = LocalDate.of(2025, 8, 5); +// +// for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { +// String dateStr = date.format(FORMATTER); +// System.out.println("▶ " + dateStr + " 배치 시작"); +// +// JobParameters jobParameters = new JobParametersBuilder() +// .addString("today", dateStr) +// .addLong("time", System.currentTimeMillis()) // JobInstance 중복 방지 +// .toJobParameters(); +// +// jobLauncher.run(dailybatchJob, jobParameters); +// } + } +} + + + + + + + + From f13b528b2630b587ebde1ed093e16ebe9e0665d0 Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:32:33 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20weekly=20=EB=B0=B0=EC=B9=98=20,=20?= =?UTF-8?q?=EC=8A=A4=EC=BC=80=EC=A5=B4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/job/investreturn/WeeklyBatch.java | 68 +++++++++++++++++++ .../investreturn/WeeklyScheduler.java | 58 ++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 src/main/java/woojooin/planitbatch/batch/job/investreturn/WeeklyBatch.java create mode 100644 src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/WeeklyScheduler.java diff --git a/src/main/java/woojooin/planitbatch/batch/job/investreturn/WeeklyBatch.java b/src/main/java/woojooin/planitbatch/batch/job/investreturn/WeeklyBatch.java new file mode 100644 index 0000000..35a48c9 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/job/investreturn/WeeklyBatch.java @@ -0,0 +1,68 @@ +package woojooin.planitbatch.batch.job.investreturn; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.batch.MyBatisPagingItemReader; +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.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import woojooin.planitbatch.batch.writer.InvestMentWriter; +import woojooin.planitbatch.domain.vo.InvestmentReturn; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; +@Slf4j +@Configuration +@EnableBatchProcessing +@RequiredArgsConstructor +public class WeeklyBatch { + private final JobBuilderFactory jobs; + private final StepBuilderFactory steps; + private final SqlSessionFactory sqlSessionFactory; + private final InvestMentWriter writer; + + @Bean + @StepScope + public MyBatisPagingItemReader weeklyReturnReader( + @Value("#{jobParameters['today']}") String todayStr ){ + + MyBatisPagingItemReader reader = new MyBatisPagingItemReader<>(); + reader.setSqlSessionFactory(sqlSessionFactory); + reader.setQueryId("woojooin.planitbatch.domain.mapper.InvestmentReturnMapper.findWeeklyReturn"); + Map parameters = new HashMap<>(); + parameters.put("today", LocalDate.parse(todayStr)); + reader.setParameterValues(parameters); + reader.setPageSize(100); + + return reader; + } + + @Bean + + public Job WeeklyBatchJob(Step WeeklyBatchStep) { + return jobs.get("WeeklyBatchJob") + .start(WeeklyBatchStep) + .build(); + + } + + @Bean + + public Step WeeklyBatchStep(MyBatisPagingItemReader weeklyReturnReader) { + return steps.get("WeeklyBatchStep") + .chunk(100) + .reader(weeklyReturnReader) + .writer(writer) + .build(); + } + +} + + diff --git a/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/WeeklyScheduler.java b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/WeeklyScheduler.java new file mode 100644 index 0000000..ed4e4d9 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/WeeklyScheduler.java @@ -0,0 +1,58 @@ +package woojooin.planitbatch.batch.scheduler.investreturn; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecutionException; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.temporal.TemporalAdjusters; + +import static woojooin.planitbatch.batch.scheduler.investreturn.PastScheduler.FORMATTER; + +@Component +@RequiredArgsConstructor +public class WeeklyScheduler { + private final JobLauncher jobLauncher; + + @Qualifier("WeeklyBatchJob") + private final Job WeeklyBatchJob; + + + @Scheduled(cron = "0 40 0 ? * MON") + public void runWeeklyBatchJob() throws JobExecutionException { + LocalDate today = LocalDate.now(); + + JobParameters jobParameters = new JobParametersBuilder() + .addString("today",today.toString()) + .addLong("time", System.currentTimeMillis()) + .toJobParameters(); + + jobLauncher.run(WeeklyBatchJob, jobParameters); + + //과거 etf 위클리 배치 +// LocalDate startDate = LocalDate.of(2024, 8, 5); +// LocalDate endDate = LocalDate.of(2025, 8, 5); +// LocalDate firstMonday = startDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY)); +// +// for (LocalDate date = firstMonday; !date.isAfter(endDate); date = date.plusWeeks(1)) { +// String dateStr = date.format(FORMATTER); +// System.out.println("▶ 주차 기준 날짜: " + dateStr + " 배치 시작"); +// +// var params = new JobParametersBuilder() +// .addString("today", dateStr) +// .addLong("time", System.currentTimeMillis()) +// .toJobParameters(); +// +// jobLauncher.run(WeeklyBatchJob, params); + } + } + + + From 06282f55ed411ef04cdae1a8051486893f9a210a Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:33:17 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=EB=A8=BC=EC=8A=AC=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=B0=EC=B9=98=20,=20=EC=8A=A4=EC=BC=80=EC=A5=B4=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../investreturn/MonthlyScheduler.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/MonthlyScheduler.java diff --git a/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/MonthlyScheduler.java b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/MonthlyScheduler.java new file mode 100644 index 0000000..b46830f --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/MonthlyScheduler.java @@ -0,0 +1,65 @@ +package woojooin.planitbatch.batch.scheduler.investreturn; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; + +@Component +@RequiredArgsConstructor +public class MonthlyScheduler { + + private final JobLauncher jobLauncher; + + @Qualifier("monthlybatchJob") + private final Job monthlybatchJob; + + private static final DateTimeFormatter FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + @Scheduled(cron = "0 30 0 L * ?") + public void runMonthlyReturnJob() throws Exception { + LocalDate today = LocalDate.now(); // 예: 2025-09-01 + + JobParameters jobParameters = new JobParametersBuilder() + .addString("today", today.toString()) + .addLong("time", System.currentTimeMillis()) + .toJobParameters(); + + jobLauncher.run(monthlybatchJob, jobParameters); + } +} + +//과거 etf 저료 넣은 것 +// LocalDate startDate = LocalDate.of(2024, 8, 5); +// LocalDate endDate = LocalDate.of(2025, 8, 5); +// +// LocalDate current = startDate.with(TemporalAdjusters.firstDayOfNextMonth()); +// if (startDate.getDayOfMonth() == 1) { +// current = startDate; +// } +// +// for (; !current.isAfter(endDate); current = current.plusMonths(1)) { +// // 그 달의 마지막 날로 재조정 +// LocalDate lastOfMonth = current.with(TemporalAdjusters.lastDayOfMonth()); +// String dateStr = lastOfMonth.format(FORMATTER); +// System.out.println("▶ 월별 기준 날짜: " + dateStr + " 배치 시작"); +// +// var params = new JobParametersBuilder() +// .addString("today", dateStr) +// .addLong("time", System.currentTimeMillis()) +// .toJobParameters(); +// +// jobLauncher.run(monthlybatchJob, params); +// } +// } + +//} From 97001c3a2d1a28af31c3d877b87c7f6f84e4e24f Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:33:58 +0900 Subject: [PATCH 6/8] feat: investment mapper , writer --- .../batch/writer/InvestMentWriter.java | 43 +++++++++ .../batch/writer/InvestMonthlyWriter.java | 43 +++++++++ .../domain/mapper/InvestmentReturnMapper.java | 22 +++++ .../domain/vo/InvestmentReturn.java | 20 +++++ .../mapper/InvestmentReturnMapper.xml | 88 +++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 src/main/java/woojooin/planitbatch/batch/writer/InvestMentWriter.java create mode 100644 src/main/java/woojooin/planitbatch/batch/writer/InvestMonthlyWriter.java create mode 100644 src/main/java/woojooin/planitbatch/domain/mapper/InvestmentReturnMapper.java create mode 100644 src/main/java/woojooin/planitbatch/domain/vo/InvestmentReturn.java create mode 100644 src/main/resources/mapper/InvestmentReturnMapper.xml diff --git a/src/main/java/woojooin/planitbatch/batch/writer/InvestMentWriter.java b/src/main/java/woojooin/planitbatch/batch/writer/InvestMentWriter.java new file mode 100644 index 0000000..5212ae5 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/writer/InvestMentWriter.java @@ -0,0 +1,43 @@ +package woojooin.planitbatch.batch.writer; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.batch.item.ItemWriter; +import org.springframework.stereotype.Component; +import woojooin.planitbatch.domain.vo.InvestmentReturn; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class InvestMentWriter implements ItemWriter { + + private final SqlSessionFactory sqlSessionFactory; + + @Override + public void write(List items) throws Exception { + try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { + log.info("▶ Writer 실행됨. item 수: {}", items.size()); + for(InvestmentReturn item : items) { + if(item.getAmountPast() != null&&item.getAmountPast().compareTo(BigDecimal.ZERO)>0) { + BigDecimal rate = item.getAmountNow() + .subtract(item.getAmountPast()) + .divide(item.getAmountPast(), 4, RoundingMode.HALF_UP) + .multiply(new BigDecimal("100")); + item.setReturnRate(rate); + } else { + item.setReturnRate(BigDecimal.ZERO); // 과거 금액이 0이면 0% + } + session.insert("woojooin.planitbatch.domain.mapper.InvestmentReturnMapper.insert", item); + } + session.flushStatements(); // 모든 INSERT 실행 + } + } +} + diff --git a/src/main/java/woojooin/planitbatch/batch/writer/InvestMonthlyWriter.java b/src/main/java/woojooin/planitbatch/batch/writer/InvestMonthlyWriter.java new file mode 100644 index 0000000..ad5a9a9 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/writer/InvestMonthlyWriter.java @@ -0,0 +1,43 @@ +package woojooin.planitbatch.batch.writer; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.ExecutorType; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.batch.item.ItemWriter; +import org.springframework.stereotype.Component; +import woojooin.planitbatch.domain.vo.InvestmentReturn; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + + +@Slf4j +@Component +@RequiredArgsConstructor +public class InvestMonthlyWriter implements ItemWriter{ + + private final SqlSessionFactory sqlSessionFactory; + + @Override + public void write(List items) throws Exception { + try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) { + log.info("▶ Writer 실행됨. item 수: {}", items.size()); + for(InvestmentReturn item : items) { + if(item.getAmountPast() != null&&item.getAmountPast().compareTo(BigDecimal.ZERO)>0) { + BigDecimal rate = item.getAmountNow() + .subtract(item.getAmountPast()) + .divide(item.getAmountPast(), 4, RoundingMode.HALF_UP) + .multiply(new BigDecimal("100")); + item.setReturnRate(rate); + } else { + item.setReturnRate(BigDecimal.ZERO); // 과거 금액이 0이면 0% + } + session.insert("woojooin.planitbatch.domain.mapper.InvestmentReturnMapper.upsertMonthlyReturn", item); + } + session.flushStatements(); // 모든 INSERT 실행 + } + } +} \ No newline at end of file diff --git a/src/main/java/woojooin/planitbatch/domain/mapper/InvestmentReturnMapper.java b/src/main/java/woojooin/planitbatch/domain/mapper/InvestmentReturnMapper.java new file mode 100644 index 0000000..2d063f3 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/domain/mapper/InvestmentReturnMapper.java @@ -0,0 +1,22 @@ +package woojooin.planitbatch.domain.mapper; + +import org.apache.ibatis.annotations.Param; +import org.springframework.cglib.core.Local; +import woojooin.planitbatch.domain.vo.DailyReturn; +import woojooin.planitbatch.domain.vo.InvestmentReturn; + +import java.time.LocalDate; +import java.util.List; + +public interface InvestmentReturnMapper { + List findDailyReturn(@Param("today") LocalDate today); + List findWeeklyReturn(@Param("today") LocalDate today); + ListfindMonthlyReturn(@Param("today") LocalDate today); + void insert(InvestmentReturn returnResult); + void upsertMonthlyReturn(InvestmentReturn returnResult); + +} + + + + diff --git a/src/main/java/woojooin/planitbatch/domain/vo/InvestmentReturn.java b/src/main/java/woojooin/planitbatch/domain/vo/InvestmentReturn.java new file mode 100644 index 0000000..080ec2d --- /dev/null +++ b/src/main/java/woojooin/planitbatch/domain/vo/InvestmentReturn.java @@ -0,0 +1,20 @@ +package woojooin.planitbatch.domain.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDate; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class InvestmentReturn { + private Long memberId; + private String returnType; + private LocalDate returnDate; + private BigDecimal amountNow; + private BigDecimal amountPast; + private BigDecimal returnRate; +} diff --git a/src/main/resources/mapper/InvestmentReturnMapper.xml b/src/main/resources/mapper/InvestmentReturnMapper.xml new file mode 100644 index 0000000..2529732 --- /dev/null +++ b/src/main/resources/mapper/InvestmentReturnMapper.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + INSERT INTO investment_return (member_id, return_type, return_date, return_rate, amount_now, amount_past) + VALUES (#{memberId}, #{returnType}, #{returnDate}, #{returnRate}, #{amountNow}, #{amountPast}) + + + + INSERT INTO investment_return + (member_id, return_type, return_date, amount_now, amount_past, return_rate) + VALUES + (#{memberId}, 'MONTHLY', #{returnDate}, + #{amountNow}, #{amountPast}, #{returnRate}) + ON DUPLICATE KEY UPDATE + amount_now = VALUES(amount_now), + amount_past = VALUES(amount_past), + return_rate = VALUES(return_rate) + + + From 49af5e5e36172d9886f2a9cf09c72f8389396ad3 Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:34:33 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20past=20batch=20,=20scheduler=20,=20?= =?UTF-8?q?=EB=A8=BC=EC=8A=AC=EB=A6=AC=20=EB=B0=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/job/investreturn/MonthlyBatch.java | 70 +++++++++++++++++++ .../batch/job/investreturn/PastBatch.java | 70 +++++++++++++++++++ .../scheduler/investreturn/PastScheduler.java | 44 ++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 src/main/java/woojooin/planitbatch/batch/job/investreturn/MonthlyBatch.java create mode 100644 src/main/java/woojooin/planitbatch/batch/job/investreturn/PastBatch.java create mode 100644 src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/PastScheduler.java diff --git a/src/main/java/woojooin/planitbatch/batch/job/investreturn/MonthlyBatch.java b/src/main/java/woojooin/planitbatch/batch/job/investreturn/MonthlyBatch.java new file mode 100644 index 0000000..9f21110 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/job/investreturn/MonthlyBatch.java @@ -0,0 +1,70 @@ +package woojooin.planitbatch.batch.job.investreturn; + +import lombok.RequiredArgsConstructor; + +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.batch.MyBatisPagingItemReader; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import woojooin.planitbatch.batch.writer.InvestMentWriter; +import woojooin.planitbatch.batch.writer.InvestMonthlyWriter; +import woojooin.planitbatch.domain.vo.InvestmentReturn; +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; +import org.springframework.beans.factory.annotation.Value; + +@Slf4j +@Configuration +@EnableBatchProcessing +@RequiredArgsConstructor +public class MonthlyBatch { + + private final JobBuilderFactory jobs; + private final StepBuilderFactory steps; + private final SqlSessionFactory sqlSessionFactory; + private final InvestMonthlyWriter writer; + + @Bean + @StepScope + public MyBatisPagingItemReader monthlyReturnReader( + @Value("#{jobParameters['today']}") String todayStr) + { + MyBatisPagingItemReader reader = new MyBatisPagingItemReader<>(); + reader.setSqlSessionFactory(sqlSessionFactory); + reader.setQueryId("woojooin.planitbatch.domain.mapper.InvestmentReturnMapper.findMonthlyReturn"); + log.info(" Reader 생성 - today={} " , todayStr ); + Map parameters = new HashMap<>(); + parameters.put("today", LocalDate.parse(todayStr)); + + reader.setParameterValues(parameters); + reader.setPageSize(100); + + return reader; + } + + @Bean + public Step monthlybatchStep(MyBatisPagingItemReader monthlyReturnReader) { + return steps.get("monthlybatchStep") + .chunk(100) + .reader(monthlyReturnReader) + .writer(writer) + .build(); + } + + @Bean + public Job monthlybatchJob(Step monthlybatchStep) { + return jobs.get("monthlybatchJob") + .start(monthlybatchStep) + .build(); + } + +} + diff --git a/src/main/java/woojooin/planitbatch/batch/job/investreturn/PastBatch.java b/src/main/java/woojooin/planitbatch/batch/job/investreturn/PastBatch.java new file mode 100644 index 0000000..a30b0d6 --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/job/investreturn/PastBatch.java @@ -0,0 +1,70 @@ +package woojooin.planitbatch.batch.job.investreturn; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.batch.MyBatisPagingItemReader; +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.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import woojooin.planitbatch.batch.writer.DailyReturnWriter; +import woojooin.planitbatch.domain.vo.DailyReturn; + +import java.time.LocalDate; +import java.util.HashMap; +import java.util.Map; + +@Configuration +@EnableBatchProcessing +@Slf4j +@RequiredArgsConstructor +public class PastBatch { + private final JobBuilderFactory jobs; + private final StepBuilderFactory steps; + private final DailyReturnWriter writer; + private final SqlSessionFactory sqlSessionFactory; + + + @Bean + @StepScope + public MyBatisPagingItemReader etfDailyReader( + @Value("#{jobParameters['startDate']}") String startDate, + @Value("#{jobParameters['endDate']}") String endDate) { + + MyBatisPagingItemReader reader = new MyBatisPagingItemReader<>(); + log.info("Reader 생성됨 - startDate={}, endDate={}", startDate, endDate); // 추가 로그 + reader.setSqlSessionFactory(sqlSessionFactory); + reader.setQueryId("woojooin.planitbatch.domain.mapper.DailyReturnMapper.findpast"); + + Map parameters = new HashMap<>(); + parameters.put("startDate", LocalDate.parse(startDate)); + parameters.put("endDate", LocalDate.parse(endDate)); + reader.setParameterValues(parameters); + reader.setPageSize(100); + + return reader; + } + + + @Bean + public Job etfDailyReturnJob(Step etfDailyReturnStep) { + return jobs.get("etfDailyReturnJob") + .start(etfDailyReturnStep) + .build(); + } + + @Bean + public Step etfDailyReturnStep(MyBatisPagingItemReader etfDailyReader) { + return steps.get("etfDailyReturnStep") + .chunk(100) + .reader(etfDailyReader) + .writer(writer) + .build(); + } +} diff --git a/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/PastScheduler.java b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/PastScheduler.java new file mode 100644 index 0000000..af6058c --- /dev/null +++ b/src/main/java/woojooin/planitbatch/batch/scheduler/investreturn/PastScheduler.java @@ -0,0 +1,44 @@ +package woojooin.planitbatch.batch.scheduler.investreturn; + +import lombok.RequiredArgsConstructor; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@Component +@RequiredArgsConstructor +public class PastScheduler { + + private final JobLauncher jobLauncher; + + @Qualifier("etfDailyReturnJob") + private final Job etfDailyReturnJob; + + + public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + +// @Scheduled(cron = "0 20 19 * * ?") // + public void runDailyReturnJob() throws Exception { + LocalDate startDate = LocalDate.of(2024, 8, 5); + LocalDate endDate = LocalDate.of(2025, 8, 5); + + for (LocalDate date = startDate; !date.isAfter(endDate); date = date.plusDays(1)) { + String dateStr = date.format(FORMATTER); + System.out.println("▶ " + dateStr + " 배치 시작"); + + JobParameters jobParameters = new JobParametersBuilder() + .addString("startDate", dateStr) + .addString("endDate", dateStr) // 하루 단위 + .addLong("time", System.currentTimeMillis()) // JobInstance 중복 방지 + .toJobParameters(); + + jobLauncher.run(etfDailyReturnJob, jobParameters); + } + } +} From 5418d1a57a83f67a07e27f490e2c17887e12714d Mon Sep 17 00:00:00 2001 From: kayoii <132435321+pgy8404@users.noreply.github.com> Date: Fri, 15 Aug 2025 21:25:30 +0900 Subject: [PATCH 8/8] .. --- .../planitbatch/batch/job/investreturn/DailyReturnBatchJob.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyReturnBatchJob.java b/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyReturnBatchJob.java index 1c7eece..a8b88ac 100644 --- a/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyReturnBatchJob.java +++ b/src/main/java/woojooin/planitbatch/batch/job/investreturn/DailyReturnBatchJob.java @@ -40,7 +40,6 @@ public MyBatisPagingItemReader reader( reader.setSqlSessionFactory(sqlSessionFactory); reader.setQueryId("woojooin.planitbatch.domain.mapper.DailyReturnMapper.findAllForToday"); - Map parameters = new HashMap<>(); parameters.put("date", LocalDate.parse(dateStr)); reader.setParameterValues(parameters);