From e34d8fab6c991176fc68b4b638344ab197d104da Mon Sep 17 00:00:00 2001 From: y11n Date: Thu, 23 Oct 2025 15:27:51 +0900 Subject: [PATCH] =?UTF-8?q?[Feat]=20=EA=B8=B0=EB=A1=9D=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EC=8B=9C=20=EB=9E=AD=ED=82=B9=20=EC=A7=91=EA=B3=84?= =?UTF-8?q?=20=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EB=B0=98=EC=98=81?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../studylog/event/RecordDeletedEvent.java | 12 +++++++++ .../event/listener/RecordEventListener.java | 13 +++++++--- .../custom/RankingRepositoryCustom.java | 1 + .../custom/RankingRepositoryImpl.java | 25 +++++++++++++++++++ .../studylog/service/StudyRecordService.java | 6 +++++ 5 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/example/studylog/event/RecordDeletedEvent.java diff --git a/src/main/java/org/example/studylog/event/RecordDeletedEvent.java b/src/main/java/org/example/studylog/event/RecordDeletedEvent.java new file mode 100644 index 0000000..78af480 --- /dev/null +++ b/src/main/java/org/example/studylog/event/RecordDeletedEvent.java @@ -0,0 +1,12 @@ +package org.example.studylog.event; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class RecordDeletedEvent { + private final Long userId; + private final int year; + private final int month; +} diff --git a/src/main/java/org/example/studylog/event/listener/RecordEventListener.java b/src/main/java/org/example/studylog/event/listener/RecordEventListener.java index 5be84a9..d7ba100 100644 --- a/src/main/java/org/example/studylog/event/listener/RecordEventListener.java +++ b/src/main/java/org/example/studylog/event/listener/RecordEventListener.java @@ -1,9 +1,9 @@ package org.example.studylog.event.listener; -import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.example.studylog.event.RecordCreatedEvent; +import org.example.studylog.event.RecordDeletedEvent; import org.example.studylog.repository.custom.RankingRepositoryImpl; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @@ -20,8 +20,15 @@ public class RecordEventListener { @Async @TransactionalEventListener(phase = AFTER_COMMIT) - public void handleRecordCreated(RecordCreatedEvent event){ - log.info("랭킹 집계 이벤트 발행: USER={}, YEAR={}, MONTH={}", event.getUserId(), event.getYear(), event.getMonth()); + public void handleRecordCreated(RecordCreatedEvent event) { + log.info("랭킹 집계 증가 이벤트 발행: USER={}, YEAR={}, MONTH={}", event.getUserId(), event.getYear(), event.getMonth()); rankingRepository.incrementOrInsert(event.getUserId(), event.getYear(), event.getMonth()); } + + @Async + @TransactionalEventListener(phase = AFTER_COMMIT) + public void handleRecordDeleted(RecordDeletedEvent event){ + log.info("랭킹 집계 감소 이벤트 발행: USER={}, YEAR={}, MONTH={}", event.getUserId(), event.getYear(), event.getMonth()); + rankingRepository.decrement(event.getUserId(), event.getYear(), event.getMonth()); + } } diff --git a/src/main/java/org/example/studylog/repository/custom/RankingRepositoryCustom.java b/src/main/java/org/example/studylog/repository/custom/RankingRepositoryCustom.java index a8cbb77..63b965c 100644 --- a/src/main/java/org/example/studylog/repository/custom/RankingRepositoryCustom.java +++ b/src/main/java/org/example/studylog/repository/custom/RankingRepositoryCustom.java @@ -7,4 +7,5 @@ public interface RankingRepositoryCustom { List findFriendRankings(int year, int month, List userIds); void incrementOrInsert(Long userId, int year, int month); + void decrement(Long userId, int year, int month); } diff --git a/src/main/java/org/example/studylog/repository/custom/RankingRepositoryImpl.java b/src/main/java/org/example/studylog/repository/custom/RankingRepositoryImpl.java index 77ff3d6..67da578 100644 --- a/src/main/java/org/example/studylog/repository/custom/RankingRepositoryImpl.java +++ b/src/main/java/org/example/studylog/repository/custom/RankingRepositoryImpl.java @@ -5,6 +5,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.example.studylog.dto.RankingResponseDTO; import org.example.studylog.entity.QUserMonthlyStat; import org.example.studylog.entity.UserMonthlyStat; @@ -13,6 +14,7 @@ import org.springframework.stereotype.Repository; import java.util.List; +@Slf4j @Repository @RequiredArgsConstructor public class RankingRepositoryImpl implements RankingRepositoryCustom{ @@ -85,4 +87,27 @@ public void incrementOrInsert(Long userId, int year, int month) { .execute(); } } + + @Override + @Transactional + public void decrement(Long userId, int year, int month) { + QUserMonthlyStat stat = QUserMonthlyStat.userMonthlyStat; + // 존재하는 경우에만 recordCount - 1 + long updated = queryFactory + .update(stat) + .set(stat.recordCount, stat.recordCount.subtract(1)) + .where( + stat.user.id.eq(userId), + stat.year.eq(year), + stat.month.eq(month), + stat.recordCount.gt(0) // 음수 방지 + ) + .execute(); + + // 혹시라도 업데이트된 행이 없다면 (예: 존재하지 않거나 이미 0인 경우) + if (updated == 0) { + log.warn("user_monthly_stat에 행이 없거나 recordCount가 이미 0: userId={}, year={}, month={}", + userId, year, month); + } + } } diff --git a/src/main/java/org/example/studylog/service/StudyRecordService.java b/src/main/java/org/example/studylog/service/StudyRecordService.java index 673def1..6c3b050 100644 --- a/src/main/java/org/example/studylog/service/StudyRecordService.java +++ b/src/main/java/org/example/studylog/service/StudyRecordService.java @@ -11,6 +11,7 @@ import org.example.studylog.entity.Streak; import org.example.studylog.entity.user.User; import org.example.studylog.event.RecordCreatedEvent; +import org.example.studylog.event.RecordDeletedEvent; import org.example.studylog.event.RecordEvent; import org.example.studylog.repository.CategoryRepository; import org.example.studylog.repository.StudyRecordRepository; @@ -83,6 +84,11 @@ public void deleteStudyRecord(User user, Long recordId) { user.decrementRecordCount(); log.info("기록 삭제 이벤트 발행: USER={}, ID={}", user.getOauthId(), recordId); eventPublisher.publishEvent(new RecordEvent(user)); + LocalDate createDate = studyRecord.getCreateDate().toLocalDate(); + int year = createDate.getYear(); + int month = createDate.getMonthValue(); + log.info("기록 삭제 내역을 랭킹 집계 테이블에 반영: USER={}, YEAR={}, MONTH={}", user.getId(), year, month); + eventPublisher.publishEvent(new RecordDeletedEvent(user.getId(), year, month)); log.info("기록 삭제 완료: ID={}", recordId); }