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); }