diff --git a/src/main/java/balancetalk/talkpick/domain/TalkPickFileHandler.java b/src/main/java/balancetalk/talkpick/application/TalkPickFileService.java similarity index 85% rename from src/main/java/balancetalk/talkpick/domain/TalkPickFileHandler.java rename to src/main/java/balancetalk/talkpick/application/TalkPickFileService.java index 98d0bb7c3..137dcfb6a 100644 --- a/src/main/java/balancetalk/talkpick/domain/TalkPickFileHandler.java +++ b/src/main/java/balancetalk/talkpick/application/TalkPickFileService.java @@ -1,4 +1,4 @@ -package balancetalk.talkpick.domain; +package balancetalk.talkpick.application; import static balancetalk.file.domain.FileType.TALK_PICK; @@ -15,7 +15,7 @@ @Component @RequiredArgsConstructor -public class TalkPickFileHandler { +public class TalkPickFileService { private final FileRepository fileRepository; private final FileHandler fileHandler; @@ -24,6 +24,9 @@ public class TalkPickFileHandler { @Retryable(backoff = @Backoff(delay = 1000)) @Transactional public void handleFilesOnTalkPickCreate(List fileIds, Long talkPickId) { + if (fileIds == null || fileIds.isEmpty()) { + return; + } relocateFiles(fileIds, talkPickId); } @@ -32,14 +35,6 @@ private void relocateFiles(List fileIds, Long talkPickId) { fileHandler.relocateFiles(files, talkPickId, TALK_PICK); } - public List findImgUrlsBy(Long talkPickId) { - return fileRepository.findImgUrlsByResourceIdAndFileType(talkPickId, TALK_PICK); - } - - public List findFileIdsBy(Long talkPickId) { - return fileRepository.findIdsByResourceIdAndFileType(talkPickId, TALK_PICK); - } - @Async @Retryable(backoff = @Backoff(delay = 1000)) @Transactional diff --git a/src/main/java/balancetalk/talkpick/application/TalkPickScheduleService.java b/src/main/java/balancetalk/talkpick/application/TalkPickScheduleService.java index 8dfa7e9ab..96700d1b2 100644 --- a/src/main/java/balancetalk/talkpick/application/TalkPickScheduleService.java +++ b/src/main/java/balancetalk/talkpick/application/TalkPickScheduleService.java @@ -1,10 +1,5 @@ package balancetalk.talkpick.application; -import static balancetalk.talkpick.domain.SummaryStatus.FAIL; - -import balancetalk.talkpick.domain.TalkPickSummarizer; -import balancetalk.talkpick.domain.repository.TalkPickRepository; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @@ -13,16 +8,12 @@ @RequiredArgsConstructor public class TalkPickScheduleService { - private final TalkPickRepository talkPickRepository; - private final TalkPickSummarizer talkPickSummarizer; + private final TalkPickSummaryService talkPickSummaryService; private final TodayTalkPickService todayTalkPickService; // @Scheduled(cron = "0 30 00 * * ?") public void retryFailedSummaries() { - List summaryFailedTalkPickIds = talkPickRepository.findIdsBySummaryStatus(FAIL); - for (Long summaryFailedTalkPickId : summaryFailedTalkPickIds) { - talkPickSummarizer.summarizeTalkPick(summaryFailedTalkPickId); - } + talkPickSummaryService.summarizeFailedTalkPick(); } @Scheduled(cron = "0 00 00 * * ?") diff --git a/src/main/java/balancetalk/talkpick/application/TalkPickService.java b/src/main/java/balancetalk/talkpick/application/TalkPickService.java index 024c03aa3..b55ce5b20 100644 --- a/src/main/java/balancetalk/talkpick/application/TalkPickService.java +++ b/src/main/java/balancetalk/talkpick/application/TalkPickService.java @@ -1,24 +1,28 @@ package balancetalk.talkpick.application; +import static balancetalk.file.domain.FileType.TALK_PICK; import static balancetalk.global.exception.ErrorCode.NOT_FOUND_TALK_PICK; import static balancetalk.talkpick.dto.TalkPickDto.CreateTalkPickRequest; import static balancetalk.talkpick.dto.TalkPickDto.TalkPickDetailResponse; import static balancetalk.talkpick.dto.TalkPickDto.TalkPickResponse; import static balancetalk.talkpick.dto.TalkPickDto.UpdateTalkPickRequest; +import balancetalk.file.domain.repository.FileRepository; import balancetalk.global.exception.BalanceTalkException; import balancetalk.member.domain.Member; import balancetalk.member.domain.MemberRepository; import balancetalk.member.dto.ApiMember; import balancetalk.member.dto.GuestOrApiMember; import balancetalk.talkpick.domain.TalkPick; -import balancetalk.talkpick.domain.TalkPickFileHandler; -import balancetalk.talkpick.domain.TalkPickSummarizer; +import balancetalk.talkpick.domain.event.TalkPickCreatedEvent; +import balancetalk.talkpick.domain.event.TalkPickDeletedEvent; +import balancetalk.talkpick.domain.event.TalkPickUpdatedEvent; import balancetalk.talkpick.domain.repository.TalkPickRepository; import balancetalk.vote.domain.TalkPickVote; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -30,8 +34,8 @@ public class TalkPickService { private final MemberRepository memberRepository; private final TalkPickRepository talkPickRepository; - private final TalkPickFileHandler talkPickFileHandler; - private final TalkPickSummarizer talkPickSummarizer; + private final ApplicationEventPublisher eventPublisher; + private final FileRepository fileRepository; @Transactional public Long createTalkPick(CreateTalkPickRequest request, ApiMember apiMember) { @@ -39,11 +43,7 @@ public Long createTalkPick(CreateTalkPickRequest request, ApiMember apiMember) { TalkPick savedTalkPick = talkPickRepository.save(request.toEntity(member)); Long savedTalkPickId = savedTalkPick.getId(); - if (request.containsFileIds()) { - talkPickFileHandler.handleFilesOnTalkPickCreate(request.getFileIds(), savedTalkPickId); - } - - talkPickSummarizer.summarizeTalkPick(savedTalkPickId); + eventPublisher.publishEvent(new TalkPickCreatedEvent(savedTalkPickId, request.getFileIds())); return savedTalkPickId; } @@ -54,8 +54,8 @@ public TalkPickDetailResponse findById(Long talkPickId, GuestOrApiMember guestOr .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_TALK_PICK)); talkPick.increaseViews(); - List imgUrls = talkPickFileHandler.findImgUrlsBy(talkPickId); - List fileIds = talkPickFileHandler.findFileIdsBy(talkPickId); + List imgUrls = fileRepository.findImgUrlsByResourceIdAndFileType(talkPickId, TALK_PICK); + List fileIds = fileRepository.findIdsByResourceIdAndFileType(talkPickId, TALK_PICK); if (guestOrApiMember.isGuest()) { return TalkPickDetailResponse.from(talkPick, imgUrls, fileIds, false, null); @@ -85,9 +85,9 @@ public void updateTalkPick(Long talkPickId, UpdateTalkPickRequest request, ApiMe Member member = apiMember.toMember(memberRepository); TalkPick talkPick = member.getTalkPickById(talkPickId); talkPick.update(request.toEntity(member)); - talkPickFileHandler - .handleFilesOnTalkPickUpdate(request.getNewFileIds(), request.getDeleteFileIds(), talkPickId); - talkPickSummarizer.summarizeTalkPick(talkPickId); + + eventPublisher.publishEvent( + new TalkPickUpdatedEvent(talkPickId, request.getNewFileIds(), request.getDeleteFileIds())); } @Transactional @@ -95,6 +95,7 @@ public void deleteTalkPick(Long talkPickId, ApiMember apiMember) { Member member = apiMember.toMember(memberRepository); TalkPick talkPick = member.getTalkPickById(talkPickId); talkPickRepository.delete(talkPick); - talkPickFileHandler.handleFilesOnTalkPickDelete(talkPickId); + + eventPublisher.publishEvent(new TalkPickDeletedEvent(talkPickId)); } } diff --git a/src/main/java/balancetalk/talkpick/domain/TalkPickSummarizer.java b/src/main/java/balancetalk/talkpick/application/TalkPickSummaryService.java similarity index 85% rename from src/main/java/balancetalk/talkpick/domain/TalkPickSummarizer.java rename to src/main/java/balancetalk/talkpick/application/TalkPickSummaryService.java index 556f289e8..557d15ce9 100644 --- a/src/main/java/balancetalk/talkpick/domain/TalkPickSummarizer.java +++ b/src/main/java/balancetalk/talkpick/application/TalkPickSummaryService.java @@ -1,4 +1,4 @@ -package balancetalk.talkpick.domain; +package balancetalk.talkpick.application; import static balancetalk.global.exception.ErrorCode.NOT_FOUND_TALK_PICK; import static balancetalk.global.exception.ErrorCode.TALK_PICK_SUMMARY_FAILED; @@ -8,10 +8,13 @@ import static balancetalk.talkpick.domain.SummaryStatus.SUCCESS; import balancetalk.global.exception.BalanceTalkException; +import balancetalk.talkpick.domain.Summary; +import balancetalk.talkpick.domain.TalkPick; import balancetalk.talkpick.domain.repository.TalkPickRepository; import balancetalk.talkpick.dto.fields.BaseTalkPickFields; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import lombok.RequiredArgsConstructor; @@ -19,13 +22,13 @@ import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Slf4j -@Component +@Service @RequiredArgsConstructor -public class TalkPickSummarizer { +public class TalkPickSummaryService { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final String SYSTEM_MESSAGE = """ @@ -58,7 +61,6 @@ public void summarizeTalkPick(Long talkPickId) { TalkPick talkPick = talkPickRepository.findById(talkPickId) .orElseThrow(() -> new BalanceTalkException(NOT_FOUND_TALK_PICK)); - // 본문 글자수가 너무 짧으면 요약 제공 안함 if (talkPick.hasShortContent()) { talkPick.updateSummaryStatus(NOT_REQUIRED); @@ -114,4 +116,18 @@ private BaseTalkPickFields getBaseTalkPickFields(TalkPick talkPick) { talkPick.getOptionA(), talkPick.getOptionB()); } + + @Transactional + public void summarizeFailedTalkPick() { + List summaryFailedTalkPicks = talkPickRepository.findAllBySummaryStatus(FAIL); + for (TalkPick summaryFailedTalkPick : summaryFailedTalkPicks) { + try { + summarize(summaryFailedTalkPick); + } catch (Exception e) { + log.error("Fail to summary TalkPick ID = {}", summaryFailedTalkPick.getId()); + log.error("exception message = {} {}", e.getMessage(), e.getStackTrace()); + summaryFailedTalkPick.updateSummaryStatus(FAIL); + } + } + } } diff --git a/src/main/java/balancetalk/talkpick/domain/event/TalkPickCreatedEvent.java b/src/main/java/balancetalk/talkpick/domain/event/TalkPickCreatedEvent.java new file mode 100644 index 000000000..ae2388120 --- /dev/null +++ b/src/main/java/balancetalk/talkpick/domain/event/TalkPickCreatedEvent.java @@ -0,0 +1,15 @@ +package balancetalk.talkpick.domain.event; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class TalkPickCreatedEvent { + + private Long talkPickId; + private List fileIds; +} diff --git a/src/main/java/balancetalk/talkpick/domain/event/TalkPickDeletedEvent.java b/src/main/java/balancetalk/talkpick/domain/event/TalkPickDeletedEvent.java new file mode 100644 index 000000000..daaa9cc1d --- /dev/null +++ b/src/main/java/balancetalk/talkpick/domain/event/TalkPickDeletedEvent.java @@ -0,0 +1,13 @@ +package balancetalk.talkpick.domain.event; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class TalkPickDeletedEvent { + + private Long talkPickId; +} diff --git a/src/main/java/balancetalk/talkpick/domain/event/TalkPickEventHandler.java b/src/main/java/balancetalk/talkpick/domain/event/TalkPickEventHandler.java new file mode 100644 index 000000000..1695169a4 --- /dev/null +++ b/src/main/java/balancetalk/talkpick/domain/event/TalkPickEventHandler.java @@ -0,0 +1,33 @@ +package balancetalk.talkpick.domain.event; + +import balancetalk.talkpick.application.TalkPickFileService; +import balancetalk.talkpick.application.TalkPickSummaryService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +@RequiredArgsConstructor +public class TalkPickEventHandler { + + private final TalkPickSummaryService talkPickSummaryService; + private final TalkPickFileService talkPickFileService; + + @TransactionalEventListener + public void handleTalkPickCreatedEvent(TalkPickCreatedEvent event) { + talkPickFileService.handleFilesOnTalkPickCreate(event.getFileIds(), event.getTalkPickId()); + talkPickSummaryService.summarizeTalkPick(event.getTalkPickId()); + } + + @TransactionalEventListener + public void handleTalkPickUpdatedEvent(TalkPickUpdatedEvent event) { + talkPickFileService.handleFilesOnTalkPickUpdate( + event.getNewFileIds(), event.getDeleteFileIds(), event.getTalkPickId()); + talkPickSummaryService.summarizeTalkPick(event.getTalkPickId()); + } + + @TransactionalEventListener + public void handleTalkPickDeletedEvent(TalkPickDeletedEvent event) { + talkPickFileService.handleFilesOnTalkPickDelete(event.getTalkPickId()); + } +} diff --git a/src/main/java/balancetalk/talkpick/domain/event/TalkPickUpdatedEvent.java b/src/main/java/balancetalk/talkpick/domain/event/TalkPickUpdatedEvent.java new file mode 100644 index 000000000..9ed651778 --- /dev/null +++ b/src/main/java/balancetalk/talkpick/domain/event/TalkPickUpdatedEvent.java @@ -0,0 +1,16 @@ +package balancetalk.talkpick.domain.event; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class TalkPickUpdatedEvent { + + private Long talkPickId; + private List newFileIds; + private List deleteFileIds; +} diff --git a/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepository.java b/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepository.java index 42ee4eae0..b88240e08 100644 --- a/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepository.java +++ b/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepository.java @@ -1,6 +1,8 @@ package balancetalk.talkpick.domain.repository; +import balancetalk.talkpick.domain.SummaryStatus; import balancetalk.talkpick.domain.TalkPick; +import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -8,4 +10,6 @@ public interface TalkPickRepository extends JpaRepository, TalkPickRepositoryCustom { Page findAllByMemberIdOrderByEditedAtDesc(Long memberId, Pageable pageable); + + List findAllBySummaryStatus(SummaryStatus summaryStatus); } diff --git a/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryCustom.java b/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryCustom.java index cca46b372..54fa461ce 100644 --- a/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryCustom.java +++ b/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryCustom.java @@ -2,7 +2,6 @@ import static balancetalk.talkpick.dto.TalkPickDto.TalkPickResponse; -import balancetalk.talkpick.domain.SummaryStatus; import balancetalk.talkpick.domain.TalkPick; import java.util.List; import org.springframework.data.domain.Page; @@ -15,6 +14,4 @@ public interface TalkPickRepositoryCustom { Page findPagedTalkPicks(Pageable pageable); List findBestTalkPicks(); - - List findIdsBySummaryStatus(SummaryStatus summaryStatus); } diff --git a/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryImpl.java b/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryImpl.java index 0b50dc638..007397bd1 100644 --- a/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryImpl.java +++ b/src/main/java/balancetalk/talkpick/domain/repository/TalkPickRepositoryImpl.java @@ -5,7 +5,6 @@ import static balancetalk.talkpick.dto.TalkPickDto.TalkPickResponse; import static balancetalk.vote.domain.QTalkPickVote.talkPickVote; -import balancetalk.talkpick.domain.SummaryStatus; import balancetalk.talkpick.domain.TalkPick; import balancetalk.talkpick.dto.QTalkPickDto_TalkPickResponse; import com.querydsl.jpa.impl.JPAQuery; @@ -65,12 +64,4 @@ public List findBestTalkPicks() { .limit(3) .fetch(); } - - @Override - public List findIdsBySummaryStatus(SummaryStatus summaryStatus) { - return queryFactory.select(talkPick.id) - .from(talkPick) - .where(talkPick.summaryStatus.eq(summaryStatus)) - .fetch(); - } } diff --git a/src/test/java/balancetalk/talkpick/application/TalkPickServiceTest.java b/src/test/java/balancetalk/talkpick/application/TalkPickServiceTest.java index 6749f9b44..0b35af459 100644 --- a/src/test/java/balancetalk/talkpick/application/TalkPickServiceTest.java +++ b/src/test/java/balancetalk/talkpick/application/TalkPickServiceTest.java @@ -1,19 +1,18 @@ package balancetalk.talkpick.application; +import static balancetalk.file.domain.FileType.TALK_PICK; import static balancetalk.vote.domain.VoteOption.A; import static balancetalk.vote.domain.VoteOption.B; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import balancetalk.file.domain.repository.FileRepository; import balancetalk.member.domain.Member; import balancetalk.member.dto.GuestOrApiMember; import balancetalk.talkpick.domain.Summary; import balancetalk.talkpick.domain.TalkPick; -import balancetalk.talkpick.domain.TalkPickFileHandler; import balancetalk.talkpick.domain.repository.TalkPickRepository; import balancetalk.talkpick.dto.TalkPickDto.TalkPickDetailResponse; import java.time.LocalDateTime; @@ -37,7 +36,7 @@ class TalkPickServiceTest { TalkPickRepository talkPickRepository; @Mock - TalkPickFileHandler talkPickFileHandler; + FileRepository fileRepository; TalkPick talkPick; GuestOrApiMember guestOrApiMember; @@ -69,8 +68,8 @@ void findById_Success_ThenIncreaseViews() { // given when(talkPickRepository.findById(1L)).thenReturn(Optional.ofNullable(talkPick)); when(guestOrApiMember.isGuest()).thenReturn(true); - when(talkPickFileHandler.findImgUrlsBy(1L)).thenReturn(any()); - when(talkPickFileHandler.findFileIdsBy(1L)).thenReturn(any()); + when(fileRepository.findImgUrlsByResourceIdAndFileType(1L, TALK_PICK)).thenReturn(List.of()); + when(fileRepository.findIdsByResourceIdAndFileType(1L, TALK_PICK)).thenReturn(List.of()); // when talkPickService.findById(1L, guestOrApiMember); @@ -85,8 +84,8 @@ void findById_Success_ThenMyBookmarkIsFalse_ByGuest() { // given when(talkPickRepository.findById(1L)).thenReturn(Optional.ofNullable(talkPick)); when(guestOrApiMember.isGuest()).thenReturn(true); - when(talkPickFileHandler.findImgUrlsBy(anyLong())).thenReturn(List.of()); - when(talkPickFileHandler.findFileIdsBy(anyLong())).thenReturn(List.of()); + when(fileRepository.findImgUrlsByResourceIdAndFileType(1L, TALK_PICK)).thenReturn(List.of()); + when(fileRepository.findIdsByResourceIdAndFileType(1L, TALK_PICK)).thenReturn(List.of()); // when TalkPickDetailResponse result = talkPickService.findById(1L, guestOrApiMember); @@ -101,8 +100,8 @@ void findById_Success_ThenVoteOptionIsNull_ByGuest() { // given when(talkPickRepository.findById(1L)).thenReturn(Optional.ofNullable(talkPick)); when(guestOrApiMember.isGuest()).thenReturn(true); - when(talkPickFileHandler.findImgUrlsBy(anyLong())).thenReturn(List.of()); - when(talkPickFileHandler.findFileIdsBy(anyLong())).thenReturn(List.of()); + when(fileRepository.findImgUrlsByResourceIdAndFileType(1L, TALK_PICK)).thenReturn(List.of()); + when(fileRepository.findIdsByResourceIdAndFileType(1L, TALK_PICK)).thenReturn(List.of()); // when TalkPickDetailResponse result = talkPickService.findById(1L, guestOrApiMember);