From 3c08cffa363b6e9e39c024defefc2e949970561b Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 22 Jan 2026 14:36:55 +0900 Subject: [PATCH 01/12] =?UTF-8?q?UPLUS-126=20feat=20:=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EB=82=B4=EC=97=AD=20=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20?= =?UTF-8?q?=EC=9A=94=EA=B8=88=EC=A0=9C=20=EC=9D=B4=EB=A6=84=EB=8F=84=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/dto/UsageNotificationCandidate.java | 3 ++- .../model/dto/UsageNotificationOutboxRow.java | 11 +++++--- .../model/dto/UsageNotificationSource.java | 2 +- .../model/entity/UsageNotificationOutbox.java | 2 +- .../config/util/UsageNotificationPolicy.java | 5 ++-- .../UsageNotificationDailyReaderConfig.java | 14 +++++++--- .../UsageNotificationMonthlyReaderConfig.java | 26 ++++++++++++------- .../writer/UsageNotificationWriter.java | 5 ++-- 8 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationCandidate.java b/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationCandidate.java index 89c20b5..4f8e2f1 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationCandidate.java +++ b/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationCandidate.java @@ -4,7 +4,8 @@ public record UsageNotificationCandidate( Long subId, String period, String unit, + String planName, int threshold, - int percent, + double percent, long totalUsedMb, long allotmentMb) {} diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationOutboxRow.java b/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationOutboxRow.java index 3dd49c3..7796377 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationOutboxRow.java +++ b/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationOutboxRow.java @@ -1,11 +1,16 @@ package com.project.rdb.batch.model.dto; +import java.time.LocalDateTime; + public record UsageNotificationOutboxRow( Long id, Long subId, String period, - String unit, + String planName, int threshold, - int percent, + double percent, long totalUsedMb, - long allotmentMb) {} + long allotmentMb, + String phoneNumber, + String email, + LocalDateTime createdAt) {} diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationSource.java b/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationSource.java index 1c7754c..5c047a2 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationSource.java +++ b/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationSource.java @@ -1,4 +1,4 @@ package com.project.rdb.batch.model.dto; public record UsageNotificationSource( - Long subId, String period, String unit, long totalUsedBytes, long allotmentAmount) {} + Long subId, String period, String unit, String planName, long totalUsedBytes, long allotmentAmount) {} diff --git a/src/main/java/com/project/rdb/batch/model/entity/UsageNotificationOutbox.java b/src/main/java/com/project/rdb/batch/model/entity/UsageNotificationOutbox.java index 0aa412a..6904aae 100644 --- a/src/main/java/com/project/rdb/batch/model/entity/UsageNotificationOutbox.java +++ b/src/main/java/com/project/rdb/batch/model/entity/UsageNotificationOutbox.java @@ -38,7 +38,7 @@ public class UsageNotificationOutbox { private int threshold; @Column(nullable = false) - private int percent; + private double percent; @Column(name = "total_used_mb", nullable = false) private Long totalUsedMb; diff --git a/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java b/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java index d238204..ec130c7 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java @@ -18,15 +18,16 @@ public Optional evaluate(UsageNotificationSource sou long totalUsedMb = source.totalUsedBytes() / (1024 * 1024); long allotmentMb = source.allotmentAmount(); - int percent = (int) ((totalUsedMb * 100L) / allotmentMb); + double percent = (double) (totalUsedMb * 100L) / allotmentMb; - return decideThreshold(percent) + return decideThreshold((int) percent) .map( threshold -> new UsageNotificationCandidate( source.subId(), source.period(), source.unit(), + source.planName(), threshold, percent, totalUsedMb, diff --git a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java index 60f4cf8..f951f87 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java @@ -17,7 +17,9 @@ public class UsageNotificationDailyReaderConfig { @Bean(name = "usageNotificationDailyReader") @StepScope public JdbcCursorItemReader usageNotificationDailyReader( - DataSource dataSource, @Value("#{jobParameters['usageDate']}") String usageDate) { + DataSource dataSource, + @Value("#{jobParameters['fromTime']}") String fromTime, + @Value("#{jobParameters['toTime']}") String toTime) { String sql = """ @@ -25,12 +27,14 @@ public JdbcCursorItemReader usageNotificationDailyReade usd.sub_id, usd.usage_date AS period, 'DAY' AS unit, + sp.plan_name, usd.total_used_bytes, sp.allotment_amount FROM usage_summary_daily usd JOIN subscription_plan sp ON sp.sub_id = usd.sub_id - WHERE usd.usage_date = ? + WHERE usd.updated_at >= ?::timestamp + AND usd.updated_at < ?::timestamp AND sp.allotment_amount = 5120 ORDER BY usd.sub_id """; @@ -40,13 +44,17 @@ public JdbcCursorItemReader usageNotificationDailyReade .dataSource(dataSource) .sql(sql) .fetchSize(1000) - .preparedStatementSetter(ps -> ps.setString(1, usageDate)) + .preparedStatementSetter(ps -> { + ps.setObject(1, fromTime); + ps.setObject(2, toTime); + }) .rowMapper( (rs, rowNum) -> new UsageNotificationSource( rs.getLong("sub_id"), rs.getString("period"), rs.getString("unit"), + rs.getString("plan_name"), rs.getLong("total_used_bytes"), rs.getLong("allotment_amount"))) .build(); diff --git a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java index d87778e..a7ba18a 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java @@ -17,23 +17,27 @@ public class UsageNotificationMonthlyReaderConfig { @Bean(name = "usageNotificationMonthlyReader") @StepScope public JdbcCursorItemReader usageNotificationMonthlyReader( - DataSource dataSource, @Value("#{jobParameters['period']}") String period) { + DataSource dataSource, + @Value("#{jobParameters['fromTime']}") String fromTime, + @Value("#{jobParameters['toTime']}") String toTime) { String sql = """ SELECT - us.sub_id, - us.period, + usm.sub_id, + usm.period, 'MONTH' AS unit, - us.total_used_bytes, + sp.plan_name, + usm.total_used_bytes, sp.allotment_amount - FROM usage_summary_monthly us + FROM usage_summary_monthly usm JOIN subscription_plan sp - ON sp.sub_id = us.sub_id - WHERE us.period = ? + ON sp.sub_id = usm.sub_id + WHERE usm.updated_at >= ?::timestamp + AND usm.updated_at < ?::timestamp AND sp.allotment_amount > 0 AND sp.allotment_amount != 5120 - ORDER BY us.sub_id + ORDER BY usm.sub_id """; return new JdbcCursorItemReaderBuilder() @@ -41,13 +45,17 @@ public JdbcCursorItemReader usageNotificationMonthlyRea .dataSource(dataSource) .sql(sql) .fetchSize(1000) - .preparedStatementSetter(ps -> ps.setString(1, period)) + .preparedStatementSetter(ps -> { + ps.setObject(1, fromTime); + ps.setObject(2, toTime); + }) .rowMapper( (rs, rowNum) -> new UsageNotificationSource( rs.getLong("sub_id"), rs.getString("period"), rs.getString("unit"), + rs.getString("plan_name"), rs.getLong("total_used_bytes"), rs.getLong("allotment_amount"))) .build(); diff --git a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java index 5a980ab..1ef2295 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java @@ -31,9 +31,9 @@ public void write(Chunk chunk) { String sql = """ INSERT INTO usage_notification_outbox - (sub_id, period, unit, threshold, percent, total_used_mb, allotment_mb, status, created_at) + (sub_id, period, plan_name, unit, threshold, percent, total_used_mb, allotment_mb, status, created_at) VALUES - (:subId, :period, :unit, :threshold, :percent, :totalUsedMb, :allotmentMb, 'PENDING', NOW()) + (:subId, :period, :planName, :unit, :threshold, :percent, :totalUsedMb, :allotmentMb, 'PENDING', NOW()) ON CONFLICT (sub_id, period, unit, threshold) DO NOTHING """; @@ -45,6 +45,7 @@ ON CONFLICT (sub_id, period, unit, threshold) Map map = new HashMap<>(); map.put("subId", c.subId()); map.put("period", c.period()); + map.put("planName", c.planName()); map.put("unit", c.unit()); map.put("threshold", c.threshold()); map.put("percent", c.percent()); From a4c40523141abd763555ce82a59623b4542f0c50 Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 22 Jan 2026 14:37:50 +0900 Subject: [PATCH 02/12] =?UTF-8?q?UPLUS-126=20feat=20:=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=EC=97=90=20=EC=9A=94=EA=B8=88=EC=A0=9C=20=EC=9D=B4=EB=A6=84,?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=A9=94=EC=9D=BC,?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A0=84=ED=99=94=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/model/dto/NotificationMessage.java | 13 ++++++ .../batch/model/dto/NotificationSendTask.java | 2 +- .../config/NotificationSendJobConfig.java | 4 +- .../processor/NotificationSendProcessor.java | 32 ++++++++++----- .../reader/NotificationSendReaderConfig.java | 40 ++++++++++++------- .../writer/NotificationSendWriter.java | 15 ++++--- .../consumer/NotificationSendConsumer.java | 8 ++-- 7 files changed, 77 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/project/rdb/batch/model/dto/NotificationMessage.java diff --git a/src/main/java/com/project/rdb/batch/model/dto/NotificationMessage.java b/src/main/java/com/project/rdb/batch/model/dto/NotificationMessage.java new file mode 100644 index 0000000..6b91e83 --- /dev/null +++ b/src/main/java/com/project/rdb/batch/model/dto/NotificationMessage.java @@ -0,0 +1,13 @@ +package com.project.rdb.batch.model.dto; + +import java.util.Map; +import java.util.UUID; + +public record NotificationMessage( + UUID eventId, + Long id, + Long templateGroupId, + Map subscriptionInfo, + Map variables +) { +} diff --git a/src/main/java/com/project/rdb/batch/model/dto/NotificationSendTask.java b/src/main/java/com/project/rdb/batch/model/dto/NotificationSendTask.java index c4a6e6b..b9927bb 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/NotificationSendTask.java +++ b/src/main/java/com/project/rdb/batch/model/dto/NotificationSendTask.java @@ -5,4 +5,4 @@ import org.springframework.kafka.support.SendResult; public record NotificationSendTask( - UsageNotificationEvent event, CompletableFuture> future) {} + NotificationMessage event, CompletableFuture> future) {} diff --git a/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java b/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java index 0c735c1..e3f5d19 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java @@ -1,5 +1,6 @@ package com.project.rdb.batch.notificationsend.config; +import com.project.rdb.batch.model.dto.NotificationMessage; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobScope; @@ -12,7 +13,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; -import com.project.rdb.batch.model.dto.UsageNotificationEvent; import com.project.rdb.batch.model.dto.UsageNotificationOutboxRow; import com.project.rdb.batch.notificationsend.processor.NotificationSendProcessor; import com.project.rdb.batch.notificationsend.writer.NotificationSendWriter; @@ -44,7 +44,7 @@ public Job notificationSendJob() { @Bean public Step notificationSendStep() { return new StepBuilder("notificationSendStep", jobRepository) - .chunk(CHUNK_SIZE, txManager) + .chunk(CHUNK_SIZE, txManager) .reader(outboxReader) .processor(notificationSendProcessor) .writer(notificationSendWriter) diff --git a/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java b/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java index 69a5b6d..a82809c 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java @@ -1,11 +1,12 @@ package com.project.rdb.batch.notificationsend.processor; +import java.util.Map; import java.util.UUID; +import com.project.rdb.batch.model.dto.NotificationMessage; import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageNotificationEvent; import com.project.rdb.batch.model.dto.UsageNotificationOutboxRow; import lombok.extern.slf4j.Slf4j; @@ -13,20 +14,29 @@ @Component @Slf4j public class NotificationSendProcessor - implements ItemProcessor { + implements ItemProcessor { @Override - public UsageNotificationEvent process(UsageNotificationOutboxRow item) { + public NotificationMessage process(UsageNotificationOutboxRow item) { - return new UsageNotificationEvent( + return new NotificationMessage( UUID.randomUUID(), item.id(), - item.subId(), - item.period(), - item.unit(), - item.threshold(), - item.percent(), - item.totalUsedMb(), - item.allotmentMb()); + 101L, + Map.of( + "subId", item.subId(), + "phoneNumber", item.phoneNumber(), + "email", item.email()), + Map.of( + "period", item.period(), + "threshold", item.threshold(), + "percent", item.percent(), + "totalUsedMb", item.totalUsedMb(), + "allotmentMb", item.allotmentMb(), + "phoneNumber", item.phoneNumber(), + "email", item.email(), + "createdAt", item.createdAt() + ) + ); } } diff --git a/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java b/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java index d1bcdb6..d4422a9 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java @@ -9,6 +9,8 @@ import com.project.rdb.batch.model.dto.UsageNotificationOutboxRow; +import java.time.LocalDateTime; + @Configuration public class NotificationSendReaderConfig { @@ -19,17 +21,24 @@ public JdbcCursorItemReader notificationSendOutboxRe String sql = """ SELECT - id, - sub_id, - period, - unit, - threshold, - percent, - total_used_mb, - allotment_mb - FROM usage_notification_outbox - WHERE status = 'PENDING' - ORDER BY id + o.id, + o.sub_id, + o.period, + o.plan_name, + o.threshold, + o.percent, + o.total_used_mb, + o.allotment_mb, + s.phone_number, + c.email_enc, + o.created_at + FROM usage_notification_outbox o + JOIN subscription s + ON s.sub_id = o.sub_id + JOIN customer c + ON c.customer_id = s.customer_id + WHERE o.status = 'PENDING' + ORDER BY o.id """; return new JdbcCursorItemReaderBuilder() @@ -43,11 +52,14 @@ public JdbcCursorItemReader notificationSendOutboxRe rs.getLong("id"), rs.getLong("sub_id"), rs.getString("period"), - rs.getString("unit"), + rs.getString("plan_name"), rs.getInt("threshold"), - rs.getInt("percent"), + rs.getDouble("percent"), rs.getLong("total_used_mb"), - rs.getLong("allotment_mb"))) + rs.getLong("allotment_mb"), + rs.getString("phone_number"), + rs.getString("email_enc"), + rs.getTimestamp("created_at").toLocalDateTime())) .build(); } } diff --git a/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java b/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java index 0258adb..f7f2527 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; +import com.project.rdb.batch.model.dto.NotificationMessage; import org.springframework.batch.item.Chunk; import org.springframework.batch.item.ItemWriter; import org.springframework.kafka.core.KafkaTemplate; @@ -14,14 +15,13 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.project.rdb.batch.model.dto.NotificationSendTask; -import com.project.rdb.batch.model.dto.UsageNotificationEvent; import com.project.rdb.batch.model.repository.UsageNotificationOutboxRepository; import lombok.RequiredArgsConstructor; @Component @RequiredArgsConstructor -public class NotificationSendWriter implements ItemWriter { +public class NotificationSendWriter implements ItemWriter { private final KafkaTemplate kafkaTemplate; private final UsageNotificationOutboxRepository repository; @@ -29,10 +29,10 @@ public class NotificationSendWriter implements ItemWriter items) { + public void write(Chunk items) { // usageNotificationOutboxId 추출 - List ids = items.getItems().stream().map(UsageNotificationEvent::id).toList(); + List ids = items.getItems().stream().map(NotificationMessage::id).toList(); // PROCESSING Status 먼저 DB 반영 repository.markProcessing(ids); @@ -42,12 +42,15 @@ public void write(Chunk items) { Map failedReasons = new HashMap<>(); - for (UsageNotificationEvent event : items) { + for (NotificationMessage event : items) { try { String payload = objectMapper.writeValueAsString(event); + String key = String.valueOf( + event.subscriptionInfo().get("subId") + ); CompletableFuture> future = - kafkaTemplate.send("notification-usage", event.subId().toString(), payload); + kafkaTemplate.send("noti-tp", key, payload); tasks.add(new NotificationSendTask(event, future)); } catch (Exception e) { diff --git a/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java b/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java index a613d55..40349b3 100644 --- a/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java +++ b/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java @@ -1,12 +1,13 @@ package com.project.rdb.kafka.consumer; +import com.project.rdb.batch.model.dto.NotificationMessage; import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.context.annotation.Profile; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.kafka.support.Acknowledgment; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; -import com.project.rdb.batch.model.dto.UsageNotificationEvent; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -22,12 +23,13 @@ public class NotificationSendConsumer { @KafkaListener( topics = "notification-usage", containerFactory = "kafkaListenerContainerFactory") + @Profile("notification-worker") public void consume(ConsumerRecord record, Acknowledgment ack) { log.info("🔥 CONSUME START offset={}, value={}", record.offset(), record.value()); try { - UsageNotificationEvent event = - objectMapper.readValue(record.value(), UsageNotificationEvent.class); + NotificationMessage event = + objectMapper.readValue(record.value(), NotificationMessage.class); String eventId = event.eventId().toString(); From 799c84bbedb846140477cec03a6dda8b3422e4b5 Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 22 Jan 2026 14:38:23 +0900 Subject: [PATCH 03/12] =?UTF-8?q?UPLUS-126=20fix=20:=20SQL=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/project/rdb/batch/usageaggregate/config/Sqls.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/config/Sqls.java b/src/main/java/com/project/rdb/batch/usageaggregate/config/Sqls.java index 9a0bbc3..9ba6505 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/config/Sqls.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/config/Sqls.java @@ -10,7 +10,7 @@ INSERT INTO usage_summary_daily ( sub_id, usage_date, total_used_bytes, updated_at ) VALUES ( - :subId, :period, :delta, NOW() + :subId, :usageDate, :delta, NOW() ) ON CONFLICT (sub_id, usage_date) DO UPDATE SET From 65b403abe998e772ea6d22ce40338cbea8f749d6 Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 22 Jan 2026 14:38:35 +0900 Subject: [PATCH 04/12] =?UTF-8?q?UPLUS-126=20fix=20:=20=EC=95=88=EC=93=B0?= =?UTF-8?q?=EB=8A=94=20Record=20=ED=8C=8C=EC=9D=BC=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../batch/model/dto/UsageNotificationEvent.java | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src/main/java/com/project/rdb/batch/model/dto/UsageNotificationEvent.java diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationEvent.java b/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationEvent.java deleted file mode 100644 index 15fd16b..0000000 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationEvent.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.project.rdb.batch.model.dto; - -import java.util.UUID; - -public record UsageNotificationEvent( - UUID eventId, - Long id, - Long subId, - String period, - String unit, - int threshold, - int percent, - long totalUsedMb, - long allotmentMb) {} From d82ca854bd4f1e495a4ee5dbf7d4770958cfe33f Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 22 Jan 2026 14:45:10 +0900 Subject: [PATCH 05/12] =?UTF-8?q?UPLUS-126=20fix=20:=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/repository/UsageNotificationOutboxRepository.java | 3 +-- .../notificationsend/config/NotificationSendJobConfig.java | 4 ++-- .../dto/NotificationMessage.java | 2 +- .../dto/NotificationSendTask.java | 2 +- .../processor/NotificationSendProcessor.java | 4 ++-- .../reader/NotificationSendReaderConfig.java | 4 +--- .../notificationsend/writer/NotificationSendWriter.java | 4 ++-- .../usageaggregate/config/UsageAggregationJobConfig.java | 6 +++--- .../dto/UsageDailyAggregation.java | 2 +- .../batch/{model => usageaggregate}/dto/UsageDailyKey.java | 2 +- .../batch/{model => usageaggregate}/dto/UsageLogRow.java | 2 +- .../dto/UsageMonthlyAggregation.java | 2 +- .../{model => usageaggregate}/dto/UsageMonthlyKey.java | 2 +- .../batch/usageaggregate/processor/UsageDailyProcessor.java | 4 ++-- .../usageaggregate/processor/UsageMonthlyProcessor.java | 4 ++-- .../usageaggregate/reader/UsageLogDailyReaderConfig.java | 2 +- .../usageaggregate/reader/UsageLogMonthlyReaderConfig.java | 2 +- .../usageaggregate/writer/UsageSummaryDailyWriter.java | 4 ++-- .../usageaggregate/writer/UsageSummaryMonthlyWriter.java | 4 ++-- .../config/UsageNotificationJobConfig.java | 4 ++-- .../config/util/UsageNotificationPolicy.java | 4 ++-- .../dto/UsageNotificationCandidate.java | 2 +- .../dto/UsageNotificationOutboxRow.java | 2 +- .../dto/UsageNotificationSource.java | 2 +- .../processor/UsageNotificationProcessor.java | 4 ++-- .../reader/UsageNotificationDailyReaderConfig.java | 2 +- .../reader/UsageNotificationMonthlyReaderConfig.java | 2 +- .../usagenotification/writer/UsageNotificationWriter.java | 2 +- .../rdb/kafka/consumer/NotificationSendConsumer.java | 2 +- 29 files changed, 41 insertions(+), 44 deletions(-) rename src/main/java/com/project/rdb/batch/{model => notificationsend}/dto/NotificationMessage.java (82%) rename src/main/java/com/project/rdb/batch/{model => notificationsend}/dto/NotificationSendTask.java (81%) rename src/main/java/com/project/rdb/batch/{model => usageaggregate}/dto/UsageDailyAggregation.java (63%) rename src/main/java/com/project/rdb/batch/{model => usageaggregate}/dto/UsageDailyKey.java (88%) rename src/main/java/com/project/rdb/batch/{model => usageaggregate}/dto/UsageLogRow.java (69%) rename src/main/java/com/project/rdb/batch/{model => usageaggregate}/dto/UsageMonthlyAggregation.java (63%) rename src/main/java/com/project/rdb/batch/{model => usageaggregate}/dto/UsageMonthlyKey.java (88%) rename src/main/java/com/project/rdb/batch/{model => usagenotification}/dto/UsageNotificationCandidate.java (81%) rename src/main/java/com/project/rdb/batch/{model => usagenotification}/dto/UsageNotificationOutboxRow.java (86%) rename src/main/java/com/project/rdb/batch/{model => usagenotification}/dto/UsageNotificationSource.java (74%) diff --git a/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java b/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java index c881e3f..0b200a4 100644 --- a/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java +++ b/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java @@ -24,8 +24,7 @@ public void markProcessing(List ids) { public void markSent(List ids) { jdbcTemplate.batchUpdate( - "UPDATE usage_notification_outbox SET status = 'SENT', sent_at = now() WHERE id =" - + " ?", + "UPDATE usage_notification_outbox SET status = 'SENT', sent_at = now() WHERE id = ?", ids, ids.size(), (ps, id) -> ps.setLong(1, id)); diff --git a/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java b/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java index e3f5d19..2fdcef5 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java @@ -1,6 +1,6 @@ package com.project.rdb.batch.notificationsend.config; -import com.project.rdb.batch.model.dto.NotificationMessage; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobScope; @@ -13,7 +13,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; -import com.project.rdb.batch.model.dto.UsageNotificationOutboxRow; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationOutboxRow; import com.project.rdb.batch.notificationsend.processor.NotificationSendProcessor; import com.project.rdb.batch.notificationsend.writer.NotificationSendWriter; diff --git a/src/main/java/com/project/rdb/batch/model/dto/NotificationMessage.java b/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationMessage.java similarity index 82% rename from src/main/java/com/project/rdb/batch/model/dto/NotificationMessage.java rename to src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationMessage.java index 6b91e83..ebde229 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/NotificationMessage.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationMessage.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.notificationsend.dto; import java.util.Map; import java.util.UUID; diff --git a/src/main/java/com/project/rdb/batch/model/dto/NotificationSendTask.java b/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationSendTask.java similarity index 81% rename from src/main/java/com/project/rdb/batch/model/dto/NotificationSendTask.java rename to src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationSendTask.java index b9927bb..168c8d6 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/NotificationSendTask.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationSendTask.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.notificationsend.dto; import java.util.concurrent.CompletableFuture; diff --git a/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java b/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java index a82809c..6e27d98 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java @@ -3,11 +3,11 @@ import java.util.Map; import java.util.UUID; -import com.project.rdb.batch.model.dto.NotificationMessage; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageNotificationOutboxRow; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationOutboxRow; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java b/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java index d4422a9..141e1f8 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/reader/NotificationSendReaderConfig.java @@ -7,9 +7,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.project.rdb.batch.model.dto.UsageNotificationOutboxRow; - -import java.time.LocalDateTime; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationOutboxRow; @Configuration public class NotificationSendReaderConfig { diff --git a/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java b/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java index f7f2527..9822484 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java @@ -6,7 +6,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; -import com.project.rdb.batch.model.dto.NotificationMessage; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.springframework.batch.item.Chunk; import org.springframework.batch.item.ItemWriter; import org.springframework.kafka.core.KafkaTemplate; @@ -14,7 +14,7 @@ import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; -import com.project.rdb.batch.model.dto.NotificationSendTask; +import com.project.rdb.batch.notificationsend.dto.NotificationSendTask; import com.project.rdb.batch.model.repository.UsageNotificationOutboxRepository; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/config/UsageAggregationJobConfig.java b/src/main/java/com/project/rdb/batch/usageaggregate/config/UsageAggregationJobConfig.java index 915b707..8e7cb2b 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/config/UsageAggregationJobConfig.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/config/UsageAggregationJobConfig.java @@ -14,9 +14,9 @@ import org.springframework.transaction.PlatformTransactionManager; import com.project.rdb.batch.model.BatchStepMetricsListener; -import com.project.rdb.batch.model.dto.UsageDailyAggregation; -import com.project.rdb.batch.model.dto.UsageLogRow; -import com.project.rdb.batch.model.dto.UsageMonthlyAggregation; +import com.project.rdb.batch.usageaggregate.dto.UsageDailyAggregation; +import com.project.rdb.batch.usageaggregate.dto.UsageLogRow; +import com.project.rdb.batch.usageaggregate.dto.UsageMonthlyAggregation; import com.project.rdb.batch.usageaggregate.processor.UsageDailyProcessor; import com.project.rdb.batch.usageaggregate.processor.UsageMonthlyProcessor; import com.project.rdb.batch.usageaggregate.writer.UsageSummaryDailyWriter; diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageDailyAggregation.java b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageDailyAggregation.java similarity index 63% rename from src/main/java/com/project/rdb/batch/model/dto/UsageDailyAggregation.java rename to src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageDailyAggregation.java index af7697c..200ee75 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageDailyAggregation.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageDailyAggregation.java @@ -1,3 +1,3 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usageaggregate.dto; public record UsageDailyAggregation(Long subId, String usageDate, long deltaBytes) {} diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageDailyKey.java b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageDailyKey.java similarity index 88% rename from src/main/java/com/project/rdb/batch/model/dto/UsageDailyKey.java rename to src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageDailyKey.java index 38de2ff..4c84c1e 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageDailyKey.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageDailyKey.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usageaggregate.dto; import java.util.Objects; diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageLogRow.java b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageLogRow.java similarity index 69% rename from src/main/java/com/project/rdb/batch/model/dto/UsageLogRow.java rename to src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageLogRow.java index a45ea7d..10541e3 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageLogRow.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageLogRow.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usageaggregate.dto; import java.time.LocalDateTime; diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageMonthlyAggregation.java b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageMonthlyAggregation.java similarity index 63% rename from src/main/java/com/project/rdb/batch/model/dto/UsageMonthlyAggregation.java rename to src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageMonthlyAggregation.java index e63dd9a..22d4387 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageMonthlyAggregation.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageMonthlyAggregation.java @@ -1,3 +1,3 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usageaggregate.dto; public record UsageMonthlyAggregation(Long subId, String period, long deltaBytes) {} diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageMonthlyKey.java b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageMonthlyKey.java similarity index 88% rename from src/main/java/com/project/rdb/batch/model/dto/UsageMonthlyKey.java rename to src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageMonthlyKey.java index 8089e34..bf5be6f 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageMonthlyKey.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/dto/UsageMonthlyKey.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usageaggregate.dto; import java.util.Objects; diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageDailyProcessor.java b/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageDailyProcessor.java index b7a1b08..dd8f233 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageDailyProcessor.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageDailyProcessor.java @@ -5,8 +5,8 @@ import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageDailyAggregation; -import com.project.rdb.batch.model.dto.UsageLogRow; +import com.project.rdb.batch.usageaggregate.dto.UsageDailyAggregation; +import com.project.rdb.batch.usageaggregate.dto.UsageLogRow; @Component public class UsageDailyProcessor implements ItemProcessor { diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageMonthlyProcessor.java b/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageMonthlyProcessor.java index 0af4e1e..d2ab8fa 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageMonthlyProcessor.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/processor/UsageMonthlyProcessor.java @@ -5,8 +5,8 @@ import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageLogRow; -import com.project.rdb.batch.model.dto.UsageMonthlyAggregation; +import com.project.rdb.batch.usageaggregate.dto.UsageLogRow; +import com.project.rdb.batch.usageaggregate.dto.UsageMonthlyAggregation; @Component public class UsageMonthlyProcessor implements ItemProcessor { diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogDailyReaderConfig.java b/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogDailyReaderConfig.java index 7c7a889..fcb1eec 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogDailyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogDailyReaderConfig.java @@ -11,7 +11,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.project.rdb.batch.model.dto.UsageLogRow; +import com.project.rdb.batch.usageaggregate.dto.UsageLogRow; @Configuration public class UsageLogDailyReaderConfig { diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogMonthlyReaderConfig.java b/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogMonthlyReaderConfig.java index 306a885..fa0c5c0 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogMonthlyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/reader/UsageLogMonthlyReaderConfig.java @@ -11,7 +11,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.project.rdb.batch.model.dto.UsageLogRow; +import com.project.rdb.batch.usageaggregate.dto.UsageLogRow; @Configuration public class UsageLogMonthlyReaderConfig { diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java index d291f0f..bdacca1 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java @@ -9,8 +9,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageDailyAggregation; -import com.project.rdb.batch.model.dto.UsageDailyKey; +import com.project.rdb.batch.usageaggregate.dto.UsageDailyAggregation; +import com.project.rdb.batch.usageaggregate.dto.UsageDailyKey; import com.project.rdb.batch.usageaggregate.config.Sqls; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java index e931e3b..3b01aca 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java @@ -9,8 +9,8 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageMonthlyAggregation; -import com.project.rdb.batch.model.dto.UsageMonthlyKey; +import com.project.rdb.batch.usageaggregate.dto.UsageMonthlyAggregation; +import com.project.rdb.batch.usageaggregate.dto.UsageMonthlyKey; import com.project.rdb.batch.usageaggregate.config.Sqls; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/batch/usagenotification/config/UsageNotificationJobConfig.java b/src/main/java/com/project/rdb/batch/usagenotification/config/UsageNotificationJobConfig.java index 7e3a8af..dfe5eda 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/config/UsageNotificationJobConfig.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/config/UsageNotificationJobConfig.java @@ -14,8 +14,8 @@ import org.springframework.transaction.PlatformTransactionManager; import com.project.rdb.batch.model.BatchStepMetricsListener; -import com.project.rdb.batch.model.dto.UsageNotificationCandidate; -import com.project.rdb.batch.model.dto.UsageNotificationSource; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationCandidate; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationSource; import com.project.rdb.batch.usagenotification.processor.UsageNotificationProcessor; import com.project.rdb.batch.usagenotification.writer.UsageNotificationWriter; diff --git a/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java b/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java index ec130c7..75b044d 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/config/util/UsageNotificationPolicy.java @@ -4,8 +4,8 @@ import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageNotificationCandidate; -import com.project.rdb.batch.model.dto.UsageNotificationSource; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationCandidate; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationSource; @Component public class UsageNotificationPolicy { diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationCandidate.java b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationCandidate.java similarity index 81% rename from src/main/java/com/project/rdb/batch/model/dto/UsageNotificationCandidate.java rename to src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationCandidate.java index 4f8e2f1..fea5d23 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationCandidate.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationCandidate.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usagenotification.dto; public record UsageNotificationCandidate( Long subId, diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationOutboxRow.java b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationOutboxRow.java similarity index 86% rename from src/main/java/com/project/rdb/batch/model/dto/UsageNotificationOutboxRow.java rename to src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationOutboxRow.java index 7796377..2d81818 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationOutboxRow.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationOutboxRow.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usagenotification.dto; import java.time.LocalDateTime; diff --git a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationSource.java b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationSource.java similarity index 74% rename from src/main/java/com/project/rdb/batch/model/dto/UsageNotificationSource.java rename to src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationSource.java index 5c047a2..d119f1f 100644 --- a/src/main/java/com/project/rdb/batch/model/dto/UsageNotificationSource.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationSource.java @@ -1,4 +1,4 @@ -package com.project.rdb.batch.model.dto; +package com.project.rdb.batch.usagenotification.dto; public record UsageNotificationSource( Long subId, String period, String unit, String planName, long totalUsedBytes, long allotmentAmount) {} diff --git a/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java b/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java index d13f601..94544f5 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java @@ -3,8 +3,8 @@ import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; -import com.project.rdb.batch.model.dto.UsageNotificationCandidate; -import com.project.rdb.batch.model.dto.UsageNotificationSource; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationCandidate; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationSource; import com.project.rdb.batch.usagenotification.config.util.UsageNotificationPolicy; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java index f951f87..858393f 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java @@ -9,7 +9,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.project.rdb.batch.model.dto.UsageNotificationSource; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationSource; @Configuration public class UsageNotificationDailyReaderConfig { diff --git a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java index a7ba18a..6d4d3d2 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java @@ -9,7 +9,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import com.project.rdb.batch.model.dto.UsageNotificationSource; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationSource; @Configuration public class UsageNotificationMonthlyReaderConfig { diff --git a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java index 1ef2295..ff9e27d 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java @@ -10,7 +10,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import com.project.rdb.batch.model.dto.UsageNotificationCandidate; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationCandidate; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java b/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java index 40349b3..17e5509 100644 --- a/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java +++ b/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java @@ -1,6 +1,6 @@ package com.project.rdb.kafka.consumer; -import com.project.rdb.batch.model.dto.NotificationMessage; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.context.annotation.Profile; import org.springframework.kafka.annotation.KafkaListener; From 834fc4187455b87254c164d17f9c706c89e8e082 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 23 Jan 2026 10:00:43 +0900 Subject: [PATCH 06/12] =?UTF-8?q?UPLUS-126=20fix=20:=20Batch=20Orchestra?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../code/domain/GlobalErrorCode.java | 3 +- .../java/com/project/rdb/BatchJobRunner.java | 104 +++++++++--------- .../orchestrator/BatchTimeWindowService.java | 41 +++++++ .../batch/orchestrator/UsageOrchestrator.java | 88 +++++++++++++++ .../batch/orchestrator/dto/TimeWindow.java | 5 + .../UsageNotificationDailyReaderConfig.java | 19 ++-- .../UsageNotificationMonthlyReaderConfig.java | 19 ++-- .../consumer/NotificationSendConsumer.java | 5 +- 8 files changed, 210 insertions(+), 74 deletions(-) create mode 100644 src/main/java/com/project/rdb/batch/orchestrator/BatchTimeWindowService.java create mode 100644 src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java create mode 100644 src/main/java/com/project/rdb/batch/orchestrator/dto/TimeWindow.java diff --git a/src/main/java/com/project/global/exception/code/domain/GlobalErrorCode.java b/src/main/java/com/project/global/exception/code/domain/GlobalErrorCode.java index 8bba73c..0c006b9 100644 --- a/src/main/java/com/project/global/exception/code/domain/GlobalErrorCode.java +++ b/src/main/java/com/project/global/exception/code/domain/GlobalErrorCode.java @@ -24,7 +24,8 @@ public enum GlobalErrorCode implements BaseErrorCode { HttpStatus.BAD_REQUEST, "COMMON_011", "UsageNotification Produce 과정에서 에러가 발생하였습니다"), USAGE_OUTBOX_WRITER_FAILED( HttpStatus.BAD_REQUEST, "COMMON_012", "UsageOutbox Writer 배치 시스템이 실패하였습니다"), - PLAN_NOT_VALID(HttpStatus.BAD_REQUEST, "COMMON_013", "존재하지 않는 요금제입니다"); + PLAN_NOT_VALID(HttpStatus.BAD_REQUEST, "COMMON_013", "존재하지 않는 요금제입니다"), + BATCH_NOT_FINISHED(HttpStatus.BAD_REQUEST, "COMMON_014", "이전 배치 작업이 끝나지 않았습니다"); private final HttpStatus httpStatus; private final String customCode; diff --git a/src/main/java/com/project/rdb/BatchJobRunner.java b/src/main/java/com/project/rdb/BatchJobRunner.java index ba14d2f..0212b2e 100644 --- a/src/main/java/com/project/rdb/BatchJobRunner.java +++ b/src/main/java/com/project/rdb/BatchJobRunner.java @@ -1,55 +1,49 @@ -// package com.project.rdb; -// -// 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.boot.CommandLineRunner; -// import org.springframework.stereotype.Component; -// -// import lombok.RequiredArgsConstructor; -// -// @Component -// @RequiredArgsConstructor -// public class BatchJobRunner implements CommandLineRunner { -// -// private final JobLauncher jobLauncher; -// private final Job usageAggregationJob; -// private final Job usageNotificationJob; -// private final Job notificationSendJob; -// -// ////// @Override -// ////// public void run(String... args) throws Exception { -// ////// JobParameters params = new JobParametersBuilder() -// ////// .addString("fromTime", "2025-12-01T00:00:00") -// ////// .addString("toTime", "2025-12-31T11:59:59") -// ////// .addLong("run.id", System.currentTimeMillis()) -// ////// .toJobParameters(); -// ////// -// ////// jobLauncher.run(usageAggregationJob, params); -// ////// } -// //// -// ////// @Override -// ////// public void run(String... args) throws Exception { -// ////// -// ////// JobParameters params = new JobParametersBuilder() -// ////// // 월 요금제 Step용 -// ////// .addString("period", "202512") -// ////// // 일 요금제 Step용 -// ////// .addString("usageDate", "20251201") -// ////// .addLong("run.id", System.currentTimeMillis()) -// ////// .toJobParameters(); -// ////// -// ////// jobLauncher.run(usageNotificationJob, params); -// ////// } -// // -// @Override -// public void run(String... args) throws Exception { -// JobParameters params = -// new JobParametersBuilder() -// .addLong("runAt", System.currentTimeMillis()) // 중복 실행 방지 -// .toJobParameters(); -// -// jobLauncher.run(notificationSendJob, params); -// } -// } +package com.project.rdb; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.project.rdb.batch.orchestrator.UsageOrchestrator; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class BatchJobRunner implements CommandLineRunner { + + private final JobLauncher jobLauncher; + private final Job usageAggregationJob; + private final Job usageNotificationJob; + private final Job notificationSendJob; + private final UsageOrchestrator usageOrchestrator; + + @Override + public void run(String... args) throws Exception { + usageOrchestrator.run(); + } + + // @Override + // public void run(String... args) throws Exception { + // + // JobParameters params = new JobParametersBuilder() + // // 월 요금제 Step용 + // .addString("fromTime", "2026-01-22T00:00:00") + // .addString("toTime", "2026-01-22T23:59:59") + // .addLong("run.id", System.currentTimeMillis()) + // .toJobParameters(); + // + // jobLauncher.run(usageNotificationJob, params); + // } + // // + // @Override + // public void run(String... args) throws Exception { + // JobParameters params = + // new JobParametersBuilder() + // .addLong("runAt", System.currentTimeMillis()) // 중복 실행 방지 + // .toJobParameters(); + // + // jobLauncher.run(notificationSendJob, params); + // } +} diff --git a/src/main/java/com/project/rdb/batch/orchestrator/BatchTimeWindowService.java b/src/main/java/com/project/rdb/batch/orchestrator/BatchTimeWindowService.java new file mode 100644 index 0000000..1bf5312 --- /dev/null +++ b/src/main/java/com/project/rdb/batch/orchestrator/BatchTimeWindowService.java @@ -0,0 +1,41 @@ +package com.project.rdb.batch.orchestrator; + +import java.time.LocalDateTime; +import java.util.Map; + +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class BatchTimeWindowService { + + private final NamedParameterJdbcTemplate jdbc; + + public LocalDateTime resolve(String jobName) { + return jdbc.queryForObject( + """ + SELECT last_processed_at + FROM batch_watermark + WHERE job_name = :jobName + """, + Map.of("jobName", jobName), + LocalDateTime.class); + } + + @Transactional + public void update(String jobName, LocalDateTime newFrom) { + jdbc.update( + """ + UPDATE batch_watermark + SET last_processed_at = :newFrom + WHERE job_name = :jobName + """, + Map.of( + "jobName", jobName, + "newFrom", newFrom)); + } +} diff --git a/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java b/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java new file mode 100644 index 0000000..4f9dac8 --- /dev/null +++ b/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java @@ -0,0 +1,88 @@ +package com.project.rdb.batch.orchestrator; + +import java.time.LocalDateTime; + +import org.springframework.batch.core.*; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class UsageOrchestrator { + + private final JobLauncher jobLauncher; + private final Job usageAggregationJob; + private final Job usageNotificationJob; + private final Job notificationSendJob; + + private final BatchTimeWindowService timeWindowService; + + public void run() throws Exception { + LocalDateTime aggregationStart = LocalDateTime.now(); + LocalDateTime aggFrom = timeWindowService.resolve("usage-aggregation"); + + JobParameters aggregationParams = + new JobParametersBuilder() + .addLocalDateTime("fromTime", aggFrom) + .addLocalDateTime("toTime", aggregationStart) + .addLong("run.id", System.currentTimeMillis()) + .toJobParameters(); + + JobExecution aggregationExec = jobLauncher.run(usageAggregationJob, aggregationParams); + + if (aggregationExec.getStatus().isUnsuccessful()) { + return; + } + + // ✅ 성공 시에만 watermark 이동 + timeWindowService.update("usage-aggregation", aggregationStart); + + /* =============================== + * 2️⃣ 알림 대상 추출 배치 + * =============================== */ + + LocalDateTime notificationStart = LocalDateTime.now(); + + LocalDateTime notificationFrom = timeWindowService.resolve("usage-notification"); + + JobParameters notificationParams = + new JobParametersBuilder() + .addLocalDateTime("fromTime", notificationFrom) + .addLocalDateTime("toTime", notificationStart) + .addLong("run.id", System.currentTimeMillis()) + .toJobParameters(); + + JobExecution notificationExec = jobLauncher.run(usageNotificationJob, notificationParams); + + if (notificationExec.getStatus().isUnsuccessful()) { + return; + } + + timeWindowService.update("usage-notification", notificationStart); + + /* =============================== + * 3️⃣ 알림 발송 배치 + * =============================== */ + + LocalDateTime sendStart = LocalDateTime.now(); + + LocalDateTime sendFrom = timeWindowService.resolve("notification-send"); + + JobParameters sendParams = + new JobParametersBuilder() + .addLocalDateTime("fromTime", sendFrom) + .addLocalDateTime("toTime", sendStart) + .addLong("run.id", System.currentTimeMillis()) + .toJobParameters(); + + JobExecution sendExec = jobLauncher.run(notificationSendJob, sendParams); + + if (sendExec.getStatus().isUnsuccessful()) { + return; + } + + timeWindowService.update("notification-send", sendStart); + } +} diff --git a/src/main/java/com/project/rdb/batch/orchestrator/dto/TimeWindow.java b/src/main/java/com/project/rdb/batch/orchestrator/dto/TimeWindow.java new file mode 100644 index 0000000..4a0be88 --- /dev/null +++ b/src/main/java/com/project/rdb/batch/orchestrator/dto/TimeWindow.java @@ -0,0 +1,5 @@ +package com.project.rdb.batch.orchestrator.dto; + +import java.time.LocalDateTime; + +public record TimeWindow(LocalDateTime from, LocalDateTime to) {} diff --git a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java index 858393f..b40b5f2 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationDailyReaderConfig.java @@ -1,5 +1,7 @@ package com.project.rdb.batch.usagenotification.reader; +import java.time.LocalDateTime; + import javax.sql.DataSource; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -18,8 +20,8 @@ public class UsageNotificationDailyReaderConfig { @StepScope public JdbcCursorItemReader usageNotificationDailyReader( DataSource dataSource, - @Value("#{jobParameters['fromTime']}") String fromTime, - @Value("#{jobParameters['toTime']}") String toTime) { + @Value("#{jobParameters['fromTime']}") LocalDateTime fromTime, + @Value("#{jobParameters['toTime']}") LocalDateTime toTime) { String sql = """ @@ -33,8 +35,8 @@ public JdbcCursorItemReader usageNotificationDailyReade FROM usage_summary_daily usd JOIN subscription_plan sp ON sp.sub_id = usd.sub_id - WHERE usd.updated_at >= ?::timestamp - AND usd.updated_at < ?::timestamp + WHERE usd.updated_at >= ? + AND usd.updated_at < ? AND sp.allotment_amount = 5120 ORDER BY usd.sub_id """; @@ -44,10 +46,11 @@ public JdbcCursorItemReader usageNotificationDailyReade .dataSource(dataSource) .sql(sql) .fetchSize(1000) - .preparedStatementSetter(ps -> { - ps.setObject(1, fromTime); - ps.setObject(2, toTime); - }) + .preparedStatementSetter( + ps -> { + ps.setObject(1, fromTime); + ps.setObject(2, toTime); + }) .rowMapper( (rs, rowNum) -> new UsageNotificationSource( diff --git a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java index 6d4d3d2..15b725b 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/reader/UsageNotificationMonthlyReaderConfig.java @@ -1,5 +1,7 @@ package com.project.rdb.batch.usagenotification.reader; +import java.time.LocalDateTime; + import javax.sql.DataSource; import org.springframework.batch.core.configuration.annotation.StepScope; @@ -18,8 +20,8 @@ public class UsageNotificationMonthlyReaderConfig { @StepScope public JdbcCursorItemReader usageNotificationMonthlyReader( DataSource dataSource, - @Value("#{jobParameters['fromTime']}") String fromTime, - @Value("#{jobParameters['toTime']}") String toTime) { + @Value("#{jobParameters['fromTime']}") LocalDateTime fromTime, + @Value("#{jobParameters['toTime']}") LocalDateTime toTime) { String sql = """ @@ -33,8 +35,8 @@ public JdbcCursorItemReader usageNotificationMonthlyRea FROM usage_summary_monthly usm JOIN subscription_plan sp ON sp.sub_id = usm.sub_id - WHERE usm.updated_at >= ?::timestamp - AND usm.updated_at < ?::timestamp + WHERE usm.updated_at >= ? + AND usm.updated_at < ? AND sp.allotment_amount > 0 AND sp.allotment_amount != 5120 ORDER BY usm.sub_id @@ -45,10 +47,11 @@ public JdbcCursorItemReader usageNotificationMonthlyRea .dataSource(dataSource) .sql(sql) .fetchSize(1000) - .preparedStatementSetter(ps -> { - ps.setObject(1, fromTime); - ps.setObject(2, toTime); - }) + .preparedStatementSetter( + ps -> { + ps.setObject(1, fromTime); + ps.setObject(2, toTime); + }) .rowMapper( (rs, rowNum) -> new UsageNotificationSource( diff --git a/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java b/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java index 17e5509..8b9030a 100644 --- a/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java +++ b/src/main/java/com/project/rdb/kafka/consumer/NotificationSendConsumer.java @@ -1,6 +1,5 @@ package com.project.rdb.kafka.consumer; -import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.context.annotation.Profile; import org.springframework.kafka.annotation.KafkaListener; @@ -8,6 +7,7 @@ import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -22,7 +22,8 @@ public class NotificationSendConsumer { @KafkaListener( topics = "notification-usage", - containerFactory = "kafkaListenerContainerFactory") + containerFactory = "kafkaListenerContainerFactory", + autoStartup = "false") @Profile("notification-worker") public void consume(ConsumerRecord record, Acknowledgment ack) { log.info("🔥 CONSUME START offset={}, value={}", record.offset(), record.value()); From 67ae527cb106120fab45dca07f8d5cee5ba97cb5 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 23 Jan 2026 10:00:58 +0900 Subject: [PATCH 07/12] =?UTF-8?q?UPLUS-126=20fix=20:=20spotless=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/UsageNotificationOutboxRepository.java | 3 ++- .../config/NotificationSendJobConfig.java | 4 ++-- .../batch/notificationsend/dto/NotificationMessage.java | 4 +--- .../processor/NotificationSendProcessor.java | 6 ++---- .../notificationsend/writer/NotificationSendWriter.java | 8 +++----- .../usageaggregate/writer/UsageSummaryDailyWriter.java | 2 +- .../usageaggregate/writer/UsageSummaryMonthlyWriter.java | 2 +- .../usagenotification/dto/UsageNotificationSource.java | 7 ++++++- .../processor/UsageNotificationProcessor.java | 2 +- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java b/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java index 0b200a4..c881e3f 100644 --- a/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java +++ b/src/main/java/com/project/rdb/batch/model/repository/UsageNotificationOutboxRepository.java @@ -24,7 +24,8 @@ public void markProcessing(List ids) { public void markSent(List ids) { jdbcTemplate.batchUpdate( - "UPDATE usage_notification_outbox SET status = 'SENT', sent_at = now() WHERE id = ?", + "UPDATE usage_notification_outbox SET status = 'SENT', sent_at = now() WHERE id =" + + " ?", ids, ids.size(), (ps, id) -> ps.setLong(1, id)); diff --git a/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java b/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java index 2fdcef5..8416ce7 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/config/NotificationSendJobConfig.java @@ -1,6 +1,5 @@ package com.project.rdb.batch.notificationsend.config; -import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.JobScope; @@ -13,9 +12,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; -import com.project.rdb.batch.usagenotification.dto.UsageNotificationOutboxRow; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import com.project.rdb.batch.notificationsend.processor.NotificationSendProcessor; import com.project.rdb.batch.notificationsend.writer.NotificationSendWriter; +import com.project.rdb.batch.usagenotification.dto.UsageNotificationOutboxRow; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationMessage.java b/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationMessage.java index ebde229..f11bd65 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationMessage.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/dto/NotificationMessage.java @@ -8,6 +8,4 @@ public record NotificationMessage( Long id, Long templateGroupId, Map subscriptionInfo, - Map variables -) { -} + Map variables) {} diff --git a/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java b/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java index 6e27d98..4895564 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/processor/NotificationSendProcessor.java @@ -3,10 +3,10 @@ import java.util.Map; import java.util.UUID; -import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import com.project.rdb.batch.usagenotification.dto.UsageNotificationOutboxRow; import lombok.extern.slf4j.Slf4j; @@ -35,8 +35,6 @@ public NotificationMessage process(UsageNotificationOutboxRow item) { "allotmentMb", item.allotmentMb(), "phoneNumber", item.phoneNumber(), "email", item.email(), - "createdAt", item.createdAt() - ) - ); + "createdAt", item.createdAt())); } } diff --git a/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java b/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java index 9822484..a61a1a5 100644 --- a/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java +++ b/src/main/java/com/project/rdb/batch/notificationsend/writer/NotificationSendWriter.java @@ -6,7 +6,6 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; -import com.project.rdb.batch.notificationsend.dto.NotificationMessage; import org.springframework.batch.item.Chunk; import org.springframework.batch.item.ItemWriter; import org.springframework.kafka.core.KafkaTemplate; @@ -14,8 +13,9 @@ import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; -import com.project.rdb.batch.notificationsend.dto.NotificationSendTask; import com.project.rdb.batch.model.repository.UsageNotificationOutboxRepository; +import com.project.rdb.batch.notificationsend.dto.NotificationMessage; +import com.project.rdb.batch.notificationsend.dto.NotificationSendTask; import lombok.RequiredArgsConstructor; @@ -45,9 +45,7 @@ public void write(Chunk items) { for (NotificationMessage event : items) { try { String payload = objectMapper.writeValueAsString(event); - String key = String.valueOf( - event.subscriptionInfo().get("subId") - ); + String key = String.valueOf(event.subscriptionInfo().get("subId")); CompletableFuture> future = kafkaTemplate.send("noti-tp", key, payload); diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java index bdacca1..7d11148 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryDailyWriter.java @@ -9,9 +9,9 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; +import com.project.rdb.batch.usageaggregate.config.Sqls; import com.project.rdb.batch.usageaggregate.dto.UsageDailyAggregation; import com.project.rdb.batch.usageaggregate.dto.UsageDailyKey; -import com.project.rdb.batch.usageaggregate.config.Sqls; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java index 3b01aca..8eefc74 100644 --- a/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java +++ b/src/main/java/com/project/rdb/batch/usageaggregate/writer/UsageSummaryMonthlyWriter.java @@ -9,9 +9,9 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; +import com.project.rdb.batch.usageaggregate.config.Sqls; import com.project.rdb.batch.usageaggregate.dto.UsageMonthlyAggregation; import com.project.rdb.batch.usageaggregate.dto.UsageMonthlyKey; -import com.project.rdb.batch.usageaggregate.config.Sqls; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationSource.java b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationSource.java index d119f1f..4c6b178 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationSource.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/dto/UsageNotificationSource.java @@ -1,4 +1,9 @@ package com.project.rdb.batch.usagenotification.dto; public record UsageNotificationSource( - Long subId, String period, String unit, String planName, long totalUsedBytes, long allotmentAmount) {} + Long subId, + String period, + String unit, + String planName, + long totalUsedBytes, + long allotmentAmount) {} diff --git a/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java b/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java index 94544f5..3b54019 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/processor/UsageNotificationProcessor.java @@ -3,9 +3,9 @@ import org.springframework.batch.item.ItemProcessor; import org.springframework.stereotype.Component; +import com.project.rdb.batch.usagenotification.config.util.UsageNotificationPolicy; import com.project.rdb.batch.usagenotification.dto.UsageNotificationCandidate; import com.project.rdb.batch.usagenotification.dto.UsageNotificationSource; -import com.project.rdb.batch.usagenotification.config.util.UsageNotificationPolicy; import lombok.RequiredArgsConstructor; From 9deb4867c05a08a73b9198600bbf02c604ac6998 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 23 Jan 2026 10:02:32 +0900 Subject: [PATCH 08/12] =?UTF-8?q?UPLUS-126=20fix=20:=20checkstyle=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/rdb/batch/orchestrator/UsageOrchestrator.java | 6 +++++- .../usagenotification/writer/UsageNotificationWriter.java | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java b/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java index 4f9dac8..b28bac1 100644 --- a/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java +++ b/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java @@ -2,7 +2,11 @@ import java.time.LocalDateTime; -import org.springframework.batch.core.*; + +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java index ff9e27d..06a44e7 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java @@ -31,7 +31,11 @@ public void write(Chunk chunk) { String sql = """ INSERT INTO usage_notification_outbox - (sub_id, period, plan_name, unit, threshold, percent, total_used_mb, allotment_mb, status, created_at) + (sub_id, period, + plan_name, unit, + threshold, percent, + total_used_mb, allotment_mb, + status, created_at) VALUES (:subId, :period, :planName, :unit, :threshold, :percent, :totalUsedMb, :allotmentMb, 'PENDING', NOW()) ON CONFLICT (sub_id, period, unit, threshold) From bc588368a00d7d9c99a965eb21213adea6fb6e8c Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 23 Jan 2026 10:03:36 +0900 Subject: [PATCH 09/12] =?UTF-8?q?UPLUS-126=20fix=20:=20checkstyle=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rdb/batch/orchestrator/UsageOrchestrator.java | 1 - .../writer/UsageNotificationWriter.java | 14 +++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java b/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java index b28bac1..992e6ea 100644 --- a/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java +++ b/src/main/java/com/project/rdb/batch/orchestrator/UsageOrchestrator.java @@ -2,7 +2,6 @@ import java.time.LocalDateTime; - import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParameters; diff --git a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java index 06a44e7..021631c 100644 --- a/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java +++ b/src/main/java/com/project/rdb/batch/usagenotification/writer/UsageNotificationWriter.java @@ -31,13 +31,17 @@ public void write(Chunk chunk) { String sql = """ INSERT INTO usage_notification_outbox - (sub_id, period, - plan_name, unit, - threshold, percent, - total_used_mb, allotment_mb, + (sub_id, period, + plan_name, unit, + threshold, percent, + total_used_mb, allotment_mb, status, created_at) VALUES - (:subId, :period, :planName, :unit, :threshold, :percent, :totalUsedMb, :allotmentMb, 'PENDING', NOW()) + (:subId, :period, + :planName, :unit, + :threshold, :percent, + :totalUsedMb, :allotmentMb, + 'PENDING', NOW()) ON CONFLICT (sub_id, period, unit, threshold) DO NOTHING """; From d78b888cf4c815ac92ebf2dd1d64ee3240f2a867 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 23 Jan 2026 10:17:02 +0900 Subject: [PATCH 10/12] =?UTF-8?q?UPLUS-126=20feat=20:=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=20batch=20runner=20=EC=82=AD=EC=A0=9C=20=ED=9B=84=20API=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/controller/BatchController.java | 27 ++++++++++ .../java/com/project/rdb/BatchJobRunner.java | 49 ------------------- 2 files changed, 27 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/project/controller/BatchController.java delete mode 100644 src/main/java/com/project/rdb/BatchJobRunner.java diff --git a/src/main/java/com/project/controller/BatchController.java b/src/main/java/com/project/controller/BatchController.java new file mode 100644 index 0000000..0fd4396 --- /dev/null +++ b/src/main/java/com/project/controller/BatchController.java @@ -0,0 +1,27 @@ +package com.project.controller; + +import com.project.rdb.batch.orchestrator.UsageOrchestrator; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/batch") +@RequiredArgsConstructor +public class BatchController { + + private final UsageOrchestrator usageOrchestrator; + + @PostMapping("/usage/run") + public ResponseEntity runUsagePipeline() { + try { + usageOrchestrator.run(); + return ResponseEntity.ok("✅ usage batch pipeline started"); + } catch (Exception e) { + return ResponseEntity.internalServerError() + .body("❌ batch execution failed: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/project/rdb/BatchJobRunner.java b/src/main/java/com/project/rdb/BatchJobRunner.java deleted file mode 100644 index 0212b2e..0000000 --- a/src/main/java/com/project/rdb/BatchJobRunner.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.project.rdb; - -import org.springframework.batch.core.Job; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.boot.CommandLineRunner; -import org.springframework.stereotype.Component; - -import com.project.rdb.batch.orchestrator.UsageOrchestrator; - -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -public class BatchJobRunner implements CommandLineRunner { - - private final JobLauncher jobLauncher; - private final Job usageAggregationJob; - private final Job usageNotificationJob; - private final Job notificationSendJob; - private final UsageOrchestrator usageOrchestrator; - - @Override - public void run(String... args) throws Exception { - usageOrchestrator.run(); - } - - // @Override - // public void run(String... args) throws Exception { - // - // JobParameters params = new JobParametersBuilder() - // // 월 요금제 Step용 - // .addString("fromTime", "2026-01-22T00:00:00") - // .addString("toTime", "2026-01-22T23:59:59") - // .addLong("run.id", System.currentTimeMillis()) - // .toJobParameters(); - // - // jobLauncher.run(usageNotificationJob, params); - // } - // // - // @Override - // public void run(String... args) throws Exception { - // JobParameters params = - // new JobParametersBuilder() - // .addLong("runAt", System.currentTimeMillis()) // 중복 실행 방지 - // .toJobParameters(); - // - // jobLauncher.run(notificationSendJob, params); - // } -} From 1e7389064f3766bf2c4fe8a32d9c760e0a76f6a3 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 23 Jan 2026 10:17:58 +0900 Subject: [PATCH 11/12] =?UTF-8?q?UPLUS-126=20fix=20:=20checkStyle=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/project/controller/BatchController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/project/controller/BatchController.java b/src/main/java/com/project/controller/BatchController.java index 0fd4396..67a9561 100644 --- a/src/main/java/com/project/controller/BatchController.java +++ b/src/main/java/com/project/controller/BatchController.java @@ -1,12 +1,14 @@ package com.project.controller; -import com.project.rdb.batch.orchestrator.UsageOrchestrator; -import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.project.rdb.batch.orchestrator.UsageOrchestrator; + +import lombok.RequiredArgsConstructor; + @RestController @RequestMapping("/api/batch") @RequiredArgsConstructor From 64a8b726d2eb86888a72b7e16ba05ad01ec48ab4 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 23 Jan 2026 13:45:58 +0900 Subject: [PATCH 12/12] =?UTF-8?q?UPLUS-126=20fix=20:=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 22864ee..e3f90b2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,12 +16,14 @@ spring: driver-class-name: org.postgresql.Driver batch: + jdbc: + initialize-schema: always job: enabled: false jpa: hibernate: - ddl-auto: validate + ddl-auto: none show-sql: true database-platform: org.hibernate.dialect.PostgreSQLDialect properties: @@ -37,15 +39,15 @@ spring: url: redis://${REDIS_USERNAME:default}:${REDIS_PASSWORD:}@${REDIS_ENDPOINT:localhost:6379} kafka: - bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS} + bootstrap-servers: ${KAFKA_BOOTSTRAP_SERVERS:pkc-oxqxx9.us-east-1.aws.confluent.cloud:9092} properties: security.protocol: SASL_SSL sasl.mechanism: PLAIN sasl.jaas.config: > org.apache.kafka.common.security.plain.PlainLoginModule required - username='${KAFKA_API_KEY}' - password='${KAFKA_API_SECRET}'; + username='${KAFKA_API_KEY:EPO6DS6OXW7GYVEE}' + password='${KAFKA_API_SECRET:cfltJj+U/TvM2TfzfqxpXIu5xDE/bPGe5sfIff5mrLZ9Usx4K9LudKBWPLIGG3QQ}'; producer: retries: 2147483647