diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/album/event/AlbumDeleteNotificationSendEvent.java b/cherrypic-api/src/main/java/org/cherrypic/domain/album/dto/event/AlbumDeleteNotificationSendEvent.java similarity index 92% rename from cherrypic-api/src/main/java/org/cherrypic/domain/album/event/AlbumDeleteNotificationSendEvent.java rename to cherrypic-api/src/main/java/org/cherrypic/domain/album/dto/event/AlbumDeleteNotificationSendEvent.java index 1a95b10c..3bf8eb35 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/album/event/AlbumDeleteNotificationSendEvent.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/album/dto/event/AlbumDeleteNotificationSendEvent.java @@ -1,4 +1,4 @@ -package org.cherrypic.domain.album.event; +package org.cherrypic.domain.album.dto.event; import java.util.List; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/album/event/AlbumImagesDeleteEvent.java b/cherrypic-api/src/main/java/org/cherrypic/domain/album/dto/event/AlbumImagesDeleteEvent.java similarity index 79% rename from cherrypic-api/src/main/java/org/cherrypic/domain/album/event/AlbumImagesDeleteEvent.java rename to cherrypic-api/src/main/java/org/cherrypic/domain/album/dto/event/AlbumImagesDeleteEvent.java index 2bb6e075..100a006e 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/album/event/AlbumImagesDeleteEvent.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/album/dto/event/AlbumImagesDeleteEvent.java @@ -1,4 +1,4 @@ -package org.cherrypic.domain.album.event; +package org.cherrypic.domain.album.dto.event; public record AlbumImagesDeleteEvent(Long albumId) { public static AlbumImagesDeleteEvent of(Long albumId) { diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/album/service/AlbumServiceImpl.java b/cherrypic-api/src/main/java/org/cherrypic/domain/album/service/AlbumServiceImpl.java index fdc79e7c..5eda0f35 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/album/service/AlbumServiceImpl.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/album/service/AlbumServiceImpl.java @@ -7,19 +7,19 @@ import org.cherrypic.album.entity.Album; import org.cherrypic.album.entity.InvitationCode; import org.cherrypic.album.enums.AlbumType; +import org.cherrypic.domain.album.dto.event.AlbumDeleteNotificationSendEvent; +import org.cherrypic.domain.album.dto.event.AlbumImagesDeleteEvent; import org.cherrypic.domain.album.dto.request.AlbumCreateRequest; import org.cherrypic.domain.album.dto.request.AlbumUpdateRequest; import org.cherrypic.domain.album.dto.response.*; -import org.cherrypic.domain.album.event.AlbumDeleteNotificationSendEvent; -import org.cherrypic.domain.album.event.AlbumImagesDeleteEvent; import org.cherrypic.domain.album.exception.AlbumErrorCode; import org.cherrypic.domain.album.repository.AlbumRepository; import org.cherrypic.domain.album.repository.InvitationCodeRepository; import org.cherrypic.domain.event.repository.EventImageRepository; import org.cherrypic.domain.event.repository.EventRepository; import org.cherrypic.domain.favorites.repository.FavoritesRepository; -import org.cherrypic.domain.image.event.ImageDeleteEvent; -import org.cherrypic.domain.image.event.ImagesDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImageDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImagesDeleteEvent; import org.cherrypic.domain.image.repository.ImageRepository; import org.cherrypic.domain.notification.repository.NotificationRepository; import org.cherrypic.domain.participant.repository.ParticipantRepository; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/event/service/EventServiceImpl.java b/cherrypic-api/src/main/java/org/cherrypic/domain/event/service/EventServiceImpl.java index 7bb96b8c..ebe5809c 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/event/service/EventServiceImpl.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/event/service/EventServiceImpl.java @@ -17,7 +17,7 @@ import org.cherrypic.domain.event.exception.EventErrorCode; import org.cherrypic.domain.event.repository.EventImageRepository; import org.cherrypic.domain.event.repository.EventRepository; -import org.cherrypic.domain.image.event.ImageDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImageDeleteEvent; import org.cherrypic.domain.image.exception.ImageErrorCode; import org.cherrypic.domain.image.repository.ImageRepository; import org.cherrypic.domain.participant.repository.ParticipantRepository; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImageDeleteEvent.java b/cherrypic-api/src/main/java/org/cherrypic/domain/image/dto/event/ImageDeleteEvent.java similarity index 77% rename from cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImageDeleteEvent.java rename to cherrypic-api/src/main/java/org/cherrypic/domain/image/dto/event/ImageDeleteEvent.java index 0b4cc335..d661f7db 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImageDeleteEvent.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/image/dto/event/ImageDeleteEvent.java @@ -1,4 +1,4 @@ -package org.cherrypic.domain.image.event; +package org.cherrypic.domain.image.dto.event; public record ImageDeleteEvent(String imageUrl) { public static ImageDeleteEvent of(String imageUrl) { diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImagesDeleteEvent.java b/cherrypic-api/src/main/java/org/cherrypic/domain/image/dto/event/ImagesDeleteEvent.java similarity index 81% rename from cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImagesDeleteEvent.java rename to cherrypic-api/src/main/java/org/cherrypic/domain/image/dto/event/ImagesDeleteEvent.java index 92c27e58..f8e6182f 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImagesDeleteEvent.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/image/dto/event/ImagesDeleteEvent.java @@ -1,4 +1,4 @@ -package org.cherrypic.domain.image.event; +package org.cherrypic.domain.image.dto.event; import java.util.List; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImageEventListener.java b/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImageEventListener.java index e455426f..60707cd4 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImageEventListener.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/image/event/ImageEventListener.java @@ -1,7 +1,9 @@ package org.cherrypic.domain.image.event; import lombok.RequiredArgsConstructor; -import org.cherrypic.domain.album.event.AlbumImagesDeleteEvent; +import org.cherrypic.domain.album.dto.event.AlbumImagesDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImageDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImagesDeleteEvent; import org.cherrypic.s3.S3Util; import org.cherrypic.s3.enums.ImageType; import org.springframework.scheduling.annotation.Async; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/image/service/ImageServiceImpl.java b/cherrypic-api/src/main/java/org/cherrypic/domain/image/service/ImageServiceImpl.java index 0ab9d9f6..98e59e10 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/image/service/ImageServiceImpl.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/image/service/ImageServiceImpl.java @@ -12,15 +12,15 @@ import org.cherrypic.domain.event.exception.EventErrorCode; import org.cherrypic.domain.event.repository.EventImageRepository; import org.cherrypic.domain.event.repository.EventRepository; +import org.cherrypic.domain.image.dto.event.ImagesDeleteEvent; import org.cherrypic.domain.image.dto.request.*; import org.cherrypic.domain.image.dto.response.*; -import org.cherrypic.domain.image.event.ImagesDeleteEvent; import org.cherrypic.domain.image.exception.ImageErrorCode; import org.cherrypic.domain.image.repository.ImageRepository; import org.cherrypic.domain.participant.repository.ParticipantRepository; import org.cherrypic.domain.subscription.exception.SubscriptionErrorCode; import org.cherrypic.domain.subscription.repository.SubscriptionRepository; -import org.cherrypic.domain.tempalbum.event.TempAlbumImagesDeleteEvent; +import org.cherrypic.domain.tempalbum.dto.event.TempAlbumImagesDeleteEvent; import org.cherrypic.domain.tempalbum.exception.TempAlbumErrorCode; import org.cherrypic.domain.tempalbum.repository.TempAlbumImageRepository; import org.cherrypic.domain.tempalbum.repository.TempAlbumRepository; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/member/service/MemberServiceImpl.java b/cherrypic-api/src/main/java/org/cherrypic/domain/member/service/MemberServiceImpl.java index 4e3e5438..afc0f3b5 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/member/service/MemberServiceImpl.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/member/service/MemberServiceImpl.java @@ -1,7 +1,7 @@ package org.cherrypic.domain.member.service; import lombok.RequiredArgsConstructor; -import org.cherrypic.domain.image.event.ImageDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImageDeleteEvent; import org.cherrypic.domain.member.dto.request.FcmTokenSaveRequest; import org.cherrypic.domain.member.dto.request.MemberProfileUpdateRequest; import org.cherrypic.domain.member.dto.response.LocalImageDeletionToggleResponse; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/notification/event/NotificationEventListener.java b/cherrypic-api/src/main/java/org/cherrypic/domain/notification/event/NotificationEventListener.java index fd4c143d..fa047b99 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/notification/event/NotificationEventListener.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/notification/event/NotificationEventListener.java @@ -1,7 +1,7 @@ package org.cherrypic.domain.notification.event; import lombok.RequiredArgsConstructor; -import org.cherrypic.domain.album.event.AlbumDeleteNotificationSendEvent; +import org.cherrypic.domain.album.dto.event.AlbumDeleteNotificationSendEvent; import org.cherrypic.domain.notification.service.NotificationService; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/controller/TempAlbumController.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/controller/TempAlbumController.java index 141f803f..7991a7e2 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/controller/TempAlbumController.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/controller/TempAlbumController.java @@ -49,4 +49,11 @@ public ResponseEntity tempAlbumUpdate( tempAlbumService.updateTempAlbum(tempAlbumId, request); return ResponseEntity.noContent().build(); } + + @DeleteMapping("/{tempAlbumId}") + @Operation(summary = "임시 앨범 삭제", description = "임시 앨범을 삭제합니다.") + public ResponseEntity tempAlbumDelete(@PathVariable Long tempAlbumId) { + tempAlbumService.deleteTempAlbum(tempAlbumId); + return ResponseEntity.noContent().build(); + } } diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/dto/event/TempAlbumDeleteEvent.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/dto/event/TempAlbumDeleteEvent.java new file mode 100644 index 00000000..05e37f63 --- /dev/null +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/dto/event/TempAlbumDeleteEvent.java @@ -0,0 +1,9 @@ +package org.cherrypic.domain.tempalbum.dto.event; + +import org.cherrypic.tempalbum.entity.TempAlbum; + +public record TempAlbumDeleteEvent(Long tempAlbumId) { + public static TempAlbumDeleteEvent of(TempAlbum tempAlbum) { + return new TempAlbumDeleteEvent(tempAlbum.getId()); + } +} diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/event/TempAlbumImagesDeleteEvent.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/dto/event/TempAlbumImagesDeleteEvent.java similarity index 82% rename from cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/event/TempAlbumImagesDeleteEvent.java rename to cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/dto/event/TempAlbumImagesDeleteEvent.java index 6f9da80f..737f3dcd 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/event/TempAlbumImagesDeleteEvent.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/dto/event/TempAlbumImagesDeleteEvent.java @@ -1,4 +1,4 @@ -package org.cherrypic.domain.tempalbum.event; +package org.cherrypic.domain.tempalbum.dto.event; import java.util.List; diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/event/TempAlbumEventListener.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/event/TempAlbumEventListener.java index 9feb3331..a0211139 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/event/TempAlbumEventListener.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/event/TempAlbumEventListener.java @@ -1,7 +1,10 @@ package org.cherrypic.domain.tempalbum.event; import lombok.RequiredArgsConstructor; +import org.cherrypic.domain.tempalbum.dto.event.TempAlbumDeleteEvent; +import org.cherrypic.domain.tempalbum.dto.event.TempAlbumImagesDeleteEvent; import org.cherrypic.s3.S3Util; +import org.cherrypic.s3.enums.ImageType; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; @@ -18,4 +21,10 @@ public class TempAlbumEventListener { public void handleTempAlbumImagesDeleteEvent(TempAlbumImagesDeleteEvent event) { s3Util.deleteAllByUrls(event.tempImageUrls()); } + + @Async + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handleTempAlbumDeleteEvent(TempAlbumDeleteEvent event) { + s3Util.deleteAllByImageTypeAndTargetId(ImageType.TEMP_ALBUM_IMAGE, event.tempAlbumId()); + } } diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumImageRepository.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumImageRepository.java index 70c92bc5..bb934a59 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumImageRepository.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumImageRepository.java @@ -2,5 +2,12 @@ import org.cherrypic.tempalbum.entity.TempAlbumImage; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -public interface TempAlbumImageRepository extends JpaRepository {} +public interface TempAlbumImageRepository extends JpaRepository { + + @Modifying(clearAutomatically = true) + @Query("delete from TempAlbumImage i where i.tempAlbum.id = :tempAlbumId") + void deleteAllByTempAlbumId(Long tempAlbumId); +} diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumRepository.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumRepository.java index 3c24391d..f1ae2344 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumRepository.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumRepository.java @@ -4,12 +4,11 @@ import org.cherrypic.tempalbum.entity.TempAlbum; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; public interface TempAlbumRepository extends JpaRepository { long countByMemberId(Long memberId); - @Query("SELECT t FROM TempAlbum t WHERE t.member.id = :memberId ORDER BY t.id DESC") - List findAllByMemberIdOrderByIdDesc(@Param("memberId") Long memberId); + @Query("select t from TempAlbum t where t.member.id = :memberId order by t.id desc") + List findAllByMemberIdOrderByIdDesc(Long memberId); } diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumService.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumService.java index 8b9c7975..a9ec209c 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumService.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumService.java @@ -15,4 +15,6 @@ public interface TempAlbumService { void updateTempAlbum(Long tempAlbumId, TempAlbumUpdateRequest request); TempAlbumInfoResponse getTempAlbum(Long tempAlbumId); + + void deleteTempAlbum(Long tempAlbumId); } diff --git a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumServiceImpl.java b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumServiceImpl.java index c5b56a50..ecbfb3f8 100644 --- a/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumServiceImpl.java +++ b/cherrypic-api/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumServiceImpl.java @@ -3,17 +3,21 @@ import java.util.List; import java.util.Objects; import lombok.RequiredArgsConstructor; +import org.cherrypic.domain.tempalbum.dto.event.TempAlbumDeleteEvent; import org.cherrypic.domain.tempalbum.dto.request.TempAlbumCreateRequest; import org.cherrypic.domain.tempalbum.dto.request.TempAlbumUpdateRequest; import org.cherrypic.domain.tempalbum.dto.response.TempAlbumCreateResponse; import org.cherrypic.domain.tempalbum.dto.response.TempAlbumInfoResponse; import org.cherrypic.domain.tempalbum.dto.response.TempAlbumListResponse; import org.cherrypic.domain.tempalbum.exception.TempAlbumErrorCode; +import org.cherrypic.domain.tempalbum.repository.TempAlbumImageRepository; import org.cherrypic.domain.tempalbum.repository.TempAlbumRepository; import org.cherrypic.exception.CustomException; import org.cherrypic.global.util.MemberUtil; import org.cherrypic.member.entity.Member; +import org.cherrypic.s3.S3Util; import org.cherrypic.tempalbum.entity.TempAlbum; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,8 +27,12 @@ public class TempAlbumServiceImpl implements TempAlbumService { private final MemberUtil memberUtil; + private final S3Util s3Util; private final TempAlbumRepository tempAlbumRepository; + private final TempAlbumImageRepository tempAlbumImageRepository; + + private final ApplicationEventPublisher eventPublisher; @Override @Transactional @@ -74,6 +82,19 @@ public TempAlbumInfoResponse getTempAlbum(Long tempAlbumId) { tempAlbum.getWebUrl()); } + @Override + @Transactional + public void deleteTempAlbum(Long tempAlbumId) { + final Member currentMember = memberUtil.getCurrentMember(); + final TempAlbum tempAlbum = getTempAlbumById(tempAlbumId); + + validateTempAlbumOwner(tempAlbum, currentMember.getId()); + + eventPublisher.publishEvent(TempAlbumDeleteEvent.of(tempAlbum)); + tempAlbumImageRepository.deleteAllByTempAlbumId(tempAlbum.getId()); + tempAlbumRepository.delete(tempAlbum); + } + private void validateTempAlbumCreateLimit(Member member) { long count = tempAlbumRepository.countByMemberId(member.getId()); if (count >= 5) { diff --git a/cherrypic-api/src/test/java/org/cherrypic/album/service/AlbumServiceTest.java b/cherrypic-api/src/test/java/org/cherrypic/album/service/AlbumServiceTest.java index 77307a07..a2149bd8 100644 --- a/cherrypic-api/src/test/java/org/cherrypic/album/service/AlbumServiceTest.java +++ b/cherrypic-api/src/test/java/org/cherrypic/album/service/AlbumServiceTest.java @@ -15,13 +15,13 @@ import org.cherrypic.album.entity.Album; import org.cherrypic.album.entity.InvitationCode; import org.cherrypic.album.enums.AlbumType; +import org.cherrypic.domain.album.dto.event.AlbumDeleteNotificationSendEvent; +import org.cherrypic.domain.album.dto.event.AlbumImagesDeleteEvent; import org.cherrypic.domain.album.dto.request.AlbumCreateRequest; import org.cherrypic.domain.album.dto.request.AlbumUpdateRequest; import org.cherrypic.domain.album.dto.response.AlbumInfoResponse; import org.cherrypic.domain.album.dto.response.AlbumListResponse; import org.cherrypic.domain.album.dto.response.InvitationLinkCreateResponse; -import org.cherrypic.domain.album.event.AlbumDeleteNotificationSendEvent; -import org.cherrypic.domain.album.event.AlbumImagesDeleteEvent; import org.cherrypic.domain.album.exception.AlbumErrorCode; import org.cherrypic.domain.album.repository.AlbumRepository; import org.cherrypic.domain.album.repository.InvitationCodeRepository; @@ -29,8 +29,8 @@ import org.cherrypic.domain.event.repository.EventImageRepository; import org.cherrypic.domain.event.repository.EventRepository; import org.cherrypic.domain.favorites.repository.FavoritesRepository; -import org.cherrypic.domain.image.event.ImageDeleteEvent; -import org.cherrypic.domain.image.event.ImagesDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImageDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImagesDeleteEvent; import org.cherrypic.domain.image.repository.ImageRepository; import org.cherrypic.domain.member.repository.MemberRepository; import org.cherrypic.domain.notification.repository.NotificationRepository; diff --git a/cherrypic-api/src/test/java/org/cherrypic/event/service/EventServiceTest.java b/cherrypic-api/src/test/java/org/cherrypic/event/service/EventServiceTest.java index c55ccb38..233e09f3 100644 --- a/cherrypic-api/src/test/java/org/cherrypic/event/service/EventServiceTest.java +++ b/cherrypic-api/src/test/java/org/cherrypic/event/service/EventServiceTest.java @@ -25,7 +25,7 @@ import org.cherrypic.domain.event.repository.EventImageRepository; import org.cherrypic.domain.event.repository.EventRepository; import org.cherrypic.domain.event.service.EventService; -import org.cherrypic.domain.image.event.ImageDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImageDeleteEvent; import org.cherrypic.domain.image.exception.ImageErrorCode; import org.cherrypic.domain.image.repository.ImageRepository; import org.cherrypic.domain.member.repository.MemberRepository; diff --git a/cherrypic-api/src/test/java/org/cherrypic/image/service/ImageServiceTest.java b/cherrypic-api/src/test/java/org/cherrypic/image/service/ImageServiceTest.java index e57a0d71..72f0e216 100644 --- a/cherrypic-api/src/test/java/org/cherrypic/image/service/ImageServiceTest.java +++ b/cherrypic-api/src/test/java/org/cherrypic/image/service/ImageServiceTest.java @@ -15,16 +15,16 @@ import org.cherrypic.domain.event.exception.EventErrorCode; import org.cherrypic.domain.event.repository.EventImageRepository; import org.cherrypic.domain.event.repository.EventRepository; +import org.cherrypic.domain.image.dto.event.ImagesDeleteEvent; import org.cherrypic.domain.image.dto.request.*; import org.cherrypic.domain.image.dto.response.*; -import org.cherrypic.domain.image.event.ImagesDeleteEvent; import org.cherrypic.domain.image.exception.ImageErrorCode; import org.cherrypic.domain.image.repository.ImageRepository; import org.cherrypic.domain.image.service.ImageService; import org.cherrypic.domain.member.repository.MemberRepository; import org.cherrypic.domain.participant.repository.ParticipantRepository; import org.cherrypic.domain.subscription.repository.SubscriptionRepository; -import org.cherrypic.domain.tempalbum.event.TempAlbumImagesDeleteEvent; +import org.cherrypic.domain.tempalbum.dto.event.TempAlbumImagesDeleteEvent; import org.cherrypic.domain.tempalbum.exception.TempAlbumErrorCode; import org.cherrypic.domain.tempalbum.repository.TempAlbumImageRepository; import org.cherrypic.domain.tempalbum.repository.TempAlbumRepository; diff --git a/cherrypic-api/src/test/java/org/cherrypic/member/service/MemberServiceTest.java b/cherrypic-api/src/test/java/org/cherrypic/member/service/MemberServiceTest.java index 2b54920a..57ce8786 100644 --- a/cherrypic-api/src/test/java/org/cherrypic/member/service/MemberServiceTest.java +++ b/cherrypic-api/src/test/java/org/cherrypic/member/service/MemberServiceTest.java @@ -4,7 +4,7 @@ import org.cherrypic.IntegrationTest; import org.cherrypic.RedisCleaner; -import org.cherrypic.domain.image.event.ImageDeleteEvent; +import org.cherrypic.domain.image.dto.event.ImageDeleteEvent; import org.cherrypic.domain.member.dto.request.FcmTokenSaveRequest; import org.cherrypic.domain.member.dto.request.MemberProfileUpdateRequest; import org.cherrypic.domain.member.dto.response.MemberInfoResponse; diff --git a/cherrypic-api/src/test/java/org/cherrypic/tempalbum/controller/TempAlbumControllerTest.java b/cherrypic-api/src/test/java/org/cherrypic/tempalbum/controller/TempAlbumControllerTest.java index 2b6a9db3..6b6d5a01 100644 --- a/cherrypic-api/src/test/java/org/cherrypic/tempalbum/controller/TempAlbumControllerTest.java +++ b/cherrypic-api/src/test/java/org/cherrypic/tempalbum/controller/TempAlbumControllerTest.java @@ -304,4 +304,51 @@ class 임시_앨범_개별_조회_요청_시 { .andExpect(jsonPath("$.data.message").value("임시 앨범 소유자가 아닌 경우 권한이 없습니다.")); } } + + @Nested + class 임시_앨범_삭제_요청_시 { + + @Test + void 유효한_요청이면_임시_앨범을_삭제한다() throws Exception { + // given + willDoNothing().given(tempAlbumService).deleteTempAlbum(1L); + + // when & then + ResultActions perform = mockMvc.perform(delete("/temp-albums/1")); + + perform.andExpect(status().isNoContent()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.status").value(HttpStatus.NO_CONTENT.value())); + } + + @Test + void 임시_앨범이_존재하지_않는_경우_예외가_발생한다() throws Exception { + // given + willThrow(new CustomException(TempAlbumErrorCode.TEMP_ALBUM_NOT_FOUND)) + .given(tempAlbumService) + .deleteTempAlbum(1L); + + // when & then + ResultActions perform = mockMvc.perform(delete("/temp-albums/1")); + + perform.andExpect(status().isNotFound()) + .andExpect(jsonPath("$.success").value(false)) + .andExpect(jsonPath("$.status").value(HttpStatus.NOT_FOUND.value())); + } + + @Test + void 임시_앨범_소유자가_아닌_경우_예외가_발생한다() throws Exception { + // given + willThrow(new CustomException(TempAlbumErrorCode.NOT_TEMP_ALBUM_OWNER)) + .given(tempAlbumService) + .deleteTempAlbum(1L); + + // when & then + ResultActions perform = mockMvc.perform(delete("/temp-albums/1")); + + perform.andExpect(status().isForbidden()) + .andExpect(jsonPath("$.success").value(false)) + .andExpect(jsonPath("$.status").value(HttpStatus.FORBIDDEN.value())); + } + } } diff --git a/cherrypic-api/src/test/java/org/cherrypic/tempalbum/service/TempAlbumServiceTest.java b/cherrypic-api/src/test/java/org/cherrypic/tempalbum/service/TempAlbumServiceTest.java index f388acf4..f3acf901 100644 --- a/cherrypic-api/src/test/java/org/cherrypic/tempalbum/service/TempAlbumServiceTest.java +++ b/cherrypic-api/src/test/java/org/cherrypic/tempalbum/service/TempAlbumServiceTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -import static org.cherrypic.member.entity.QMember.member; import static org.mockito.BDDMockito.given; import java.math.BigDecimal; @@ -10,11 +9,13 @@ import java.util.List; import org.cherrypic.IntegrationTest; import org.cherrypic.domain.member.repository.MemberRepository; +import org.cherrypic.domain.tempalbum.dto.event.TempAlbumDeleteEvent; import org.cherrypic.domain.tempalbum.dto.request.TempAlbumCreateRequest; import org.cherrypic.domain.tempalbum.dto.request.TempAlbumUpdateRequest; import org.cherrypic.domain.tempalbum.dto.response.TempAlbumInfoResponse; import org.cherrypic.domain.tempalbum.dto.response.TempAlbumListResponse; import org.cherrypic.domain.tempalbum.exception.TempAlbumErrorCode; +import org.cherrypic.domain.tempalbum.repository.TempAlbumImageRepository; import org.cherrypic.domain.tempalbum.repository.TempAlbumRepository; import org.cherrypic.domain.tempalbum.service.TempAlbumService; import org.cherrypic.exception.CustomException; @@ -22,6 +23,7 @@ import org.cherrypic.member.entity.Member; import org.cherrypic.member.entity.OauthInfo; import org.cherrypic.tempalbum.entity.TempAlbum; +import org.cherrypic.tempalbum.entity.TempAlbumImage; import org.cherrypic.tempalbum.enums.TempAlbumType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -29,15 +31,20 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.context.event.ApplicationEvents; +import org.springframework.test.context.event.RecordApplicationEvents; +@RecordApplicationEvents public class TempAlbumServiceTest extends IntegrationTest { @Autowired private TempAlbumService tempAlbumService; @Autowired TempAlbumRepository tempAlbumRepository; @Autowired MemberRepository memberRepository; + @Autowired TempAlbumImageRepository tempAlbumImageRepository; @MockitoBean private MemberUtil memberUtil; + @Autowired private ApplicationEvents applicationEvents; @Nested class 임시_앨범을_성성할_때 { @@ -249,4 +256,68 @@ void setUp() { .hasMessage(TempAlbumErrorCode.NOT_TEMP_ALBUM_OWNER.getMessage()); } } + + @Nested + class 임시_앨범을_삭제할_때 { + + @BeforeEach + void setUp() { + Member member1 = + Member.createMember( + OauthInfo.createOauthInfo("testOauthId", "testOauthProvider"), + "testNickname1", + "testProfileImageUrl1"); + Member member2 = + Member.createMember( + OauthInfo.createOauthInfo("testOauthId", "testOauthProvider"), + "testNickname2", + "testProfileImageUrl2"); + memberRepository.saveAll(List.of(member1, member2)); + given(memberUtil.getCurrentMember()).willReturn(member1); + + TempAlbum tempAlbum1 = TempAlbum.createTempAlbum(member1, "testTitle1"); + TempAlbum tempAlbum2 = TempAlbum.createTempAlbum(member2, "testTitle2"); + tempAlbumRepository.saveAll(List.of(tempAlbum1, tempAlbum2)); + + TempAlbumImage image1 = + TempAlbumImage.createTempAlbumImage(tempAlbum1, "testUrl1", BigDecimal.ONE); + TempAlbumImage image2 = + TempAlbumImage.createTempAlbumImage(tempAlbum1, "testUrl2", BigDecimal.ONE); + tempAlbumImageRepository.saveAll(List.of(image1, image2)); + } + + @Test + void 유효한_요청이면_임시_앨범과_유관_정보를_모두_삭제한다() { + // when + tempAlbumService.deleteTempAlbum(1L); + + // then + var events = applicationEvents.stream(TempAlbumDeleteEvent.class).toList(); + Assertions.assertAll( + () -> assertThat(events.getFirst().tempAlbumId()).isEqualTo(1L), + () -> assertThat(tempAlbumRepository.findById(1L)).isNotPresent(), + () -> + assertThat( + tempAlbumImageRepository + .findAllById(List.of(1L, 2L)) + .isEmpty()) + .isTrue()); + } + + @Test + void 임시_앨범이_존재하지_않는_경우_예외가_발생한다() { + // when & then + assertThatThrownBy(() -> tempAlbumService.deleteTempAlbum(999L)) + .isInstanceOf(CustomException.class) + .hasMessage(TempAlbumErrorCode.TEMP_ALBUM_NOT_FOUND.getMessage()); + } + + @Test + void 임시_앨범_소유자가_아닌_경우_예외가_발생한다() { + // when & then + assertThatThrownBy(() -> tempAlbumService.deleteTempAlbum(2L)) + .isInstanceOf(CustomException.class) + .hasMessage(TempAlbumErrorCode.NOT_TEMP_ALBUM_OWNER.getMessage()); + } + } } diff --git a/cherrypic-batch/build.gradle b/cherrypic-batch/build.gradle index 4e3d479c..3940626b 100644 --- a/cherrypic-batch/build.gradle +++ b/cherrypic-batch/build.gradle @@ -2,9 +2,16 @@ jar { enabled = false } +repositories { + maven { + url = 'https://jitpack.io' + } +} + dependencies { implementation project(':cherrypic-domain') implementation project(':cherrypic-common') + implementation project(':cherrypic-infrastructure') implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' diff --git a/cherrypic-batch/src/main/java/org/cherrypic/domain/subscription/repository/SubscriptionRepository.java b/cherrypic-batch/src/main/java/org/cherrypic/domain/subscription/repository/SubscriptionRepository.java index 590ab2d0..6a7b90fe 100644 --- a/cherrypic-batch/src/main/java/org/cherrypic/domain/subscription/repository/SubscriptionRepository.java +++ b/cherrypic-batch/src/main/java/org/cherrypic/domain/subscription/repository/SubscriptionRepository.java @@ -5,7 +5,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; public interface SubscriptionRepository extends JpaRepository { @Modifying(clearAutomatically = true) @@ -13,5 +12,5 @@ public interface SubscriptionRepository extends JpaRepository { + + @Modifying(clearAutomatically = true) + @Query("delete from TempAlbumImage i where i.tempAlbum.id in :albumIds") + void deleteAllByTempAlbumIds(List albumIds); +} diff --git a/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumRepository.java b/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumRepository.java new file mode 100644 index 00000000..4177210c --- /dev/null +++ b/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/repository/TempAlbumRepository.java @@ -0,0 +1,13 @@ +package org.cherrypic.domain.tempalbum.repository; + +import java.time.LocalDate; +import java.util.List; +import org.cherrypic.tempalbum.entity.TempAlbum; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface TempAlbumRepository extends JpaRepository { + + @Query("select t.id from TempAlbum t where t.expiredAt = :now") + List findAllExpiredIdsToday(LocalDate now); +} diff --git a/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/schedular/TempAlbumExpireScheduler.java b/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/schedular/TempAlbumExpireScheduler.java new file mode 100644 index 00000000..3e0e5eb7 --- /dev/null +++ b/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/schedular/TempAlbumExpireScheduler.java @@ -0,0 +1,18 @@ +package org.cherrypic.domain.tempalbum.schedular; + +import lombok.RequiredArgsConstructor; +import org.cherrypic.domain.tempalbum.job.TempAlbumExpireJob; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class TempAlbumExpireScheduler { + + private final TempAlbumExpireJob tempAlbumExpireJob; + + @Scheduled(cron = "0 0 0 * * *") + public void runTempAlbumExpirejob() { + tempAlbumExpireJob.run(); + } +} diff --git a/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumService.java b/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumService.java new file mode 100644 index 00000000..9a89085b --- /dev/null +++ b/cherrypic-batch/src/main/java/org/cherrypic/domain/tempalbum/service/TempAlbumService.java @@ -0,0 +1,29 @@ +package org.cherrypic.domain.tempalbum.service; + +import java.time.LocalDate; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.cherrypic.domain.tempalbum.repository.TempAlbumImageRepository; +import org.cherrypic.domain.tempalbum.repository.TempAlbumRepository; +import org.cherrypic.s3.S3Util; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class TempAlbumService { + + private final TempAlbumRepository tempAlbumRepository; + private final TempAlbumImageRepository tempAlbumImageRepository; + + private final S3Util s3Util; + + public void expireOverdueTempAlbum() { + List tempAlbumIds = tempAlbumRepository.findAllExpiredIdsToday(LocalDate.now()); + + s3Util.deleteAllTempAlbumImagesInBatch(tempAlbumIds); + tempAlbumImageRepository.deleteAllByTempAlbumIds(tempAlbumIds); + tempAlbumRepository.deleteAllByIdInBatch(tempAlbumIds); + } +} diff --git a/cherrypic-batch/src/main/resources/application-dev.yml b/cherrypic-batch/src/main/resources/application-dev.yml index 1d712c5a..b3e84195 100644 --- a/cherrypic-batch/src/main/resources/application-dev.yml +++ b/cherrypic-batch/src/main/resources/application-dev.yml @@ -14,3 +14,16 @@ spring: flyway: enabled: true baseline-on-migrate: true + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD} + +aws: + access-key-id: ${AWS_ACCESS_KEY_ID} + secret-access-key: ${AWS_SECRET_ACCESS_KEY} + region: ${AWS_REGION} + s3: + bucket: ${S3_BUCKET} + endpoint: ${S3_ENDPOINT:https://s3.ap-northeast-2.amazonaws.com} diff --git a/cherrypic-batch/src/main/resources/application-prod.yml b/cherrypic-batch/src/main/resources/application-prod.yml index 1d712c5a..b3e84195 100644 --- a/cherrypic-batch/src/main/resources/application-prod.yml +++ b/cherrypic-batch/src/main/resources/application-prod.yml @@ -14,3 +14,16 @@ spring: flyway: enabled: true baseline-on-migrate: true + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD} + +aws: + access-key-id: ${AWS_ACCESS_KEY_ID} + secret-access-key: ${AWS_SECRET_ACCESS_KEY} + region: ${AWS_REGION} + s3: + bucket: ${S3_BUCKET} + endpoint: ${S3_ENDPOINT:https://s3.ap-northeast-2.amazonaws.com} diff --git a/cherrypic-batch/src/main/resources/application.yml b/cherrypic-batch/src/main/resources/application.yml index 5906ed17..0563b480 100644 --- a/cherrypic-batch/src/main/resources/application.yml +++ b/cherrypic-batch/src/main/resources/application.yml @@ -12,3 +12,6 @@ management: endpoint: health: access: unrestricted + +fcm: + certification: ${FCM_CERTIFICATION:} diff --git a/cherrypic-batch/src/test/resources/application-test.yml b/cherrypic-batch/src/test/resources/application-test.yml index d3e35dac..7abdbe98 100644 --- a/cherrypic-batch/src/test/resources/application-test.yml +++ b/cherrypic-batch/src/test/resources/application-test.yml @@ -3,3 +3,16 @@ spring: url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;MODE=MYSQL flyway: enabled: false + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT:6379} + password: ${REDIS_PASSWORD} + +aws: + access-key-id: ${AWS_ACCESS_KEY_ID} + secret-access-key: ${AWS_SECRET_ACCESS_KEY} + region: ${AWS_REGION} + s3: + bucket: ${S3_BUCKET} + endpoint: ${S3_ENDPOINT:https://s3.ap-northeast-2.amazonaws.com} diff --git a/cherrypic-infrastructure/src/main/java/org/cherrypic/s3/S3Util.java b/cherrypic-infrastructure/src/main/java/org/cherrypic/s3/S3Util.java index a1a3275d..1a0b4fb7 100644 --- a/cherrypic-infrastructure/src/main/java/org/cherrypic/s3/S3Util.java +++ b/cherrypic-infrastructure/src/main/java/org/cherrypic/s3/S3Util.java @@ -115,6 +115,17 @@ public void deleteAllByImageTypeAndTargetId(ImageType imageType, Long targetId) } while (result.isTruncated()); } + public void deleteAllTempAlbumImagesInBatch(List targetIds) { + if (targetIds == null || targetIds.isEmpty()) { + log.info("deleteAllByImageTypeAndTargetIds skipped: empty targetIds"); + return; + } + + for (Long targetId : targetIds) { + deleteAllByImageTypeAndTargetId(ImageType.TEMP_ALBUM_IMAGE, targetId); + } + } + public void deleteByUrl(String url) { String bucket = s3Properties.bucket(); String objectKey = extractObjectKey(url);