From 799f40e08b5c88e05e1205d8a0a79184a789d630 Mon Sep 17 00:00:00 2001 From: GaBaljaintheroom Date: Mon, 8 Jul 2024 00:39:46 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat=20:=20=EC=BA=A1=EC=8A=90=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/capsule/entity/CapsuleType.java | 7 ++- .../generic_capsule/api/CapsuleApi.java | 32 ++++++++++++++ .../api/CapsuleApiController.java | 17 +++++++ .../repository/capsule/CapsuleRepository.java | 8 ++++ .../repository/image/ImageRepository.java | 8 ++++ .../repository/video/VideoRepository.java | 8 ++++ .../service/CapsuleService.java | 44 ++++++++++++++++++- .../GroupCapsuleOpenRepository.java | 8 ++++ .../member_group/entity/MemberGroup.java | 10 +++-- 9 files changed, 137 insertions(+), 5 deletions(-) diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/CapsuleType.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/CapsuleType.java index 0f6dc063a..fc522e1dc 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/CapsuleType.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/CapsuleType.java @@ -5,5 +5,10 @@ public enum CapsuleType { PUBLIC, GROUP, TREASURE, - ALL + ALL; + + public boolean isGroupCapsule() { + return this.equals(CapsuleType.GROUP); + } + } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApi.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApi.java index f8ea94237..a3a9a95f9 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApi.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApi.java @@ -195,5 +195,37 @@ ResponseEntity> updateCapsuleOpened( @Parameter(in = ParameterIn.PATH, description = "캡슐 아이디", required = true) Long capsuleId ); + + @Operation( + summary = "캡슐 삭제", + description = "캡슐을 삭제한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"capsule"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "처리 완료" + ), + @ApiResponse( + responseCode = "404", + description = "해당 캡슐을 찾을 수 없을 경우 발생하는 예외", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "403", + description = "그룹 캡슐일 경우 그룹장이 아닐 때 발생하는 예외", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ) + }) + ResponseEntity> deleteCapsule( + Long memberId, + + @Parameter(in = ParameterIn.PATH, description = "캡슐 아이디", required = true) + Long capsuleId, + + @Parameter(in = ParameterIn.PATH, description = "캡슐 타입", required = true) + CapsuleType capsuleType + ); } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApiController.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApiController.java index eef579899..fd16b128b 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApiController.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/api/CapsuleApiController.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -151,4 +152,20 @@ public ResponseEntity> updateCapsuleOpened( ); } + @DeleteMapping(value = "/{capsule_id}/{capsule_type}", produces = {"application/json"}) + @Override + public ResponseEntity> deleteCapsule( + @AuthenticationPrincipal final Long memberId, + @PathVariable("capsule_id") final Long capsuleId, + @PathVariable("capsule_type") final CapsuleType capsuleType + ) { + capsuleService.deleteCapsule(memberId, capsuleId, capsuleType); + + return ResponseEntity.ok( + ApiSpec.empty( + SuccessCode.SUCCESS + ) + ); + } + } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/capsule/CapsuleRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/capsule/CapsuleRepository.java index 55ac62035..9dbc8a48b 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/capsule/CapsuleRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/capsule/CapsuleRepository.java @@ -51,4 +51,12 @@ void deleteExcludeGroupCapsuleByMemberId( @Query("select c from Capsule c where c.group.id in :groupIds") List findCapsulesByGroupIds(@Param("groupIds") List groupIds); + + @Query("UPDATE Capsule c SET c.deletedAt = :deletedAt WHERE c.member.id = :memberId and c.id = :capsuleId") + @Modifying + void deleteByMemberIdAndCapsuleId( + @Param("memberId") Long memberId, + @Param("capsuleId") Long capsuleId, + @Param("deletedAt") ZonedDateTime deletedAt + ); } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/image/ImageRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/image/ImageRepository.java index 82531238e..bd8cef57e 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/image/ImageRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/image/ImageRepository.java @@ -25,4 +25,12 @@ void deleteByCapsuleIds( @Param("groupCapsuleIds") List groupCapsuleIds, @Param("deletedAt") ZonedDateTime deletedAt ); + + @Query("UPDATE Image i SET i.deletedAt = :deletedAt WHERE i.member.id =:memberId and i.capsule.id =:capsuleId") + @Modifying + void deleteByMemberIdAndCapsuleId( + @Param("memberId") Long memberId, + @Param("capsuleId") Long capsuleId, + @Param("deletedAt") ZonedDateTime deletedAt + ); } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/video/VideoRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/video/VideoRepository.java index 75d70b565..7f6a358f0 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/video/VideoRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/repository/video/VideoRepository.java @@ -25,4 +25,12 @@ void deleteByCapsuleIds( @Param("groupCapsuleIds") List groupCapsuleIds, @Param("deletedAt") ZonedDateTime deletedAt ); + + @Query("UPDATE Video v SET v.deletedAt = :deletedAt WHERE v.member.id = :memberId and v.capsule.id = :capsuleId") + @Modifying + void deleteByMemberIdAndCapsuleId( + @Param("memberId") Long memberId, + @Param("capsuleId") Long capsuleId, + @Param("deletedAt") ZonedDateTime deletedAt + ); } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/service/CapsuleService.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/service/CapsuleService.java index 3b5d111b5..9757c455f 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/service/CapsuleService.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/generic_capsule/service/CapsuleService.java @@ -17,10 +17,14 @@ import site.timecapsulearchive.core.domain.capsule.generic_capsule.repository.capsule.CapsuleRepository; import site.timecapsulearchive.core.domain.capsule.generic_capsule.repository.image.ImageRepository; import site.timecapsulearchive.core.domain.capsule.generic_capsule.repository.video.VideoRepository; +import site.timecapsulearchive.core.domain.capsule.group_capsule.repository.GroupCapsuleOpenRepository; import site.timecapsulearchive.core.domain.capsuleskin.entity.CapsuleSkin; import site.timecapsulearchive.core.domain.capsuleskin.repository.CapsuleSkinRepository; import site.timecapsulearchive.core.domain.friend.repository.member_friend.MemberFriendRepository; import site.timecapsulearchive.core.domain.member.entity.Member; +import site.timecapsulearchive.core.domain.member_group.entity.MemberGroup; +import site.timecapsulearchive.core.domain.member_group.repository.member_group_repository.MemberGroupRepository; +import site.timecapsulearchive.core.global.common.supplier.ZonedDateTimeSupplier; import site.timecapsulearchive.core.global.geography.GeoTransformManager; @Service @@ -30,6 +34,8 @@ public class CapsuleService { private final CapsuleRepository capsuleRepository; private final MemberFriendRepository memberFriendRepository; + private final MemberGroupRepository memberGroupRepository; + private final GroupCapsuleOpenRepository groupCapsuleOpenRepository; private final GeoTransformManager geoTransformManager; private final ImageRepository imageRepository; private final VideoRepository videoRepository; @@ -152,10 +158,46 @@ public List findFriendsARCapsulesByCurrentLocation( } @Transactional - public void deleteRelatedAllCapsuleByMemberId(final Long memberId, final ZonedDateTime deletedAt) { + public void deleteRelatedAllCapsuleByMemberId( + final Long memberId, + final ZonedDateTime deletedAt + ) { imageRepository.deleteByMemberId(memberId, deletedAt); videoRepository.deleteByMemberId(memberId, deletedAt); capsuleSkinRepository.deleteByMemberId(memberId, deletedAt); capsuleRepository.deleteExcludeGroupCapsuleByMemberId(memberId, deletedAt); } + + @Transactional + public void deleteCapsule( + final Long memberId, + final Long capsuleId, + final CapsuleType capsuleType + ) { + final ZonedDateTime deletedAt = ZonedDateTimeSupplier.utc().get(); + + if (capsuleType.isGroupCapsule()) { + validateGroupCapsuleOwnership(memberId, capsuleId); + } + + deleteCapsuleAssets(memberId, capsuleId, deletedAt); + } + + private void validateGroupCapsuleOwnership(final Long memberId, final Long capsuleId) { + final Capsule capsule = capsuleRepository.findCapsuleByMemberIdAndCapsuleId(memberId, capsuleId) + .orElseThrow(CapsuleNotFondException::new); + final Long groupId = capsule.getGroup().getId(); + + final MemberGroup memberGroup = memberGroupRepository.findMemberGroupByMemberIdAndGroupId(memberId, groupId) + .orElseThrow(CapsuleNotFondException::new); + memberGroup.checkDeleteGroupCapsuleAuthority(); + } + + private void deleteCapsuleAssets(final Long memberId, final Long capsuleId, final ZonedDateTime deletedAt) { + imageRepository.deleteByMemberIdAndCapsuleId(memberId, capsuleId, deletedAt); + videoRepository.deleteByMemberIdAndCapsuleId(memberId, capsuleId, deletedAt); + groupCapsuleOpenRepository.deleteByMemberIdAndCapsuleId(memberId, capsuleId, deletedAt); + capsuleRepository.deleteByMemberIdAndCapsuleId(memberId, capsuleId, deletedAt); + } + } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/repository/GroupCapsuleOpenRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/repository/GroupCapsuleOpenRepository.java index 155bf2196..8fba88557 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/repository/GroupCapsuleOpenRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/repository/GroupCapsuleOpenRepository.java @@ -26,4 +26,12 @@ void deleteByCapsuleIds( @Param("groupCapsuleIds") List groupCapsuleIds, @Param("deletedAt") ZonedDateTime deletedAt ); + + @Query("UPDATE GroupCapsuleOpen gco SET gco.deletedAt = :deletedAt WHERE gco.member.id = :memberId and gco.capsule.id = :capsuleId") + @Modifying + void deleteByMemberIdAndCapsuleId( + @Param("memberId") Long memberId, + @Param("capsuleId") Long capsuleId, + @Param("deletedAt") ZonedDateTime deletedAt + ); } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/member_group/entity/MemberGroup.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/member_group/entity/MemberGroup.java index 370e2fa8e..91c6d357e 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/member_group/entity/MemberGroup.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/member_group/entity/MemberGroup.java @@ -12,11 +12,9 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import org.hibernate.annotations.Filter; -import org.hibernate.annotations.FilterDef; -import org.hibernate.annotations.ParamDef; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; +import site.timecapsulearchive.core.domain.capsule.exception.NoCapsuleAuthorityException; import site.timecapsulearchive.core.domain.group.entity.Group; import site.timecapsulearchive.core.domain.member.entity.Member; import site.timecapsulearchive.core.domain.member_group.exception.GroupQuitException; @@ -65,4 +63,10 @@ public void checkGroupMemberOwner() { throw new GroupQuitException(); } } + + public void checkDeleteGroupCapsuleAuthority() { + if (!this.isOwner) { + throw new NoCapsuleAuthorityException(); + } + } } From b255b4a21c6e5633a836a889bab58e53dd243e1d Mon Sep 17 00:00:00 2001 From: GaBaljaintheroom Date: Mon, 8 Jul 2024 21:58:11 +0900 Subject: [PATCH 2/2] =?UTF-8?q?chore=20:=20=EA=B5=AC=EA=B8=80=20=EC=9E=90?= =?UTF-8?q?=EB=B0=94=20=ED=98=95=EC=8B=9D=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/MessageVerificationService.java | 8 ------ .../core/domain/capsule/entity/Capsule.java | 25 +++++-------------- .../service/CapsuleService.java | 9 ++++--- .../group_capsule/api/GroupCapsuleApi.java | 2 +- .../api/GroupCapsuleApiController.java | 1 - .../data/response/GroupCapsuleResponse.java | 1 + .../GroupCapsuleQueryRepository.java | 5 ++-- .../service/GroupCapsuleService.java | 5 ++-- .../CapsuleSkinQueryRepositoryImpl.java | 2 -- .../member/api/MemberApiController.java | 4 +-- .../domain/member/service/MemberService.java | 3 ++- .../common/dependency/UnitTestDependency.java | 1 - .../common/fixture/dto/CapsuleDtoFixture.java | 20 +++++++++------ .../dto/GroupCapsuleMemberDtoFixture.java | 5 ++-- .../MessageVerificationServiceTest.java | 7 ------ .../GroupCapsuleQueryRepositoryTest.java | 2 +- .../service/GroupCapsuleServiceTest.java | 18 +++---------- .../repository/MediaQueryRepositoryTest.java | 1 - .../PublicCapsuleQueryRepositoryTest.java | 2 -- .../service/PublicCapsuleServiceTest.java | 1 - .../command/FriendCommandServiceTest.java | 6 +++-- .../member/service/MemberServiceTest.java | 1 - .../jwt/JwtAuthenticationProviderTest.java | 6 +++-- .../global/security/jwt/JwtFactoryTest.java | 2 +- 24 files changed, 54 insertions(+), 83 deletions(-) diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/auth/service/MessageVerificationService.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/auth/service/MessageVerificationService.java index 72c75d72f..f52f94110 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/auth/service/MessageVerificationService.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/auth/service/MessageVerificationService.java @@ -4,20 +4,12 @@ import java.security.SecureRandom; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.dao.DataIntegrityViolationException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import site.timecapsulearchive.core.domain.auth.data.dto.VerificationMessageSendDto; import site.timecapsulearchive.core.domain.auth.exception.CertificationNumberNotFoundException; import site.timecapsulearchive.core.domain.auth.exception.CertificationNumberNotMatchException; import site.timecapsulearchive.core.domain.auth.repository.MessageAuthenticationCacheRepository; -import site.timecapsulearchive.core.domain.member.entity.Member; -import site.timecapsulearchive.core.domain.member.entity.MemberTemporary; -import site.timecapsulearchive.core.domain.member.exception.MemberNotFoundException; -import site.timecapsulearchive.core.domain.member.exception.MemberTagDuplicatedException; -import site.timecapsulearchive.core.domain.member.repository.MemberRepository; -import site.timecapsulearchive.core.domain.member.repository.MemberTemporaryRepository; -import site.timecapsulearchive.core.global.security.encryption.AESEncryptionManager; import site.timecapsulearchive.core.global.security.encryption.HashEncryptionManager; import site.timecapsulearchive.core.infra.sms.data.response.SmsApiResponse; import site.timecapsulearchive.core.infra.sms.manager.SmsApiManager; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/Capsule.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/Capsule.java index 824d897b5..18548adf6 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/Capsule.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/entity/Capsule.java @@ -1,6 +1,5 @@ package site.timecapsulearchive.core.domain.capsule.entity; -import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Embedded; import jakarta.persistence.Entity; @@ -40,45 +39,33 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Capsule extends BaseEntity { + @OneToMany(mappedBy = "capsule") + private final List images = new ArrayList<>(); + @OneToMany(mappedBy = "capsule") + private final List