diff --git a/backend/src/main/java/com/mapbefine/mapbefine/admin/application/AdminCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/admin/application/AdminCommandService.java index d19f772c..e957fd79 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/admin/application/AdminCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/admin/application/AdminCommandService.java @@ -1,5 +1,6 @@ package com.mapbefine.mapbefine.admin.application; +import static com.mapbefine.mapbefine.pin.exception.PinErrorCode.PIN_NOT_FOUND; import static com.mapbefine.mapbefine.topic.exception.TopicErrorCode.TOPIC_NOT_FOUND; import com.mapbefine.mapbefine.atlas.domain.AtlasRepository; @@ -11,14 +12,14 @@ import com.mapbefine.mapbefine.pin.domain.Pin; import com.mapbefine.mapbefine.pin.domain.PinImageRepository; import com.mapbefine.mapbefine.pin.domain.PinRepository; +import com.mapbefine.mapbefine.pin.exception.PinException.PinNotFoundException; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; import com.mapbefine.mapbefine.topic.exception.TopicException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.util.List; import java.util.NoSuchElementException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @Transactional @@ -61,12 +62,15 @@ private void deleteAllRelatedMember(Member member) { List pinIds = extractPinIdsByMember(member); Long memberId = member.getId(); - pinImageRepository.deleteAllByPinIds(pinIds); - topicRepository.deleteAllByMemberId(memberId); - pinRepository.deleteAllByMemberId(memberId); permissionRepository.deleteAllByMemberId(memberId); + permissionRepository.flush(); atlasRepository.deleteAllByMemberId(memberId); + atlasRepository.flush(); bookmarkRepository.deleteAllByMemberId(memberId); + bookmarkRepository.flush(); + pinImageRepository.deleteAllByPinIds(pinIds); + pinRepository.deleteAllByMemberId(memberId); + topicRepository.deleteAllByMemberId(memberId); } private Member findMemberById(Long id) { @@ -82,21 +86,44 @@ private List extractPinIdsByMember(Member member) { } public void deleteTopic(Long topicId) { + Topic topic = findTopicById(topicId); + List pinIds = extractPinIdsByTopic(topic); + + permissionRepository.deleteAllByTopicId(topicId); + permissionRepository.flush(); + atlasRepository.deleteAllByTopicId(topicId); + atlasRepository.flush(); + bookmarkRepository.deleteAllByTopicId(topicId); + bookmarkRepository.flush(); + pinImageRepository.deleteAllByPinIds(pinIds); + pinRepository.deleteAllByTopicId(topicId); topicRepository.deleteById(topicId); } + private List extractPinIdsByTopic(Topic topic) { + return topic.getPins() + .stream() + .map(Pin::getId) + .toList(); + } + public void deleteTopicImage(Long topicId) { Topic topic = findTopicById(topicId); topic.removeImage(); } private Topic findTopicById(Long topicId) { - return topicRepository.findByIdAndIsDeletedFalse(topicId) + return topicRepository.findById(topicId) .orElseThrow(() -> new TopicException.TopicNotFoundException(TOPIC_NOT_FOUND, List.of(topicId))); } public void deletePin(Long pinId) { - pinRepository.deleteById(pinId); + Pin pin = pinRepository.findById(pinId) + .orElseThrow(() -> new PinNotFoundException(PIN_NOT_FOUND, pinId)); + + pin.decreaseTopicPinCount(); + pinImageRepository.deleteAllByPinId(pinId); + pinRepository.deleteById(pin.getId()); } public void deletePinImage(Long pinImageId) { diff --git a/backend/src/main/java/com/mapbefine/mapbefine/atlas/application/AtlasCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/atlas/application/AtlasCommandService.java index b1db0033..95c9588b 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/atlas/application/AtlasCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/atlas/application/AtlasCommandService.java @@ -13,11 +13,10 @@ import com.mapbefine.mapbefine.member.domain.MemberRepository; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.util.NoSuchElementException; import java.util.Objects; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @Transactional @@ -73,7 +72,7 @@ private boolean isTopicAlreadyAdded(Long topicId, Long memberId) { } private Topic findTopicById(Long topicId) { - return topicRepository.findByIdAndIsDeletedFalse(topicId) + return topicRepository.findById(topicId) .orElseThrow(() -> new AtlasBadRequestException(ILLEGAL_TOPIC_ID)); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/atlas/domain/AtlasRepository.java b/backend/src/main/java/com/mapbefine/mapbefine/atlas/domain/AtlasRepository.java index 664cdc94..ede90f46 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/atlas/domain/AtlasRepository.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/atlas/domain/AtlasRepository.java @@ -7,7 +7,10 @@ public interface AtlasRepository extends JpaRepository { boolean existsByMemberIdAndTopicId(Long memberId, Long topicId); + void deleteByMemberIdAndTopicId(Long memberId, Long topicId); void deleteAllByMemberId(Long memberId); + + void deleteAllByTopicId(Long topicId); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/auth/application/AuthService.java b/backend/src/main/java/com/mapbefine/mapbefine/auth/application/AuthService.java index d35346de..e4419cf6 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/auth/application/AuthService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/auth/application/AuthService.java @@ -11,7 +11,6 @@ import com.mapbefine.mapbefine.topic.domain.Topic; import java.util.List; import java.util.Objects; -import java.util.Optional; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -32,9 +31,12 @@ public void validateMember(Long memberId) { } public AuthMember findAuthMemberByMemberId(Long memberId) { - return memberRepository.findById(memberId) - .map(this::convertToAuthMember) + Member member = memberRepository.findById(memberId) .orElseThrow(() -> new IllegalArgumentException("findAuthMemberByMemberId; memberId= " + memberId)); + if (member.isNormalStatus()) { + return convertToAuthMember(member); + } + throw new AuthUnauthorizedException(AuthErrorCode.ILLEGAL_MEMBER_ID); } private AuthMember convertToAuthMember(Member member) { @@ -63,15 +65,4 @@ private List getCreatedTopics(Member member) { .toList(); } - public boolean isAdmin(Long memberId) { - if (Objects.isNull(memberId)) { - return false; - } - - Optional member = memberRepository.findById(memberId); - - return member.map(Member::isAdmin) - .orElse(false); - } - } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/bookmark/application/BookmarkCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/bookmark/application/BookmarkCommandService.java index 7c9828dc..b0dea0ea 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/bookmark/application/BookmarkCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/bookmark/application/BookmarkCommandService.java @@ -15,11 +15,10 @@ import com.mapbefine.mapbefine.member.domain.MemberRepository; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; +import java.util.NoSuchElementException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.NoSuchElementException; - @Service @Transactional public class BookmarkCommandService { @@ -53,7 +52,7 @@ public Long addTopicInBookmark(AuthMember authMember, Long topicId) { } private Topic getTopicById(Long topicId) { - return topicRepository.findByIdAndIsDeletedFalse(topicId) + return topicRepository.findById(topicId) .orElseThrow(() -> new BookmarkBadRequestException(ILLEGAL_TOPIC_ID)); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/bookmark/domain/BookmarkRepository.java b/backend/src/main/java/com/mapbefine/mapbefine/bookmark/domain/BookmarkRepository.java index 34c58d3c..bb97144c 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/bookmark/domain/BookmarkRepository.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/bookmark/domain/BookmarkRepository.java @@ -1,17 +1,16 @@ package com.mapbefine.mapbefine.bookmark.domain; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; - import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; public interface BookmarkRepository extends JpaRepository { + Optional findByMemberIdAndTopicId(Long memberId, Long topicId); + boolean existsByMemberIdAndTopicId(Long memberId, Long topicId); - @Modifying(clearAutomatically = true) void deleteAllByMemberId(Long memberId); - Optional findByMemberIdAndTopicId(Long memberId, Long topicId); + void deleteAllByTopicId(Long topicId); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberCommandService.java index d7c315f4..f3360468 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberCommandService.java @@ -26,7 +26,7 @@ public void updateInfoById(AuthMember authMember, MemberUpdateRequest request) { validateNicknameDuplicated(nickName); - member.update(nickName); + member.updateNickName(nickName); } private Member findMemberById(Long memberId) { diff --git a/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberQueryService.java b/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberQueryService.java index 76c3af98..688a6db5 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberQueryService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/member/application/MemberQueryService.java @@ -66,7 +66,7 @@ public List findAll() { public List findAllTopicsInBookmark(AuthMember authMember) { Member member = findMemberById(authMember.getMemberId()); - List bookMarkedTopics = topicRepository.findTopicsByBookmarksMemberIdAndIsDeletedFalse( + List bookMarkedTopics = topicRepository.findTopicsByBookmarksMemberId( authMember.getMemberId()); return bookMarkedTopics.stream() .map(topic -> TopicResponse.from( @@ -90,7 +90,6 @@ private boolean isInAtlas(Long memberId, Long topicId) { public List findAllTopicsInAtlas(AuthMember authMember) { Member member = findMemberById(authMember.getMemberId()); - List topicsInAtlas = findTopicsInAtlas(member); return topicsInAtlas.stream() @@ -107,7 +106,6 @@ private boolean isInBookmark(Long memberId, Long topicId) { } public List findMyAllTopics(AuthMember authMember) { - Long memberId = authMember.getMemberId(); Member member = findMemberById(memberId); diff --git a/backend/src/main/java/com/mapbefine/mapbefine/member/domain/Member.java b/backend/src/main/java/com/mapbefine/mapbefine/member/domain/Member.java index 31ebf243..865e3f0b 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/member/domain/Member.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/member/domain/Member.java @@ -104,12 +104,16 @@ private static String createNicknameSuffix() { .substring(0, DEFAULT_NICKNAME_SUFFIX_LENGTH); } - public void update( + public void updateNickName( String nickName ) { memberInfo = memberInfo.createUpdatedMemberInfo(nickName); } + public void updateStatus(Status status) { + memberInfo = memberInfo.createUpdatedMemberInfo(status); + } + public void addTopic(Topic topic) { createdTopics.add(topic); } @@ -147,14 +151,4 @@ public List getTopicsWithPermissions() { public boolean isNormalStatus() { return memberInfo.getStatus() == Status.NORMAL; } - - public void updateStatus(Status status) { - memberInfo = MemberInfo.of( - memberInfo.getNickName(), - memberInfo.getEmail(), - memberInfo.getImageUrl(), - memberInfo.getRole(), - status - ); - } } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java b/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java index ee36d80a..5bedae4b 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/member/domain/MemberInfo.java @@ -113,6 +113,11 @@ public MemberInfo createUpdatedMemberInfo(String nickName) { return MemberInfo.of(nickName, this.email, this.imageUrl.getImageUrl(), this.role, this.status); } + public MemberInfo createUpdatedMemberInfo(Status status) { + + return MemberInfo.of(this.nickName, this.email, this.imageUrl.getImageUrl(), this.role, status); + } + public String getImageUrl() { return imageUrl.getImageUrl(); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionCommandService.java index 62865a7a..1e35500b 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionCommandService.java @@ -15,11 +15,10 @@ import com.mapbefine.mapbefine.permission.exception.PermissionException.PermissionForbiddenException; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.util.List; import java.util.Objects; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @Transactional @@ -78,7 +77,7 @@ private boolean isNotSelfMember(AuthMember authMember, Member member) { private Topic findTopic(PermissionRequest request) { Long topicId = request.topicId(); - return topicRepository.findByIdAndIsDeletedFalse(topicId) + return topicRepository.findById(topicId) .orElseThrow(() -> new PermissionBadRequestException(ILLEGAL_TOPIC_ID, topicId)); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionQueryService.java b/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionQueryService.java index 5afb2404..e9602c73 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionQueryService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/permission/application/PermissionQueryService.java @@ -14,11 +14,10 @@ import com.mapbefine.mapbefine.topic.domain.TopicStatus; import com.mapbefine.mapbefine.topic.exception.TopicErrorCode; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicNotFoundException; +import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Service @Transactional(readOnly = true) public class PermissionQueryService { @@ -43,12 +42,13 @@ public TopicAccessDetailResponse findTopicAccessDetailById(Long topicId) { } private Publicity findTopicPublicityById(Long topicId) { - return topicRepository.findByIdAndIsDeletedFalse(topicId) + return topicRepository.findById(topicId) .map(Topic::getTopicStatus) .map(TopicStatus::getPublicity) .orElseThrow(() -> new TopicNotFoundException(TopicErrorCode.TOPIC_NOT_FOUND, topicId)); } + @Deprecated(since = "2023.10.06") public PermissionMemberDetailResponse findPermissionById(Long permissionId) { Permission permission = permissionRepository.findById(permissionId) .orElseThrow(() -> new PermissionNotFoundException(PERMISSION_NOT_FOUND, permissionId)); diff --git a/backend/src/main/java/com/mapbefine/mapbefine/permission/domain/PermissionRepository.java b/backend/src/main/java/com/mapbefine/mapbefine/permission/domain/PermissionRepository.java index 7d14c061..c8300ce4 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/permission/domain/PermissionRepository.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/permission/domain/PermissionRepository.java @@ -10,4 +10,7 @@ public interface PermissionRepository extends JpaRepository { boolean existsByTopicIdAndMemberId(Long topicId, Long memberId); void deleteAllByMemberId(Long memberId); + + void deleteAllByTopicId(Long topicId); + } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/permission/presentation/PermissionController.java b/backend/src/main/java/com/mapbefine/mapbefine/permission/presentation/PermissionController.java index 188348ea..e26be0e7 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/permission/presentation/PermissionController.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/permission/presentation/PermissionController.java @@ -56,8 +56,7 @@ public ResponseEntity findTopicAccessDetailByTopicId( return ResponseEntity.ok(response); } - // TODO 이 API를 쓰는 곳이 있나? + 결국 특정 회원을 조회하는 건데 어떤 API인지 알기 어렵다.. - // 회원 정보 조회는 /members 에서 하는 걸로 충분하지 않나? 재사용성이 떨어진다. 테스트의 DisplayName도 매칭이 안된다. + @Deprecated(since = "2023.10.06") @LoginRequired @GetMapping("/{permissionId}") public ResponseEntity findPermissionById(@PathVariable Long permissionId) { diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java index c91628c1..1f3344a3 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinCommandService.java @@ -27,13 +27,12 @@ import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicBadRequestException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; @Transactional @Service @@ -100,7 +99,7 @@ private Topic findTopic(Long topicId) { if (Objects.isNull(topicId)) { throw new TopicBadRequestException(ILLEGAL_TOPIC_ID); } - return topicRepository.findByIdAndIsDeletedFalse(topicId) + return topicRepository.findById(topicId) .orElseThrow(() -> new TopicBadRequestException(ILLEGAL_TOPIC_ID)); } @@ -146,7 +145,7 @@ public void update( } private Pin findPin(Long pinId) { - return pinRepository.findByIdAndIsDeletedFalse(pinId) + return pinRepository.findById(pinId) .orElseThrow(() -> new PinBadRequestException(ILLEGAL_PIN_ID)); } @@ -182,7 +181,7 @@ public void removeImageById(AuthMember authMember, Long pinImageId) { } private PinImage findPinImage(Long pinImageId) { - return pinImageRepository.findByIdAndIsDeletedFalse(pinImageId) + return pinImageRepository.findById(pinImageId) .orElseThrow(() -> new PinBadRequestException(ILLEGAL_PIN_IMAGE_ID)); } diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinQueryService.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinQueryService.java index 441752d7..b2f4a1c0 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinQueryService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/application/PinQueryService.java @@ -11,11 +11,10 @@ import com.mapbefine.mapbefine.pin.exception.PinException.PinForbiddenException; import com.mapbefine.mapbefine.pin.exception.PinException.PinNotFoundException; import com.mapbefine.mapbefine.topic.domain.Topic; +import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - @Transactional(readOnly = true) @Service public class PinQueryService { @@ -27,7 +26,7 @@ public PinQueryService(PinRepository pinRepository) { } public List findAllReadable(AuthMember member) { - return pinRepository.findAllByIsDeletedFalse() + return pinRepository.findAll() .stream() .filter(pin -> member.canRead(pin.getTopic())) .map(PinResponse::from) @@ -35,7 +34,7 @@ public List findAllReadable(AuthMember member) { } public PinDetailResponse findDetailById(AuthMember member, Long pinId) { - Pin pin = pinRepository.findByIdAndIsDeletedFalse(pinId) + Pin pin = pinRepository.findById(pinId) .orElseThrow(() -> new PinNotFoundException(PIN_NOT_FOUND, pinId)); validateReadAuth(member, pin.getTopic()); @@ -51,7 +50,7 @@ private void validateReadAuth(AuthMember member, Topic topic) { } public List findAllPinsByMemberId(AuthMember authMember, Long memberId) { - return pinRepository.findAllByCreatorIdAndIsDeletedFalse(memberId) + return pinRepository.findAllByCreatorId(memberId) .stream() .filter(pin -> authMember.canRead(pin.getTopic())) .map(PinResponse::from) diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/Pin.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/Pin.java index 8081b913..bfabaa86 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/Pin.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/Pin.java @@ -24,10 +24,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.Where; @Entity @NoArgsConstructor(access = PROTECTED) @Getter +@Where(clause = "is_deleted = false") public class Pin extends BaseTimeEntity { @Id @@ -102,6 +104,10 @@ public void updatePinInfo(String name, String description) { pinInfo = PinInfo.of(name, description); } + public void decreaseTopicPinCount() { + topic.decreasePinCount(); + } + public void copyToTopic(Member creator, Topic topic) { Pin copiedPin = Pin.createPinAssociatedWithLocationAndTopicAndMember( pinInfo.getName(), diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java index 4da68c7d..66a3136e 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImage.java @@ -14,10 +14,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.Where; @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter +@Where(clause = "is_deleted = false") public class PinImage extends BaseTimeEntity { @Id diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImageRepository.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImageRepository.java index 818b0d5a..fb10e25d 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImageRepository.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinImageRepository.java @@ -1,20 +1,19 @@ package com.mapbefine.mapbefine.pin.domain; +import java.util.List; +import java.util.Optional; 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; import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; - @Repository public interface PinImageRepository extends JpaRepository { - Optional findByIdAndIsDeletedFalse(Long pinId); + Optional findById(Long pinId); - List findAllByPinIdAndIsDeletedFalse(Long pinId); + List findAllByPinId(Long pinId); @Modifying(clearAutomatically = true) @Query("update PinImage p set p.isDeleted = true where p.pin.id = :pinId") diff --git a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinRepository.java b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinRepository.java index 3b221b9d..00827a6a 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinRepository.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/pin/domain/PinRepository.java @@ -1,5 +1,6 @@ package com.mapbefine.mapbefine.pin.domain; +import java.util.List; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -7,23 +8,15 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; - @Repository public interface PinRepository extends JpaRepository { @EntityGraph(attributePaths = {"location", "topic", "creator", "pinImages"}) - List findAllByIsDeletedFalse(); - - Optional findByIdAndIsDeletedFalse(Long pinId); + List findAll(); @EntityGraph(attributePaths = {"location", "topic", "creator", "pinImages"}) List findAllByTopicId(Long topicId); - @EntityGraph(attributePaths = {"location", "topic", "creator", "pinImages"}) - List findAllByCreatorIdAndIsDeletedFalse(Long creatorId); - @EntityGraph(attributePaths = {"location", "topic", "creator", "pinImages"}) List findAllByCreatorId(Long creatorId); diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java index f6030259..5c86b3b7 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicCommandService.java @@ -22,14 +22,13 @@ import com.mapbefine.mapbefine.topic.dto.request.TopicUpdateRequest; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicBadRequestException; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicForbiddenException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; - import java.util.Collection; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; @Transactional @Service @@ -94,7 +93,9 @@ private Member findCreatorByAuthMember(AuthMember member) { } return memberRepository.findById(memberId) - .orElseThrow(() -> new NoSuchElementException("findCreatorByAuthMember; member not found; id=" + memberId)); + .orElseThrow( + () -> new NoSuchElementException("findCreatorByAuthMember; member not found; id=" + memberId) + ); } private void copyPinsToTopic( @@ -160,7 +161,7 @@ private Topic convertToTopic(AuthMember member, TopicMergeRequest request) { } private List findAllTopics(List topicIds) { - List findTopics = topicRepository.findByIdInAndIsDeletedFalse(topicIds); + List findTopics = topicRepository.findByIdIn(topicIds); if (topicIds.size() != findTopics.size()) { throw new TopicBadRequestException(ILLEGAL_TOPIC_ID); @@ -196,7 +197,7 @@ public void copyPin(AuthMember member, Long topicId, List pinIds) { } private Topic findTopic(Long topicId) { - return topicRepository.findByIdAndIsDeletedFalse(topicId) + return topicRepository.findById(topicId) .orElseThrow(() -> new TopicBadRequestException(ILLEGAL_TOPIC_ID)); } @@ -233,15 +234,17 @@ private void validateUpdateAuth(AuthMember member, Topic topic) { throw new TopicForbiddenException(FORBIDDEN_TOPIC_UPDATE); } + @Deprecated(since = "2023.10.06") public void delete(AuthMember member, Long topicId) { Topic topic = findTopic(topicId); - validateDeleteAuth(member, topic); + /// TODO: 2023/10/06 연관관계 다 삭제해야 하는데, 관리자 API와 중복 로직이며 관리자 API에서만 사용됨 pinRepository.deleteAllByTopicId(topicId); topicRepository.deleteById(topicId); } + @Deprecated(since = "2023.10.06") private void validateDeleteAuth(AuthMember member, Topic topic) { if (member.canDelete(topic)) { return; diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicQueryService.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicQueryService.java index 5404bfef..053d9012 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicQueryService.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/application/TopicQueryService.java @@ -14,13 +14,12 @@ import com.mapbefine.mapbefine.topic.dto.response.TopicResponse; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicForbiddenException; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicNotFoundException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.util.Comparator; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @Transactional(readOnly = true) @@ -51,7 +50,7 @@ public List findAllReadable(AuthMember authMember) { } private List getGuestTopicResponses(AuthMember authMember) { - return topicRepository.findAllByIsDeletedFalse() + return topicRepository.findAll() .stream() .filter(authMember::canRead) .map(TopicResponse::fromGuestQuery) @@ -61,7 +60,7 @@ private List getGuestTopicResponses(AuthMember authMember) { private List getUserTopicResponses(AuthMember authMember) { Member member = findMemberById(authMember.getMemberId()); - return topicRepository.findAllByIsDeletedFalse().stream() + return topicRepository.findAll().stream() .filter(authMember::canRead) .map(topic -> TopicResponse.from( topic, @@ -103,7 +102,7 @@ public TopicDetailResponse findDetailById(AuthMember authMember, Long topicId) { } private Topic findTopic(Long id) { - return topicRepository.findByIdAndIsDeletedFalse(id) + return topicRepository.findById(id) .orElseThrow(() -> new TopicNotFoundException(TOPIC_NOT_FOUND, List.of(id))); } @@ -116,7 +115,7 @@ private void validateReadableTopic(AuthMember member, Topic topic) { } public List findDetailsByIds(AuthMember authMember, List ids) { - List topics = topicRepository.findByIdInAndIsDeletedFalse(ids); + List topics = topicRepository.findByIdIn(ids); validateTopicsCount(ids, topics); validateReadableTopics(authMember, topics); @@ -163,7 +162,7 @@ private void validateReadableTopics(AuthMember member, List topics) { public List findAllTopicsByMemberId(AuthMember authMember, Long memberId) { if (Objects.isNull(authMember.getMemberId())) { - return topicRepository.findAllByCreatorIdAndIsDeletedFalse(memberId) + return topicRepository.findAllByCreatorId(memberId) .stream() .filter(authMember::canRead) .map(TopicResponse::fromGuestQuery) @@ -172,7 +171,7 @@ public List findAllTopicsByMemberId(AuthMember authMember, Long m Member member = findMemberById(authMember.getMemberId()); - return topicRepository.findAllByCreatorIdAndIsDeletedFalse(memberId) + return topicRepository.findAllByCreatorId(memberId) .stream() .filter(authMember::canRead) .map(topic -> TopicResponse.from( @@ -193,7 +192,7 @@ public List findAllByOrderByUpdatedAtDesc(AuthMember authMember) private List getUserNewestTopicResponse(AuthMember authMember) { Member member = findMemberById(authMember.getMemberId()); - return topicRepository.findAllByIsDeletedFalseOrderByLastPinUpdatedAtDesc() + return topicRepository.findAllByOrderByLastPinUpdatedAtDesc() .stream() .filter(authMember::canRead) .map(topic -> TopicResponse.from( @@ -205,7 +204,7 @@ private List getUserNewestTopicResponse(AuthMember authMember) { } private List getGuestNewestTopicResponse(AuthMember authMember) { - return topicRepository.findAllByIsDeletedFalseOrderByLastPinUpdatedAtDesc() + return topicRepository.findAllByOrderByLastPinUpdatedAtDesc() .stream() .filter(authMember::canRead) .map(TopicResponse::fromGuestQuery) @@ -220,7 +219,7 @@ public List findAllBestTopics(AuthMember authMember) { } private List getGuestBestTopicResponse(AuthMember authMember) { - return topicRepository.findAllByIsDeletedFalse() + return topicRepository.findAll() .stream() .filter(authMember::canRead) .sorted(Comparator.comparing(Topic::countBookmarks).reversed()) @@ -231,7 +230,7 @@ private List getGuestBestTopicResponse(AuthMember authMember) { private List getUserBestTopicResponse(AuthMember authMember) { Member member = findMemberById(authMember.getMemberId()); - return topicRepository.findAllByIsDeletedFalse() + return topicRepository.findAll() .stream() .filter(authMember::canRead) .sorted(Comparator.comparing(Topic::countBookmarks).reversed()) diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/Topic.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/Topic.java index 598e098b..c61f5cc7 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/Topic.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/Topic.java @@ -25,10 +25,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.Where; @Entity @NoArgsConstructor(access = PROTECTED) @Getter +@Where(clause = "is_deleted = false") public class Topic extends BaseTimeEntity { @Id @@ -147,6 +149,10 @@ public void removeImage() { this.topicInfo = topicInfo.removeImage(); } + public void decreasePinCount() { + pinCount--; + } + public void removeBookmark(Bookmark bookmark) { bookmarks.remove(bookmark); bookmarkCount--; diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicRepository.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicRepository.java index cbaba31d..480d5967 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicRepository.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/domain/TopicRepository.java @@ -1,5 +1,7 @@ package com.mapbefine.mapbefine.topic.domain; +import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -7,32 +9,26 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; - @Repository public interface TopicRepository extends JpaRepository { @EntityGraph(attributePaths = {"creator", "permissions"}) - Optional findByIdAndIsDeletedFalse(Long id); - - @EntityGraph(attributePaths = {"creator", "permissions"}) - List findAllByIsDeletedFalse(); + Optional findById(Long id); @EntityGraph(attributePaths = {"creator", "permissions"}) - List findByIdInAndIsDeletedFalse(List ids); + List findAll(); @EntityGraph(attributePaths = {"creator", "permissions"}) - List findAllByIsDeletedFalseOrderByLastPinUpdatedAtDesc(); + List findByIdIn(List ids); @EntityGraph(attributePaths = {"creator", "permissions"}) - List findAllByCreatorIdAndIsDeletedFalse(Long creatorId); + List findAllByOrderByLastPinUpdatedAtDesc(); @EntityGraph(attributePaths = {"creator", "permissions"}) List findAllByCreatorId(Long creatorId); @EntityGraph(attributePaths = {"creator", "permissions"}) - List findTopicsByBookmarksMemberIdAndIsDeletedFalse(Long memberId); + List findTopicsByBookmarksMemberId(Long memberId); @Modifying(clearAutomatically = true) @Query("update Topic t set t.isDeleted = true where t.id = :topicId") diff --git a/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java b/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java index 7bb8d169..1c37b567 100644 --- a/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java +++ b/backend/src/main/java/com/mapbefine/mapbefine/topic/presentation/TopicController.java @@ -78,7 +78,9 @@ public ResponseEntity mergeAndCreate( @LoginRequired @PostMapping("/{topicId}/copy") - public ResponseEntity copyPin(AuthMember member, @PathVariable Long topicId, @RequestParam List pinIds) { + public ResponseEntity copyPin( + AuthMember member, @PathVariable Long topicId, @RequestParam List pinIds + ) { topicCommandService.copyPin(member, topicId, pinIds); return ResponseEntity.ok().build(); @@ -137,6 +139,14 @@ public ResponseEntity update( return ResponseEntity.ok().build(); } + @GetMapping("/bests") + public ResponseEntity> findAllBestTopics(AuthMember authMember) { + List responses = topicQueryService.findAllBestTopics(authMember); + + return ResponseEntity.ok(responses); + } + + @Deprecated(since = "2023.10.06") @LoginRequired @DeleteMapping("/{topicId}") public ResponseEntity delete(AuthMember member, @PathVariable Long topicId) { @@ -145,11 +155,4 @@ public ResponseEntity delete(AuthMember member, @PathVariable Long topicId return ResponseEntity.noContent().build(); } - @GetMapping("/bests") - public ResponseEntity> findAllBestTopics(AuthMember authMember) { - List responses = topicQueryService.findAllBestTopics(authMember); - - return ResponseEntity.ok(responses); - } - } diff --git a/backend/src/main/resources/data-default.sql b/backend/src/main/resources/data-default.sql index 1ef6fe67..4fc4dcc1 100644 --- a/backend/src/main/resources/data-default.sql +++ b/backend/src/main/resources/data-default.sql @@ -14,3 +14,6 @@ VALUES ('dummyTopic', 'https://map-befine-official.github.io/favicon.png', 'desc 1L, now(), now(), now()) ; + +INSERT INTO bookmark (member_id, topic_id) +VALUES (1L, 1L); diff --git a/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminCommandServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminCommandServiceTest.java index 0289b727..1e45c96f 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminCommandServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminCommandServiceTest.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; @ServiceTest class AdminCommandServiceTest { @@ -63,6 +64,9 @@ class AdminCommandServiceTest { @Autowired private BookmarkRepository bookmarkRepository; + @Autowired + TestEntityManager testEntityManager; + private Location location; private Member admin; private Member member; @@ -92,33 +96,35 @@ void blockMember_Success() { atlasRepository.save(atlas); permissionRepository.save(permission); - assertAll(() -> { - assertThat(member.getMemberInfo().getStatus()).isEqualTo(Status.NORMAL); - assertThat(topic.isDeleted()).isFalse(); - assertThat(pin.isDeleted()).isFalse(); - assertThat(pinImage.isDeleted()).isFalse(); - assertThat(bookmarkRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())).isTrue(); - assertThat(atlasRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())).isTrue(); - assertThat(permissionRepository.existsByTopicIdAndMemberId(topic.getId(), member.getId())).isTrue(); - }); + assertAll( + () -> assertThat(member.getMemberInfo().getStatus()).isEqualTo(Status.NORMAL), + () -> assertThat(topic.isDeleted()).isFalse(), + () -> assertThat(pin.isDeleted()).isFalse(), + () -> assertThat(pinImage.isDeleted()).isFalse(), + () -> assertThat(bookmarkRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())).isTrue(), + () -> assertThat(atlasRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())).isTrue(), + () -> assertThat(permissionRepository.existsByTopicIdAndMemberId(topic.getId(), member.getId())) + .isTrue() + ); //when + testEntityManager.clear(); adminCommandService.blockMember(member.getId()); //then - Topic deletedTopic = topicRepository.findById(topic.getId()).get(); - Pin deletedPin = pinRepository.findById(pin.getId()).get(); - PinImage deletedPinImage = pinImageRepository.findById(pinImage.getId()).get(); - - assertAll(() -> { - assertThat(member.getMemberInfo().getStatus()).isEqualTo(Status.BLOCKED); - assertThat(deletedTopic.isDeleted()).isTrue(); - assertThat(deletedPin.isDeleted()).isTrue(); - assertThat(deletedPinImage.isDeleted()).isTrue(); - assertThat(bookmarkRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())).isFalse(); - assertThat(atlasRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())).isFalse(); - assertThat(permissionRepository.existsByTopicIdAndMemberId(topic.getId(), member.getId())).isFalse(); - }); + Member blockedMember = memberRepository.findById(member.getId()).get(); + + assertAll( + () -> assertThat(blockedMember.getMemberInfo().getStatus()).isEqualTo(Status.BLOCKED), + () -> assertThat(topicRepository.existsById(topic.getId())).isFalse(), + () -> assertThat(pinRepository.existsById(pin.getId())).isFalse(), + () -> assertThat(pinImageRepository.existsById(pinImage.getId())).isFalse(), + () -> assertThat(bookmarkRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())) + .isFalse(), + () -> assertThat(atlasRepository.existsByMemberIdAndTopicId(member.getId(), topic.getId())).isFalse(), + () -> assertThat(permissionRepository.existsByTopicIdAndMemberId(topic.getId(), member.getId())) + .isFalse() + ); } @DisplayName("Admin은 토픽을 삭제시킬 수 있다.") @@ -128,12 +134,11 @@ void deleteTopic_Success() { assertThat(topic.isDeleted()).isFalse(); //when - adminCommandService.deleteTopic(topic.getId()); + Long topicId = topic.getId(); + adminCommandService.deleteTopic(topicId); //then - Topic deletedTopic = topicRepository.findById(topic.getId()).get(); - - assertThat(deletedTopic.isDeleted()).isTrue(); + assertThat(topicRepository.existsById(topicId)).isFalse(); } @DisplayName("Admin은 토픽 이미지를 삭제할 수 있다.") @@ -167,9 +172,7 @@ void deletePin_Success() { adminCommandService.deletePin(pin.getId()); //then - Pin deletedPin = pinRepository.findById(pin.getId()).get(); - - assertThat(deletedPin.isDeleted()).isTrue(); + assertThat(pinRepository.existsById(pin.getId())).isFalse(); } @DisplayName("Admin인 경우, 핀 이미지를 삭제할 수 있다.") @@ -182,9 +185,7 @@ void deletePinImage_Success() { adminCommandService.deletePinImage(pinImage.getId()); //then - PinImage deletedPinImage = pinImageRepository.findById(pinImage.getId()).get(); - - assertThat(deletedPinImage.isDeleted()).isTrue(); + assertThat(pinImageRepository.findById(pinImage.getId())).isEmpty(); } } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminQueryServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminQueryServiceTest.java index a50dd3f0..fbef9e48 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminQueryServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/admin/application/AdminQueryServiceTest.java @@ -1,11 +1,9 @@ package com.mapbefine.mapbefine.admin.application; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.mapbefine.mapbefine.admin.dto.AdminMemberDetailResponse; import com.mapbefine.mapbefine.admin.dto.AdminMemberResponse; -import com.mapbefine.mapbefine.auth.domain.AuthMember; import com.mapbefine.mapbefine.common.annotation.ServiceTest; import com.mapbefine.mapbefine.location.LocationFixture; import com.mapbefine.mapbefine.location.domain.Location; @@ -14,7 +12,6 @@ import com.mapbefine.mapbefine.member.domain.Member; import com.mapbefine.mapbefine.member.domain.MemberRepository; import com.mapbefine.mapbefine.member.domain.Role; -import com.mapbefine.mapbefine.permission.exception.PermissionException.PermissionForbiddenException; import com.mapbefine.mapbefine.pin.PinFixture; import com.mapbefine.mapbefine.pin.domain.Pin; import com.mapbefine.mapbefine.pin.domain.PinRepository; @@ -72,6 +69,7 @@ void findMemberDetail_Success() { .ignoringFields("updatedAt") .isEqualTo(AdminMemberDetailResponse.of(member, List.of(topic), List.of(pin))); } + @Test @DisplayName("모든 사용자와 관련된 세부 정보를 모두 조회할 수 있다.") void findAllMemberDetails_Success() { diff --git a/backend/src/test/java/com/mapbefine/mapbefine/auth/application/AuthServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/auth/application/AuthServiceTest.java new file mode 100644 index 00000000..aafc4e1b --- /dev/null +++ b/backend/src/test/java/com/mapbefine/mapbefine/auth/application/AuthServiceTest.java @@ -0,0 +1,94 @@ +package com.mapbefine.mapbefine.auth.application; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.mapbefine.mapbefine.admin.application.AdminCommandService; +import com.mapbefine.mapbefine.auth.domain.AuthMember; +import com.mapbefine.mapbefine.common.annotation.ServiceTest; +import com.mapbefine.mapbefine.common.exception.UnauthorizedException; +import com.mapbefine.mapbefine.member.MemberFixture; +import com.mapbefine.mapbefine.member.domain.Member; +import com.mapbefine.mapbefine.member.domain.MemberRepository; +import com.mapbefine.mapbefine.member.domain.Role; +import com.mapbefine.mapbefine.permission.domain.Permission; +import com.mapbefine.mapbefine.permission.domain.PermissionRepository; +import com.mapbefine.mapbefine.topic.TopicFixture; +import com.mapbefine.mapbefine.topic.domain.Topic; +import com.mapbefine.mapbefine.topic.domain.TopicRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +@ServiceTest +class AuthServiceTest { + + @Autowired + private AuthService authService; + @Autowired + private AdminCommandService adminCommandService; + @Autowired + private MemberRepository memberRepository; + @Autowired + private TopicRepository topicRepository; + @Autowired + private PermissionRepository permissionRepository; + private Member member; + private Topic topicWithPermission; + private Topic createdTopic; + + + @BeforeEach + void setUp() { + Member admin = memberRepository.save(MemberFixture.create("admin", "admin@member.com", Role.ADMIN)); + member = memberRepository.save(MemberFixture.create("member", "member1@member.com", Role.USER)); + topicWithPermission = topicRepository.save(TopicFixture.createPrivateAndGroupOnlyTopic(admin)); + createdTopic = topicRepository.save(TopicFixture.createPublicAndAllMembersTopic(admin)); + permissionRepository.save(Permission.createPermissionAssociatedWithTopicAndMember(topicWithPermission, member)); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + @Test + @DisplayName("인증된 회원 정보를 가져올 때, 차단 혹은 탈퇴한 회원의 정보는 가져오지 않는다.") + void findAuthMemberByMemberId_Success_notContainingNotNormalMember() { + // given + adminCommandService.blockMember(member.getId()); + + // when + // then + assertThatThrownBy(() -> authService.findAuthMemberByMemberId(member.getId())) + .isInstanceOf(UnauthorizedException.class); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + @Test + @DisplayName("인증된 회원 정보를 가져올 때, 삭제된 지도에 대해서는 권한을 부여하지 않는다. (soft delete 반영)") + void findAuthMemberByMemberId_Success_notContainingSoftDeletedPermission() { + // given + adminCommandService.deleteTopic(topicWithPermission.getId()); + + // when + AuthMember authMember = authService.findAuthMemberByMemberId(member.getId()); + + // then + assertThat(authMember.canPinCreateOrUpdate(topicWithPermission)).isFalse(); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + @Test + @DisplayName("인증된 회원 정보를 가져올 때, 삭제된 지도에 대해서는 생성자 여부를 확인하지 않는다. (soft delete 반영)") + void findAuthMemberByMemberId_Success_notContainingSoftDeletedCreatedTopic() { + // given + adminCommandService.deleteTopic(createdTopic.getId()); + + // when + AuthMember authMember = authService.findAuthMemberByMemberId(member.getId()); + + // then + assertThat(authMember.canTopicUpdate(createdTopic)).isFalse(); + } + +} diff --git a/backend/src/test/java/com/mapbefine/mapbefine/common/annotation/ServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/common/annotation/ServiceTest.java index 7993e891..2c80c964 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/common/annotation/ServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/common/annotation/ServiceTest.java @@ -25,7 +25,7 @@ ), @Filter( type = FilterType.REGEX, - pattern = "com.mapbefine.mapbefine.auth.application.*" + pattern = "com.mapbefine.mapbefine.auth.application.TokenService" ) } ) diff --git a/backend/src/test/java/com/mapbefine/mapbefine/location/application/LocationQueryServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/location/application/LocationQueryServiceTest.java index f97542e6..dd83e8da 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/location/application/LocationQueryServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/location/application/LocationQueryServiceTest.java @@ -16,6 +16,8 @@ import com.mapbefine.mapbefine.member.domain.MemberRepository; import com.mapbefine.mapbefine.member.domain.Role; import com.mapbefine.mapbefine.pin.PinFixture; +import com.mapbefine.mapbefine.pin.domain.Pin; +import com.mapbefine.mapbefine.pin.domain.PinRepository; import com.mapbefine.mapbefine.topic.TopicFixture; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; @@ -38,6 +40,8 @@ class LocationQueryServiceTest { private MemberRepository memberRepository; @Autowired private TopicRepository topicRepository; + @Autowired + private PinRepository pinRepository; @Autowired private LocationQueryService locationQueryService; @@ -56,9 +60,9 @@ void setup() { locationRepository.save(allPinsLocation); topics = List.of( - createAndSaveTopic("준팍의 또간집", 1), - createAndSaveTopic("도이의 또간집", 2), - createAndSaveTopic("패트릭의 또간집", 3) + createAndSaveTopic("준팍의 또간집", 2), + createAndSaveTopic("도이의 또간집", 3), + createAndSaveTopic("패트릭의 또간집", 4) ); } @@ -68,12 +72,13 @@ private Topic createAndSaveTopic(String topicName, int pinCounts) { for (int i = 0; i < pinCounts; i++) { PinFixture.create(allPinsLocation, topic, member); } + return topicRepository.save(topic); } @Test @DisplayName("주어진 좌표의 3KM 이내 Topic들을 Pin 개수의 내림차순으로 정렬하여 조회한다.") - void findNearbyTopicsSortedByPinCount() { + void findNearbyTopicsSortedByPinCount_Success() { // given Coordinate baseCoordinate = BASE_COORDINATE; @@ -93,4 +98,34 @@ void findNearbyTopicsSortedByPinCount() { assertThat(currentTopics).isEqualTo(expected); } + @Test + @DisplayName("반경 내 핀을 찾을 때, soft delete 된 핀은 제외한다.") + void findNearbyTopicsSortedByPinCount_Success_notContainingSoftDeletedPins() { + // given + Coordinate baseCoordinate = BASE_COORDINATE; + Topic second = topics.get(1); + deletePins(second, 2); + + // when + List currentTopics = locationQueryService.findNearbyTopicsSortedByPinCount( + authMember, + baseCoordinate.getLatitude(), + baseCoordinate.getLongitude() + ); + + // then + assertThat(currentTopics) + .extracting("id") + .containsExactly(topics.get(2).getId(), topics.get(0).getId(), topics.get(1).getId()); + } + + private void deletePins(Topic topic, int deleteCounts) { + /// TODO: 2023/10/05 Topic의 pinCount를 줄이는 로직을 삭제 로직과 통합하지 못해 테스트에서 세부 구현이 드러나는 문제가 있음 + for (int i = 0; i < deleteCounts; i++) { + Pin delete = topic.getPins().get(i); + delete.decreaseTopicPinCount(); + pinRepository.deleteById(delete.getId()); + } + } + } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/member/application/MemberQueryServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/member/application/MemberQueryServiceTest.java index 1165eaf0..3e631be9 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/member/application/MemberQueryServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/member/application/MemberQueryServiceTest.java @@ -177,6 +177,26 @@ void findMyAllTopics_Success() { .isEqualTo(topicIds); } + @Test + @DisplayName("로그인한 회원의 모든 지도를 가져올 때, 삭제된 지도는 제외한다. (soft delete 반영)") + void findMyAllTopics_Success_notContainingSoftDeleted() { + // given + List topicIds = topics.stream() + .map(Topic::getId) + .toList(); + + // when + Long deleted = topicIds.get(0); + topicRepository.deleteById(deleted); + topicRepository.flush(); + List myAllTopics = memberQueryService.findMyAllTopics(authMember); + + // then + assertThat(myAllTopics).hasSize(topics.size() - 1); + assertThat(myAllTopics).extractingResultOf("id") + .doesNotContain(deleted); + } + @Test @DisplayName("로그인한 회원이 생성한 모든 핀을 가져올 수 있다.") void findMyAllPins_Success() { diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java index d85b05b3..567e352f 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/application/PinCommandServiceTest.java @@ -29,6 +29,7 @@ import com.mapbefine.mapbefine.topic.TopicFixture; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; +import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -36,8 +37,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.multipart.MultipartFile; -import java.util.List; - @ServiceTest class PinCommandServiceTest { @@ -200,9 +199,9 @@ void removeById_Success() { pinCommandService.removeById(authMember, pinId); // then - assertThat(pinRepository.findByIdAndIsDeletedFalse(pinId)) + assertThat(pinRepository.findById(pinId)) .isEmpty(); - assertThat(pinImageRepository.findByIdAndIsDeletedFalse(pinId)) + assertThat(pinImageRepository.findById(pinId)) .isEmpty(); } @@ -268,7 +267,7 @@ void removeImageById_Success() { pinCommandService.removeImageById(authMember, pinImageId); // then - assertThat(pinImageRepository.findByIdAndIsDeletedFalse(pinImageId)) + assertThat(pinImageRepository.findById(pinImageId)) .isEmpty(); } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinImageRepositoryTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinImageRepositoryTest.java index 50b6983e..c3b20b38 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinImageRepositoryTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinImageRepositoryTest.java @@ -14,14 +14,13 @@ import com.mapbefine.mapbefine.topic.TopicFixture; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import java.util.List; - @DataJpaTest class PinImageRepositoryTest { @@ -62,7 +61,7 @@ void deleteById_Success() { pinImageRepository.deleteById(pinImageId); //then - assertThat(pinImageRepository.findByIdAndIsDeletedFalse(pinImageId)) + assertThat(pinImageRepository.findById(pinImageId)) .isEmpty(); } @@ -81,7 +80,7 @@ void deleteAllByPinId_Success() { pinImageRepository.deleteAllByPinId(pin.getId()); //then - assertThat(pinImageRepository.findByIdAndIsDeletedFalse(pin.getId())) + assertThat(pinImageRepository.findById(pin.getId())) .isEmpty(); } @@ -102,9 +101,9 @@ void deleteAllByMemberId_Success() { pinImageRepository.deleteAllByPinIds(List.of(pin.getId(), otherPin.getId())); //then - assertThat(pinImageRepository.findByIdAndIsDeletedFalse(pin.getId())) + assertThat(pinImageRepository.findById(pin.getId())) .isEmpty(); - assertThat(pinImageRepository.findAllByPinIdAndIsDeletedFalse(otherPin.getId())) + assertThat(pinImageRepository.findAllByPinId(otherPin.getId())) .isEmpty(); } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinRepositoryTest.java b/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinRepositoryTest.java index 24bf7cde..fd26deda 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinRepositoryTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/pin/domain/PinRepositoryTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.mapbefine.mapbefine.common.config.JpaConfig; import com.mapbefine.mapbefine.location.LocationFixture; import com.mapbefine.mapbefine.location.domain.Location; import com.mapbefine.mapbefine.location.domain.LocationRepository; @@ -13,14 +14,14 @@ import com.mapbefine.mapbefine.topic.TopicFixture; import com.mapbefine.mapbefine.topic.domain.Topic; import com.mapbefine.mapbefine.topic.domain.TopicRepository; -import java.util.List; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +@Import(JpaConfig.class) @DataJpaTest class PinRepositoryTest { @@ -57,11 +58,7 @@ void deleteById_Success() { pinRepository.deleteById(pinId); // then - pinRepository.findById(pinId) - .ifPresentOrElse( - found -> assertThat(found.isDeleted()).isTrue(), - Assertions::fail - ); + assertThat(pinRepository.existsById(pinId)); } @Test @@ -75,12 +72,11 @@ void deleteAllByTopicId_Success() { // when assertThat(topic.getPins()).extractingResultOf("isDeleted") .containsOnly(false); - pinRepository.deleteAllByTopicId(topic.getId()); + Long topicId = topic.getId(); + pinRepository.deleteAllByTopicId(topicId); // then - List deletedPins = pinRepository.findAllByTopicId(topic.getId()); - assertThat(deletedPins).extractingResultOf("isDeleted") - .containsOnly(true); + assertThat(pinRepository.findAllByTopicId(topicId).isEmpty()); } @@ -96,18 +92,17 @@ void deleteAllByMemberId_Success() { assertThat(member.getCreatedPins()).hasSize(10) .extractingResultOf("isDeleted") .containsOnly(false); - pinRepository.deleteAllByMemberId(member.getId()); + Long memberId = member.getId(); + pinRepository.deleteAllByMemberId(memberId); //then - List deletedPins = pinRepository.findAllByCreatorId(member.getId()); - assertThat(deletedPins).extractingResultOf("isDeleted") - .containsOnly(true); + assertThat(pinRepository.findAllByCreatorId(memberId)).isEmpty(); } @Test @DisplayName("다른 토픽에 존재하는 핀들이여도, Member ID로 모든 핀을 soft-delete 할 수 있다.") void deleteAllByMemberIdInOtherTopics_Success() { - //given + // given Topic otherTopic = TopicFixture.createByName("otherTopic", member); topicRepository.save(otherTopic); @@ -116,16 +111,15 @@ void deleteAllByMemberIdInOtherTopics_Success() { pinRepository.save(PinFixture.create(location, otherTopic, member)); } - //when + // when assertThat(member.getCreatedPins()).hasSize(20) .extractingResultOf("isDeleted") .containsOnly(false); - pinRepository.deleteAllByMemberId(member.getId()); + Long MemberId = member.getId(); + pinRepository.deleteAllByMemberId(MemberId); - //then - List deletedPins = pinRepository.findAllByCreatorId(member.getId()); - assertThat(deletedPins).extractingResultOf("isDeleted") - .containsOnly(true); + // then + assertThat(pinRepository.findAllByCreatorId(MemberId)).isEmpty(); } } diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicCommandServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicCommandServiceTest.java index bde489c8..c66f1ae1 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicCommandServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicCommandServiceTest.java @@ -29,14 +29,13 @@ import com.mapbefine.mapbefine.topic.dto.response.TopicDetailResponse; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicBadRequestException; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicForbiddenException; +import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.util.Collections; -import java.util.List; - @ServiceTest class TopicCommandServiceTest { @@ -398,9 +397,7 @@ void delete_Success() { topicCommandService.delete(adminAuthMember, topic.getId()); //then - Topic deletedTopic = topicRepository.findById(topic.getId()).get(); - - assertThat(deletedTopic.isDeleted()).isTrue(); + assertThat(topicRepository.existsById(topic.getId())).isFalse(); } @Test diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicQueryServiceTest.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicQueryServiceTest.java index 3657dbb5..cc45bb7a 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicQueryServiceTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/application/TopicQueryServiceTest.java @@ -27,14 +27,13 @@ import com.mapbefine.mapbefine.topic.dto.response.TopicResponse; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicForbiddenException; import com.mapbefine.mapbefine.topic.exception.TopicException.TopicNotFoundException; +import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.util.Collections; -import java.util.List; - @ServiceTest class TopicQueryServiceTest { @@ -206,7 +205,7 @@ void findAllReadableWithoutBookmark_Success() { assertThat(topics).extractingResultOf("isBookmarked") .containsExactlyInAnyOrder(Boolean.FALSE, Boolean.FALSE); } - + @Test @DisplayName("권한이 있는 토픽을 ID로 조회한다.") void findDetailById_Success() { @@ -225,6 +224,25 @@ void findDetailById_Success() { assertThat(detail.name()).isEqualTo("아무나 읽을 수 있는 토픽"); } + @Test + @DisplayName("토픽 상세 조회 시 삭제된 핀은 볼 수 없다. (soft delete 반영)") + void findDetailById_Success_getPinsOnlyNotDeleted() { + //given + Topic topic = TopicFixture.createPublicAndAllMembersTopic(member); + Location location = LocationFixture.create(); + Pin pin = PinFixture.create(location, topic, member); + locationRepository.save(location); + topicRepository.save(topic); + pinRepository.save(pin); + pinRepository.deleteById(pin.getId()); + + //when + TopicDetailResponse response = topicQueryService.findDetailById(new Admin(member.getId()), topic.getId()); + + //then + assertThat(response.pins()).isEmpty(); + } + @Test @DisplayName("토픽 상세 조회 시 토픽의 변경일자는 핀의 최신 변경 일자이다.") void findDetailById_Success_lastPinUpdatedAt() { diff --git a/backend/src/test/java/com/mapbefine/mapbefine/topic/domain/TopicRepositoryTest.java b/backend/src/test/java/com/mapbefine/mapbefine/topic/domain/TopicRepositoryTest.java index c50e5f4d..c419261d 100644 --- a/backend/src/test/java/com/mapbefine/mapbefine/topic/domain/TopicRepositoryTest.java +++ b/backend/src/test/java/com/mapbefine/mapbefine/topic/domain/TopicRepositoryTest.java @@ -13,8 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import java.util.List; - @DataJpaTest class TopicRepositoryTest { @@ -43,8 +41,7 @@ void deleteById_Success() { topicRepository.deleteById(topic.getId()); //then - Topic deletedTopic = topicRepository.findById(topic.getId()).get(); - assertThat(deletedTopic.isDeleted()).isTrue(); + assertThat(topicRepository.existsById(topic.getId())).isFalse(); } @Test @@ -62,9 +59,7 @@ void deleteAllByMemberId_Success() { topicRepository.deleteAllByMemberId(member.getId()); //then - List deletedTopics = topicRepository.findAllByCreatorId(member.getId()); - assertThat(deletedTopics).hasSize(10) - .extractingResultOf("isDeleted") - .containsOnly(true); + assertThat(topicRepository.findAllByCreatorId(member.getId())) + .isEmpty(); } }