From edb39a02b58231b4f313fe7d5f95dd4a7b23b46f Mon Sep 17 00:00:00 2001 From: LeeKiWoo Date: Thu, 28 Sep 2023 13:50:27 +0900 Subject: [PATCH] feat(block): Adding Blocking Features (#153) * feat(block): add 'Block' Entity and Table * feat(block): add read block list query & test * feat(block): add blocking member business code & test * feat(block): add block API Controller & test * feat(block): add find block member list query * refactor(suggestion): except blocked member from suggestion and history query * refactor(heart): except blocked member from find sent or received hearts query * refactor(meeting): except blocked member from find meeting and meeting request * docs(block): add documentation block api to oas3 file * feat(team): add query check blocked team & test * feat(block): add BlockedException * feat(block): prevent blocked users from checking when read team --- .../controller/member/BlockController.java | 32 +++ .../heart/HeartCustomRepositoryImpl.java | 11 +- .../meeting/MeetingReadRepositoryImpl.java | 135 ++++++----- .../com/e2i/wemeet/domain/member/Block.java | 39 +++ .../wemeet/domain/member/BlockRepository.java | 27 +++ .../com/e2i/wemeet/domain/member/Member.java | 21 ++ .../wemeet/domain/team/TeamRepository.java | 15 ++ .../suggestion/SuggestionRepositoryImpl.java | 25 +- .../com/e2i/wemeet/exception/ErrorCode.java | 2 + .../badrequest/BlockedException.java | 15 ++ .../wemeet/service/member/BlockService.java | 12 + .../service/member/BlockServiceImpl.java | 41 ++++ .../wemeet/service/team/TeamServiceImpl.java | 7 + .../migration/V2309271230__create_block.sql | 12 + src/main/resources/messages.properties | 2 + .../resources/static/swagger-ui/openapi3.yaml | 87 +++++++ .../member/BlockControllerTest.java | 126 ++++++++++ .../heart/HeartCustomRepositoryTest.java | 86 ++++++- .../MeetingReadRepositoryImplTest.java | 124 +++++++++- .../domain/member/BlockRepositoryTest.java | 90 +++++++ .../domain/team/TeamRepositoryTest.java | 52 ++++ .../SuggestionRepositoryImplTest.java | 224 ++++++++++++++++++ .../service/heart/HeartServiceTest.java | 77 ++++++ .../service/member/BlockServiceImplTest.java | 73 ++++++ .../service/team/TeamServiceImplTest.java | 25 ++ .../config/AbstractControllerUnitTest.java | 7 +- .../support/config/QueryDslTestConfig.java | 5 +- .../wemeet/support/fixture/MemberFixture.java | 6 +- 28 files changed, 1297 insertions(+), 81 deletions(-) create mode 100644 src/main/java/com/e2i/wemeet/controller/member/BlockController.java create mode 100644 src/main/java/com/e2i/wemeet/domain/member/Block.java create mode 100644 src/main/java/com/e2i/wemeet/domain/member/BlockRepository.java create mode 100644 src/main/java/com/e2i/wemeet/exception/badrequest/BlockedException.java create mode 100644 src/main/java/com/e2i/wemeet/service/member/BlockService.java create mode 100644 src/main/java/com/e2i/wemeet/service/member/BlockServiceImpl.java create mode 100644 src/main/resources/db/migration/V2309271230__create_block.sql create mode 100644 src/test/java/com/e2i/wemeet/controller/member/BlockControllerTest.java create mode 100644 src/test/java/com/e2i/wemeet/domain/member/BlockRepositoryTest.java create mode 100644 src/test/java/com/e2i/wemeet/domain/team/TeamRepositoryTest.java create mode 100644 src/test/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImplTest.java create mode 100644 src/test/java/com/e2i/wemeet/service/member/BlockServiceImplTest.java diff --git a/src/main/java/com/e2i/wemeet/controller/member/BlockController.java b/src/main/java/com/e2i/wemeet/controller/member/BlockController.java new file mode 100644 index 00000000..a0fdafe3 --- /dev/null +++ b/src/main/java/com/e2i/wemeet/controller/member/BlockController.java @@ -0,0 +1,32 @@ +package com.e2i.wemeet.controller.member; + +import com.e2i.wemeet.config.resolver.member.MemberId; +import com.e2i.wemeet.dto.response.ResponseDto; +import com.e2i.wemeet.service.member.BlockService; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RequestMapping("/v1/member/block") +@RestController +public class BlockController { + + private final BlockService blockService; + + @PostMapping("/{blockMemberId}") + public ResponseDto blockMember(@MemberId Long memberId, @PathVariable Long blockMemberId) { + final Long blockedMemberId = blockService.block(memberId, blockMemberId); + return ResponseDto.success("Block Member Success", blockedMemberId); + } + + @GetMapping + public ResponseDto> readBlockMembers(@MemberId Long memberId) { + final List blockList = blockService.readBlockList(memberId); + return ResponseDto.success("Read Block Member Success", blockList); + } +} diff --git a/src/main/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryImpl.java b/src/main/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryImpl.java index 54d5956e..6c9e2af7 100644 --- a/src/main/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryImpl.java +++ b/src/main/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryImpl.java @@ -5,6 +5,7 @@ import static com.e2i.wemeet.domain.team.QTeam.team; import static com.e2i.wemeet.domain.team_image.QTeamImage.teamImage; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.team.data.suggestion.TeamLeaderData; import com.e2i.wemeet.dto.dsl.HeartTeamData; import com.querydsl.core.types.Projections; @@ -19,12 +20,16 @@ public class HeartCustomRepositoryImpl implements HeartCustomRepository { private final JPAQueryFactory queryFactory; + private final BlockRepository blockRepository; @Override public List findSentHeart(Long teamId, LocalDateTime requestedTime) { LocalDateTime beforeTime = requestedTime.minusDays(1); + // 차단 목록 조회 + List blockMemberIds = blockRepository.findBlockMemberIdsByTeamId(teamId); + return queryFactory .select( Projections.constructor(HeartTeamData.class, heart.partnerTeam.teamId, @@ -40,12 +45,12 @@ public List findSentHeart(Long teamId, .from(heart) .join(team).on(heart.team.teamId.eq(team.teamId)) .join(heart.partnerTeam.teamLeader, member) - .on(heart.partnerTeam.teamLeader.memberId.eq(member.memberId)) .join(teamImage).on(teamImage.team.teamId.eq(heart.partnerTeam.teamId)) .where( team.teamId.eq(teamId), team.deletedAt.isNull(), heart.createdAt.between(beforeTime, requestedTime), + member.memberId.notIn(blockMemberIds), teamImage.sequence.eq(1) ) .fetch(); @@ -56,6 +61,9 @@ public List findReceivedHeart(Long teamId, LocalDateTime requestedTime) { LocalDateTime beforeTime = requestedTime.minusDays(1); + // 차단 목록 조회 + List blockMemberIds = blockRepository.findBlockMemberIdsByTeamId(teamId); + return queryFactory .select( Projections.constructor(HeartTeamData.class, heart.team.teamId, @@ -75,6 +83,7 @@ public List findReceivedHeart(Long teamId, team.teamId.eq(teamId), team.deletedAt.isNull(), heart.createdAt.between(beforeTime, requestedTime), + member.memberId.notIn(blockMemberIds), teamImage.sequence.eq(1) ) .fetch(); diff --git a/src/main/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImpl.java b/src/main/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImpl.java index 011c292d..760fb105 100644 --- a/src/main/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImpl.java +++ b/src/main/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImpl.java @@ -7,6 +7,7 @@ import static com.e2i.wemeet.domain.team.QTeam.team; import static com.e2i.wemeet.domain.team_image.QTeamImage.teamImage; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.member.QMember; import com.e2i.wemeet.domain.team.QTeam; import com.e2i.wemeet.domain.team.Team; @@ -33,6 +34,7 @@ public class MeetingReadRepositoryImpl implements MeetingReadRepository { private final JPAQueryFactory queryFactory; private final EntityManager entityManager; + private final BlockRepository blockRepository; private final QMember partnerTeamLeader = new QMember("teamLeader"); private final QTeam partnerTeam = new QTeam("partnerTeam"); @@ -64,11 +66,14 @@ public Team findTeamReferenceById(final Long teamId) { // 성사된 미팅 조회 @Override public List findAcceptedMeetingList(final Long memberId) { + // 차단된 사용자 조회 + List blockMemberIds = blockRepository.findBlockMemberIds(memberId); + // 내가 미팅 신청하고 성사되었을 때 목록 - List meetingList = findMeetingInformationWhatIRequested(memberId); + List meetingList = findMeetingInformationWhatIRequested(memberId, blockMemberIds); // 내가 미팅 신청받고 수락하여 성사되었을 때 목록 - meetingList.addAll(findMeetingInformationWhatIReceived(memberId)); + meetingList.addAll(findMeetingInformationWhatIReceived(memberId, blockMemberIds)); return meetingList.stream() .map(meetingInformation -> AcceptedMeetingResponseDto.of( @@ -78,8 +83,70 @@ meetingInformation, findTeamProfileImageUrl(meetingInformation.getTeamId()) .toList(); } + // 보낸 미팅 신청 조회 + @Override + public List findSentRequestList(final Long memberId) { + // 차단된 사용자 조회 + List blockMemberIds = blockRepository.findBlockMemberIds(memberId); + + List meetingRequestList = selectMeetingRequestInformationDto() + .from(meetingRequest) + // My Team & Partner Team + .join(meetingRequest.team, team).on(team.deletedAt.isNull()) + .join(meetingRequest.partnerTeam, partnerTeam) + // Me & Partner Team Leader + .join(team.teamLeader, member) + .join(partnerTeam.teamLeader, partnerTeamLeader) + // Partner Team Leader College + .join(partnerTeamLeader.collegeInfo.collegeCode, code) + .where( + member.memberId.eq(memberId), + member.deletedAt.isNull(), + // filter blocked member + partnerTeamLeader.memberId.notIn(blockMemberIds) + ) + .fetch(); + + return meetingRequestList.stream() + .map(meetingRequestInformation -> SentMeetingResponseDto.of( + meetingRequestInformation, findTeamProfileImageUrl(meetingRequestInformation.getTeamId()) + )) + .toList(); + } + + // 받은 미팅 신청 조회 + @Override + public List findReceiveRequestList(final Long memberId) { + // 차단된 사용자 조회 + List blockMemberIds = blockRepository.findBlockMemberIds(memberId); + + List meetingReceivedList = selectMeetingRequestInformationDto() + .from(meetingRequest) + // meetingRequest.partnerTeam == RequestReceivedTeam == My Team + .join(meetingRequest.team, partnerTeam) + .join(meetingRequest.partnerTeam, team).on(team.deletedAt.isNull()) + // Me & Partner Team Leader + .join(team.teamLeader, member) + .join(partnerTeam.teamLeader, partnerTeamLeader) + // Partner Team Leader College + .join(partnerTeamLeader.collegeInfo.collegeCode, code) + .where( + member.memberId.eq(memberId), + member.deletedAt.isNull(), + // filter blocked member + partnerTeamLeader.memberId.notIn(blockMemberIds) + ) + .fetch(); + + return meetingReceivedList.stream() + .map(meetingRequestInformation -> ReceivedMeetingResponseDto.of( + meetingRequestInformation, findTeamProfileImageUrl(meetingRequestInformation.getTeamId()) + )) + .toList(); + } + // 내가 미팅 신청하고 성사되었을 때 목록 - private List findMeetingInformationWhatIRequested(Long memberId) { + private List findMeetingInformationWhatIRequested(final Long memberId, final List blockMemberIds) { return selectMeetingInformationDto() .from(meeting) // My Team & Partner Team @@ -92,13 +159,15 @@ private List findMeetingInformationWhatIRequested(Long me .join(partnerTeamLeader.collegeInfo.collegeCode, code) .where( member.memberId.eq(memberId), - member.deletedAt.isNull() + member.deletedAt.isNull(), + // filter blocked member + partnerTeamLeader.memberId.notIn(blockMemberIds) ) .fetch(); } // 내가 미팅 신청받고 수락하여 성사되었을 때 목록 - private List findMeetingInformationWhatIReceived(Long memberId) { + private List findMeetingInformationWhatIReceived(final Long memberId, final List blockMemberIds) { return selectMeetingInformationDto() .from(meeting) // My Team & Partner Team @@ -111,7 +180,9 @@ private List findMeetingInformationWhatIReceived(Long mem .join(partnerTeamLeader.collegeInfo.collegeCode, code) .where( member.memberId.eq(memberId), - member.deletedAt.isNull() + member.deletedAt.isNull(), + // filter blocked member + partnerTeamLeader.memberId.notIn(blockMemberIds) ) .fetch(); } @@ -137,58 +208,6 @@ private JPAQuery selectMeetingInformationDto() { )); } - // 보낸 미팅 신청 조회 - @Override - public List findSentRequestList(final Long memberId) { - List meetingRequestList = selectMeetingRequestInformationDto() - .from(meetingRequest) - // My Team & Partner Team - .join(meetingRequest.team, team).on(team.deletedAt.isNull()) - .join(meetingRequest.partnerTeam, partnerTeam) - // Me & Partner Team Leader - .join(team.teamLeader, member) - .join(partnerTeam.teamLeader, partnerTeamLeader) - // Partner Team Leader College - .join(partnerTeamLeader.collegeInfo.collegeCode, code) - .where( - member.memberId.eq(memberId), - member.deletedAt.isNull() - ) - .fetch(); - - return meetingRequestList.stream() - .map(meetingRequestInformation -> SentMeetingResponseDto.of( - meetingRequestInformation, findTeamProfileImageUrl(meetingRequestInformation.getTeamId()) - )) - .toList(); - } - - // 받은 미팅 신청 조회 - @Override - public List findReceiveRequestList(final Long memberId) { - List meetingReceivedList = selectMeetingRequestInformationDto() - .from(meetingRequest) - // PartnerTeam == RequestReceivedTeam == My Team - .join(meetingRequest.team, partnerTeam) - .join(meetingRequest.partnerTeam, team).on(team.deletedAt.isNull()) - // Me & Partner Team Leader - .join(team.teamLeader, member) - .join(partnerTeam.teamLeader, partnerTeamLeader) - // Partner Team Leader College - .join(partnerTeamLeader.collegeInfo.collegeCode, code) - .where( - member.memberId.eq(memberId), - member.deletedAt.isNull() - ) - .fetch(); - - return meetingReceivedList.stream() - .map(meetingRequestInformation -> ReceivedMeetingResponseDto.of( - meetingRequestInformation, findTeamProfileImageUrl(meetingRequestInformation.getTeamId()) - )) - .toList(); - } - private JPAQuery selectMeetingRequestInformationDto() { return queryFactory.select( new QMeetingRequestInformationDto( diff --git a/src/main/java/com/e2i/wemeet/domain/member/Block.java b/src/main/java/com/e2i/wemeet/domain/member/Block.java new file mode 100644 index 00000000..143de6c2 --- /dev/null +++ b/src/main/java/com/e2i/wemeet/domain/member/Block.java @@ -0,0 +1,39 @@ +package com.e2i.wemeet.domain.member; + +import com.e2i.wemeet.domain.base.CreateTimeEntity; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Entity +public class Block extends CreateTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long blockId; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member", referencedColumnName = "memberId", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "blockMember", referencedColumnName = "memberId", nullable = false) + private Member blockMember; + + @Builder + public Block(Member member, Member blockMember) { + this.member = member; + this.blockMember = blockMember; + } + +} diff --git a/src/main/java/com/e2i/wemeet/domain/member/BlockRepository.java b/src/main/java/com/e2i/wemeet/domain/member/BlockRepository.java new file mode 100644 index 00000000..fb3852e9 --- /dev/null +++ b/src/main/java/com/e2i/wemeet/domain/member/BlockRepository.java @@ -0,0 +1,27 @@ +package com.e2i.wemeet.domain.member; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface BlockRepository extends JpaRepository { + + // 차단 목록 조회 + @Query("select b from Block b join fetch b.blockMember where b.member.memberId = :memberId") + List findAllByMemberId(@Param("memberId") Long memberId); + + // 차단한 사용자 목록 조회 (ID) + @Query("select b.blockMember.memberId from Block b where b.member.memberId = :memberId") + List findBlockMemberIds(@Param("memberId") Long memberId); + + // 차단한 사용자 목록 조회 (ID) + @Query(""" + select b.blockMember.memberId + from Block b + join Team t on t.teamLeader.memberId = b.member.memberId + where t.teamId = :teamId + """) + List findBlockMemberIdsByTeamId(@Param("teamId") Long teamId); + +} diff --git a/src/main/java/com/e2i/wemeet/domain/member/Member.java b/src/main/java/com/e2i/wemeet/domain/member/Member.java index 7dad1261..e34fe816 100644 --- a/src/main/java/com/e2i/wemeet/domain/member/Member.java +++ b/src/main/java/com/e2i/wemeet/domain/member/Member.java @@ -106,6 +106,9 @@ public class Member extends BaseTimeEntity { @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) private List history = new ArrayList<>(); + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true) + private List blocks = new ArrayList<>(); + @Builder public Member(String nickname, Gender gender, String phoneNumber, String email, CollegeInfo collegeInfo, Mbti mbti, Integer credit, Boolean allowMarketing, @@ -150,6 +153,14 @@ public Member checkMemberValid() { return this; } + public boolean isDeleted() { + return this.deletedAt != null; + } + + public boolean isActive() { + return this.deletedAt == null; + } + private void validateManager() { if (this.role != Role.MANAGER && this.role != Role.ADMIN) { throw new UnAuthorizedRoleException(); @@ -234,5 +245,15 @@ public void registerRecommender(final String recommenderPhone) { } this.recommenderPhone = recommenderPhone; } + + // 차단 목록에 추가 + public void addBlockMember(final Member blockMember) { + Block block = Block.builder() + .member(this) + .blockMember(blockMember) + .build(); + this.blocks.add(block); + } + } diff --git a/src/main/java/com/e2i/wemeet/domain/team/TeamRepository.java b/src/main/java/com/e2i/wemeet/domain/team/TeamRepository.java index 40931af9..6bc17474 100644 --- a/src/main/java/com/e2i/wemeet/domain/team/TeamRepository.java +++ b/src/main/java/com/e2i/wemeet/domain/team/TeamRepository.java @@ -4,6 +4,7 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface TeamRepository extends JpaRepository, TeamCustomRepository, SuggestionRepository { @@ -11,4 +12,18 @@ public interface TeamRepository extends JpaRepository, TeamCustomRep @Query("select t from Team t where t.teamLeader.memberId = :memberId") Optional findByMemberId(Long memberId); + /* + ** 팀이 차단된 사용자의 팀인지 확인 + - memberId = 조회하는 사람의 ID + - teamId = 차단된 팀인지 확인 하는 대상의 teamId + */ + @Query(""" + SELECT COUNT(b.blockMember) > 0 + FROM Team t + JOIN Block b on b.blockMember = t.teamLeader + WHERE t.teamId = :teamId + AND b.member.memberId = :memberId + """) + boolean isBlockedTeam(@Param("memberId") Long memberId, @Param("teamId") Long teamId); + } diff --git a/src/main/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImpl.java b/src/main/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImpl.java index 153ca94c..8cb816dc 100644 --- a/src/main/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImpl.java +++ b/src/main/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImpl.java @@ -6,6 +6,7 @@ import static com.e2i.wemeet.domain.team_image.QTeamImage.teamImage; import com.e2i.wemeet.domain.history.History; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.member.data.Gender; import com.e2i.wemeet.domain.team.data.suggestion.SuggestionHistoryData; import com.e2i.wemeet.domain.team.data.suggestion.SuggestionTeamData; @@ -24,9 +25,10 @@ public class SuggestionRepositoryImpl implements SuggestionRepository { private final JPAQueryFactory queryFactory; - private static final int SUGGESTION_TEAM_LIMIT = 2; - private static final LocalTime boundaryTime = LocalTime.of(23, 11); + private final BlockRepository blockRepository; + public static final int SUGGESTION_TEAM_LIMIT = 2; + private static final LocalTime boundaryTime = LocalTime.of(23, 11); @Override public List findSuggestionTeamForUser(Long memberId, Gender gender) { @@ -39,6 +41,8 @@ public List findSuggestionTeamForUser(Long memberId, Gender ) .fetch(); + List blockList = blockRepository.findBlockMemberIds(memberId); + return queryFactory .select(Projections.constructor(SuggestionTeamData.class, team, teamImage.teamImageUrl.as("teamMainImageUrl"), @@ -55,6 +59,7 @@ public List findSuggestionTeamForUser(Long memberId, Gender team.deletedAt.isNull(), team.gender.ne(gender), team.teamId.notIn(suggestionHistory), + member.memberId.notIn(blockList), teamImage.sequence.eq(1) ) .orderBy(Expressions.numberTemplate(Double.class, "function('rand')").asc()) @@ -62,6 +67,7 @@ public List findSuggestionTeamForUser(Long memberId, Gender .fetch(); } + @Override public List findHistory(Long memberId, LocalDateTime requestedTime) { LocalDateTime boundaryDateTime = requestedTime.with(boundaryTime); @@ -69,12 +75,19 @@ public List findHistory(Long memberId, LocalDateTime requestedTime) { boundaryDateTime = boundaryDateTime.minusDays(1); } + List blockIdList = blockRepository.findBlockMemberIds(memberId); + return queryFactory.selectFrom(history) - .where(history.member.memberId.eq(memberId)) - .where(history.createdAt.between(boundaryDateTime, requestedTime)) + .join(history.team, team) + .where( + history.member.memberId.eq(memberId), + team.teamLeader.memberId.notIn(blockIdList), + history.createdAt.between(boundaryDateTime, requestedTime) + ) .fetch(); } + @Override public List findSuggestionHistoryTeam(Long memberId, LocalDateTime requestedTime) { LocalDateTime boundaryDateTime = requestedTime.with(boundaryTime); @@ -83,6 +96,8 @@ public List findSuggestionHistoryTeam(Long memberId, boundaryDateTime = boundaryDateTime.minusDays(1); } + List blockList = blockRepository.findBlockMemberIds(memberId); + return queryFactory .select( Projections.constructor(SuggestionHistoryData.class, team.teamId, team.memberNum, @@ -101,8 +116,10 @@ public List findSuggestionHistoryTeam(Long memberId, history.member.memberId.eq(memberId), history.createdAt.between(boundaryDateTime, requestedTime), team.deletedAt.isNull(), + member.memberId.notIn(blockList), teamImage.sequence.eq(1) ) .fetch(); } + } diff --git a/src/main/java/com/e2i/wemeet/exception/ErrorCode.java b/src/main/java/com/e2i/wemeet/exception/ErrorCode.java index 956aab14..72295f46 100644 --- a/src/main/java/com/e2i/wemeet/exception/ErrorCode.java +++ b/src/main/java/com/e2i/wemeet/exception/ErrorCode.java @@ -51,6 +51,8 @@ public enum ErrorCode { MEETING_ALREADY_EXIST(40042, "meeting.already.exist"), RECOMMENDER_ALREADY_EXIST(40043, "recommender.already.exist"), IMAGE_COUNT_EXCEEDED(40044, "image.count.exceeded"), + BLOCKED_MEMBER(40045, "blocked.member"), + BLOCKED_TEAM(40046, "blocked.team"), NOTFOUND_SMS_CREDENTIAL(40100, "notfound.sms.credential"), MEMBER_NOT_FOUND(40101, "member.not.found"), diff --git a/src/main/java/com/e2i/wemeet/exception/badrequest/BlockedException.java b/src/main/java/com/e2i/wemeet/exception/badrequest/BlockedException.java new file mode 100644 index 00000000..d07f8c1f --- /dev/null +++ b/src/main/java/com/e2i/wemeet/exception/badrequest/BlockedException.java @@ -0,0 +1,15 @@ +package com.e2i.wemeet.exception.badrequest; + +import com.e2i.wemeet.exception.ErrorCode; + +public class BlockedException extends BadRequestException { + + public BlockedException() { + super(ErrorCode.BLOCKED_MEMBER); + } + + public BlockedException(ErrorCode errorCode) { + super(errorCode); + } + +} diff --git a/src/main/java/com/e2i/wemeet/service/member/BlockService.java b/src/main/java/com/e2i/wemeet/service/member/BlockService.java new file mode 100644 index 00000000..eb26b8ec --- /dev/null +++ b/src/main/java/com/e2i/wemeet/service/member/BlockService.java @@ -0,0 +1,12 @@ +package com.e2i.wemeet.service.member; + +import java.util.List; + +public interface BlockService { + + // 차단하기 + Long block(Long memberId, Long blockMemberId); + + // 차단목록 조회 + List readBlockList(Long memberId); +} diff --git a/src/main/java/com/e2i/wemeet/service/member/BlockServiceImpl.java b/src/main/java/com/e2i/wemeet/service/member/BlockServiceImpl.java new file mode 100644 index 00000000..e1208f07 --- /dev/null +++ b/src/main/java/com/e2i/wemeet/service/member/BlockServiceImpl.java @@ -0,0 +1,41 @@ +package com.e2i.wemeet.service.member; + +import com.e2i.wemeet.domain.member.BlockRepository; +import com.e2i.wemeet.domain.member.Member; +import com.e2i.wemeet.domain.member.MemberRepository; +import com.e2i.wemeet.exception.notfound.MemberNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@RequiredArgsConstructor +@Transactional(readOnly = true) +@Service +public class BlockServiceImpl implements BlockService { + + private final MemberRepository memberRepository; + private final BlockRepository blockRepository; + + @Transactional + @Override + public Long block(final Long memberId, final Long blockMemberId) { + Member member = memberRepository.findByMemberId(memberId) + .orElseThrow(MemberNotFoundException::new) + .checkMemberValid(); + + Member blockMember = memberRepository.getReferenceById(blockMemberId); + member.addBlockMember(blockMember); + + return blockMember.getMemberId(); + } + + @Override + public List readBlockList(final Long memberId) { + return blockRepository.findAllByMemberId(memberId).stream() + .filter(block -> block.getBlockMember().isActive()) + .map(block -> block.getBlockMember().getMemberId()) + .toList(); + } + +} diff --git a/src/main/java/com/e2i/wemeet/service/team/TeamServiceImpl.java b/src/main/java/com/e2i/wemeet/service/team/TeamServiceImpl.java index 588706b7..231278f5 100644 --- a/src/main/java/com/e2i/wemeet/service/team/TeamServiceImpl.java +++ b/src/main/java/com/e2i/wemeet/service/team/TeamServiceImpl.java @@ -1,5 +1,7 @@ package com.e2i.wemeet.service.team; +import static com.e2i.wemeet.exception.ErrorCode.BLOCKED_TEAM; + import com.e2i.wemeet.domain.code.Code; import com.e2i.wemeet.domain.code.CodePk; import com.e2i.wemeet.domain.code.CodeRepository; @@ -18,6 +20,7 @@ import com.e2i.wemeet.dto.response.team.MyTeamDetailResponseDto; import com.e2i.wemeet.dto.response.team.MyTeamResponseDto; import com.e2i.wemeet.dto.response.team.TeamDetailResponseDto; +import com.e2i.wemeet.exception.badrequest.BlockedException; import com.e2i.wemeet.exception.notfound.CodeNotFoundException; import com.e2i.wemeet.exception.notfound.MemberNotFoundException; import com.e2i.wemeet.exception.notfound.TeamNotFoundException; @@ -110,6 +113,10 @@ public MyTeamResponseDto readTeam(Long memberId) { @Transactional @Override public TeamDetailResponseDto readByTeamId(final Long memberId, final Long teamId, final LocalDateTime readTime) { + if (teamRepository.isBlockedTeam(memberId, teamId)) { + throw new BlockedException(BLOCKED_TEAM); + } + TeamInformationDto teamInformation = teamRepository.findTeamInformationByTeamId(memberId, teamId, readTime) .orElseThrow(TeamNotFoundException::new); LeaderResponseDto leader = teamRepository.findLeaderByTeamId(teamId) diff --git a/src/main/resources/db/migration/V2309271230__create_block.sql b/src/main/resources/db/migration/V2309271230__create_block.sql new file mode 100644 index 00000000..827307df --- /dev/null +++ b/src/main/resources/db/migration/V2309271230__create_block.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS `block` +( + `block_id` bigint NOT NULL AUTO_INCREMENT, + `member` bigint NOT NULL, + `block_member` bigint NOT NULL, + `created_at` datetime(6), + PRIMARY KEY (`block_id`), + FOREIGN KEY (`member`) REFERENCES member (`member_id`) ON DELETE CASCADE, + FOREIGN KEY (`block_member`) REFERENCES member (`member_id`) ON DELETE CASCADE +) + ENGINE = InnoDB + DEFAULT CHARACTER SET = utf8mb4; diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 8583dbe8..8fb4cf2f 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -87,6 +87,8 @@ cost.not.found=Cost 정보를 찾을 수 없습니다. refresh.token.not.exist=저장소에 해당 유저의 RefreshToken이 존재하지 않습니다. recommender.already.exist=유저 추천은 계정당 1번만 가능합니다. image.count.exceeded=이미지 최대 등록 가능 개수를 초과하였습니다. +blocked.member=차단된 사용자입니다. +blocked.team=차단된 사용자의 팀입니다. # Expired expired=요청 대상이 만료되어 더 이상 사용할 수 없습니다. expired.meeting=미팅이 유효 기간이 만료되었습니다. diff --git a/src/main/resources/static/swagger-ui/openapi3.yaml b/src/main/resources/static/swagger-ui/openapi3.yaml index 046b35fc..f3fe787e 100644 --- a/src/main/resources/static/swagger-ui/openapi3.yaml +++ b/src/main/resources/static/swagger-ui/openapi3.yaml @@ -89,6 +89,63 @@ paths: 미팅 신청: value: "{\"status\":\"SUCCESS\",\"message\":\"Send meeting request\ \ success\",\"data\":null}" + /v1/member/block: + get: + tags: + - 차단 관련 API + summary: 차단한 사용자들의 ID를 조회합니다. + description: |2 + 차단한 사용자들의 ID를 조회합니다. + operationId: 차단된 사용자의 목록을 조회합니다. + security: + - AccessToken: [ ] + responses: + "200": + description: "200" + content: + application/json;charset=UTF-8: + schema: + $ref: '#/components/schemas/v1-member-block-61521594' + examples: + 차단된 사용자의 목록을 조회합니다.: + value: "{\"status\":\"SUCCESS\",\"message\":\"Read Block Member\ + \ Success\",\"data\":[2,3]}" + /v1/member/block/{blockMemberId}: + post: + tags: + - 차단 관련 API + summary: 지정된 사용자를 차단합니다. + description: |2 + url에서 ID로 넘겨받은 사용자를 차단합니다. + 차단된 사용자는 다음의 기능에서 노출되지 않습니다. + - 추천 API + - 받은 좋아요 + - 보낸 좋아요 + - 성사된 매칭 + - 보낸 미팅 신청 + - 받은 미팅 신청 + - 팀 상세 정보 조회 불가 + operationId: 차단하기 + security: + - AccessToken: [ ] + parameters: + - name: blockMemberId + in: path + description: "" + required: true + schema: + type: string + responses: + "200": + description: "200" + content: + application/json;charset=UTF-8: + schema: + $ref: '#/components/schemas/v1-member-block-blockMemberId-356556804' + examples: + 차단하기: + value: "{\"status\":\"SUCCESS\",\"message\":\"Block Member Success\"\ + ,\"data\":2}" /v1/member: get: tags: @@ -1052,6 +1109,36 @@ components: name: AccessToken in: header schemas: + v1-member-block-61521594: + type: object + properties: + data: + type: array + description: 차단된 사용자의 ID + items: + oneOf: + - type: object + - type: boolean + - type: string + - type: number + message: + type: string + description: 응답 메시지 + status: + type: string + description: 응답 상태 + v1-member-block-blockMemberId-356556804: + type: object + properties: + data: + type: number + description: 차단된 사용자의 ID + message: + type: string + description: 응답 메시지 + status: + type: string + description: 응답 상태 v1-team-1407123040: type: object properties: diff --git a/src/test/java/com/e2i/wemeet/controller/member/BlockControllerTest.java b/src/test/java/com/e2i/wemeet/controller/member/BlockControllerTest.java new file mode 100644 index 00000000..2889c423 --- /dev/null +++ b/src/test/java/com/e2i/wemeet/controller/member/BlockControllerTest.java @@ -0,0 +1,126 @@ +package com.e2i.wemeet.controller.member; + +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.e2i.wemeet.support.config.AbstractControllerUnitTest; +import com.e2i.wemeet.support.config.WithCustomMockUser; +import com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + +class BlockControllerTest extends AbstractControllerUnitTest { + + @DisplayName("사용자를 차단할 수 있다.") + @WithCustomMockUser + @Test + void block() throws Exception { + // given + given(blockService.block(1L, 2L)) + .willReturn(2L); + + // when + ResultActions perform = mockMvc.perform(post("/v1/member/block/{blockMemberId}", 2L)); + + // then + perform + .andExpectAll( + status().isOk(), + jsonPath("$.status").value("SUCCESS"), + jsonPath("$.message").value("Block Member Success"), + jsonPath("$.data").value(2L) + ); + + blockAPIWriteRestDocs(perform); + } + + @DisplayName("차단한 사용자의 ID 목록을 조회할 수 있다.") + @WithCustomMockUser(id = "10") + @Test + void readBlock() throws Exception { + // given + List idList = List.of(2L, 3L); + given(blockService.readBlockList(10L)) + .willReturn(idList); + + // when + ResultActions perform = mockMvc.perform(get("/v1/member/block")); + + // then + perform + .andExpectAll( + status().isOk(), + jsonPath("$.status").value("SUCCESS"), + jsonPath("$.message").value("Read Block Member Success"), + jsonPath("$.data.[0]").value(2L), + jsonPath("$.data.[1]").value(3L) + ); + + readBlockAPIWriteRestDocs(perform); + } + + private static void readBlockAPIWriteRestDocs(ResultActions perform) throws Exception { + perform + .andDo( + MockMvcRestDocumentationWrapper.document("차단된 사용자의 목록을 조회합니다.", + ResourceSnippetParameters.builder() + .tag("차단 관련 API") + .summary("차단한 사용자들의 ID를 조회합니다.") + .description( + """ + 차단한 사용자들의 ID를 조회합니다. + """) + .pathParameters( + parameterWithName("blockMemberId").description("차단할 사용자의 ID") + ), + responseFields( + fieldWithPath("status").type(JsonFieldType.STRING).description("응답 상태"), + fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.ARRAY) + .description("차단된 사용자의 ID") + ) + )); + } + + + private static void blockAPIWriteRestDocs(ResultActions perform) throws Exception { + perform + .andDo( + MockMvcRestDocumentationWrapper.document("차단하기", + ResourceSnippetParameters.builder() + .tag("차단 관련 API") + .summary("지정된 사용자를 차단합니다.") + .description( + """ + url에서 ID로 넘겨받은 사용자를 차단합니다. + 차단된 사용자는 다음의 기능에서 노출되지 않습니다. + - 추천 API + - 받은 좋아요 + - 보낸 좋아요 + - 성사된 매칭 + - 보낸 미팅 신청 + - 받은 미팅 신청 + - 팀 상세 조회 불가 + """) + .pathParameters( + parameterWithName("blockMemberId").description("차단할 사용자의 ID") + ), + responseFields( + fieldWithPath("status").type(JsonFieldType.STRING).description("응답 상태"), + fieldWithPath("message").type(JsonFieldType.STRING).description("응답 메시지"), + fieldWithPath("data").type(JsonFieldType.NUMBER) + .description("차단된 사용자의 ID") + ) + )); + } +} \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryTest.java b/src/test/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryTest.java index e422e5b8..b77e34d9 100644 --- a/src/test/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryTest.java +++ b/src/test/java/com/e2i/wemeet/domain/heart/HeartCustomRepositoryTest.java @@ -1,5 +1,6 @@ package com.e2i.wemeet.domain.heart; +import static com.e2i.wemeet.support.fixture.MemberFixture.JEONGYEOL; import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; import static com.e2i.wemeet.support.fixture.MemberFixture.RIM; import static com.e2i.wemeet.support.fixture.MemberFixture.SEYUN; @@ -10,6 +11,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; +import com.e2i.wemeet.domain.member.Block; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.member.Member; import com.e2i.wemeet.domain.member.MemberRepository; import com.e2i.wemeet.domain.team.Team; @@ -41,6 +44,9 @@ class HeartCustomRepositoryTest extends AbstractRepositoryUnitTest { @Autowired private HeartRepository heartRepository; + @Autowired + private BlockRepository blockRepository; + @DisplayName("보낸 좋아요 조회 테스트") @Nested class GetSentHeart { @@ -96,6 +102,33 @@ void getSentHeart_WithNotSentHeart() { // then assertThat(result).isEmpty(); } + + @DisplayName("차단된 사용자는 보낸 좋아요 목록에 조회되지 않는다.") + @Test + void getReceivedHeartWithoutBlockedMember() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + saveHeartEntity(rimTeam, jeonyeolTeam, seyunTeam); + + blockRepository.save(new Block(rim, seyun)); + LocalDateTime requestTime = LocalDateTime.now(); + + // when + List receivedHeart = heartCustomRepository.findSentHeart(rimTeam.getTeamId(), requestTime); + + // then + assertThat(receivedHeart).hasSize(1) + .extracting("teamId") + .containsExactly(jeonyeolTeam.getTeamId()); + } } @DisplayName("받은 좋아요 조회 테스트") @@ -118,14 +151,8 @@ void getReceivedHeart() { teamImageRepository.saveAll(BASIC_TEAM_IMAGE.createTeamImages(partnerTeam1)); teamImageRepository.saveAll(BASIC_TEAM_IMAGE.createTeamImages(partnerTeam2)); - heartRepository.save(Heart.builder() - .team(partnerTeam1) - .partnerTeam(team) - .build()); - heartRepository.save(Heart.builder() - .team(partnerTeam2) - .partnerTeam(team) - .build()); + saveHeartEntity(partnerTeam1, team); + saveHeartEntity(partnerTeam2, team); // when List result = heartCustomRepository.findReceivedHeart(team.getTeamId(), @@ -171,6 +198,49 @@ void getReceivedHeart_WithNotReceivedHeart() { // then assertThat(result).isEmpty(); } + + @DisplayName("차단된 사용자는 받은 좋아요 목록에 조회되지 않는다.") + @Test + void getReceivedHeartWithoutBlockedMember() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + + saveHeartEntity(jeonyeolTeam, rimTeam); + saveHeartEntity(seyunTeam, rimTeam); + + blockRepository.save(new Block(rim, seyun)); + LocalDateTime requestTime = LocalDateTime.now(); + + // when + List receivedHeart = heartCustomRepository.findReceivedHeart(rimTeam.getTeamId(), requestTime); + + // then + assertThat(receivedHeart).hasSize(1) + .extracting("teamId") + .containsExactly(jeonyeolTeam.getTeamId()); + } + } + + private void saveHeartEntity(Team team, Team... partnerTeam) { + for (Team partner : partnerTeam) { + heartRepository.save(Heart.builder() + .team(team) + .partnerTeam(partner) + .build()); + } + } + + private void saveTeamImages(Team... teams) { + for (Team team : teams) { + teamImageRepository.saveAll(BASIC_TEAM_IMAGE.createTeamImages(team)); + } } } diff --git a/src/test/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImplTest.java b/src/test/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImplTest.java index 30a294ca..31f9b30e 100644 --- a/src/test/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImplTest.java +++ b/src/test/java/com/e2i/wemeet/domain/meeting/MeetingReadRepositoryImplTest.java @@ -4,6 +4,7 @@ import static com.e2i.wemeet.support.fixture.MeetingRequestFixture.BASIC_REQUEST; import static com.e2i.wemeet.support.fixture.MemberFixture.CHAEWON; import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; +import static com.e2i.wemeet.support.fixture.MemberFixture.KARINA; import static com.e2i.wemeet.support.fixture.MemberFixture.RIM; import static com.e2i.wemeet.support.fixture.TeamFixture.HONGDAE_TEAM_1; import static com.e2i.wemeet.support.fixture.TeamImagesFixture.BASIC_TEAM_IMAGE; @@ -14,6 +15,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.groups.Tuple.tuple; +import com.e2i.wemeet.domain.member.Block; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.member.Member; import com.e2i.wemeet.domain.member.MemberRepository; import com.e2i.wemeet.domain.team.Team; @@ -50,6 +53,9 @@ class MeetingReadRepositoryImplTest extends AbstractRepositoryUnitTest { @Autowired private TeamImageRepository teamImageRepository; + @Autowired + private BlockRepository blockRepository; + @Nested class FindTeamProxy { @@ -147,9 +153,7 @@ void findAcceptedMeetingList() { Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); Team chaewonTeam = teamRepository.save(HONGDAE_TEAM_1.create(chaewon, create_3_woman())); - - teamImageRepository.saveAll(BASIC_TEAM_IMAGE.createTeamImages(rimTeam)); - teamImageRepository.saveAll(SECOND_TEAM_IMAGE.createTeamImages(chaewonTeam)); + saveTeamImages(rimTeam, chaewonTeam); meetingRepository.save(BASIC_MEETING.create(kaiTeam, rimTeam)); meetingRepository.save(BASIC_MEETING.create(chaewonTeam, kaiTeam)); @@ -161,12 +165,12 @@ void findAcceptedMeetingList() { assertThat(meetingList).hasSize(2) .extracting("memberCount", "region", "isDeleted", "teamProfileImageUrl", "leader.nickname") - .contains( + .containsExactlyInAnyOrder( tuple(4, rimTeam.getRegion(), false, BASIC_TEAM_IMAGE.getTeamImages(), rim.getNickname() ), tuple(4, chaewonTeam.getRegion(), false, - SECOND_TEAM_IMAGE.getTeamImages(), chaewon.getNickname() + BASIC_TEAM_IMAGE.getTeamImages(), chaewon.getNickname() ) ); @@ -186,6 +190,42 @@ void findAcceptedMeetingListWithNoneSuccessedMeeting() { assertThat(acceptedMeetingList).isEmpty(); } + @DisplayName("성사된 미팅 목록 조회 시, 차단된 팀은 조회되지 않는다.") + @Test + void findAcceptedMeetingListWithoutBlockedMember() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member rim = memberRepository.save(RIM.create(WOMANS_CODE)); + Member chaewon = memberRepository.save(CHAEWON.create(WOMANS_CODE)); + Member karina = memberRepository.save(KARINA.create(WOMANS_CODE)); + + Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team chaewonTeam = teamRepository.save(HONGDAE_TEAM_1.create(chaewon, create_3_woman())); + Team karinaTeam = teamRepository.save(HONGDAE_TEAM_1.create(karina, create_3_woman())); + saveTeamImages(rimTeam, chaewonTeam, karinaTeam); + + meetingRepository.save(BASIC_MEETING.create(kaiTeam, rimTeam)); + meetingRepository.save(BASIC_MEETING.create(chaewonTeam, kaiTeam)); + meetingRepository.save(BASIC_MEETING.create(karinaTeam, kaiTeam)); + + blockRepository.save(new Block(kai, rim)); + blockRepository.save(new Block(kai, chaewon)); + + // when + List acceptedMeetingList = meetingReadRepository.findAcceptedMeetingList(kai.getMemberId()); + + // then + assertThat(acceptedMeetingList).hasSize(1) + .extracting("memberCount", "region", "isDeleted", + "teamProfileImageUrl", "leader.nickname") + .contains( + tuple(4, karinaTeam.getRegion(), false, + BASIC_TEAM_IMAGE.getTeamImages(), karina.getNickname() + ) + ); + } + @DisplayName("팀이 없을 경우 아무것도 조회되지 않는다.") @Test void findAcceptedMeetingListWithNoTeam() { @@ -249,6 +289,40 @@ void findSentRequestListWithNoRequest() { assertThat(sentRequestList).isEmpty(); } + @DisplayName("보낸 요청 목록 조회 시, 차단된 팀은 조회되지 않는다.") + @Test + void findSentRequestListWithoutBlockedMember() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member chaewon = memberRepository.save(CHAEWON.create(WOMANS_CODE)); + Member karina = memberRepository.save(KARINA.create(WOMANS_CODE)); + + Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); + Team chaewonTeam = teamRepository.save(HONGDAE_TEAM_1.create(chaewon, create_3_woman())); + Team karinaTeam = teamRepository.save(HONGDAE_TEAM_1.create(karina, create_3_woman())); + saveTeamImages(chaewonTeam, karinaTeam); + + meetingRequestRepository.saveAll(List.of( + BASIC_REQUEST.create(kaiTeam, karinaTeam), + BASIC_REQUEST.create(kaiTeam, chaewonTeam)) + ); + + blockRepository.save(new Block(kai, chaewon)); + + // when + List sentRequestList = meetingReadRepository.findSentRequestList(kai.getMemberId()); + + // then + assertThat(sentRequestList).hasSize(1) + .extracting("memberCount", "region", "partnerTeamDeleted", + "teamProfileImageUrl", "leader.nickname") + .contains( + tuple(4, karinaTeam.getRegion(), false, + BASIC_TEAM_IMAGE.getTeamImages(), karina.getNickname() + ) + ); + } + @DisplayName("받은 요청을 목록을 조회할 수 있다.") @Test void findReceivedRequestList() { @@ -299,6 +373,46 @@ void findReceivedRequestListWithNoRequest() { assertThat(receivedRequest).isEmpty(); } + @DisplayName("받은 요청 목록 조회 시, 차단된 팀은 조회되지 않는다.") + @Test + void findReceivedRequestListWithoutBlockedMember() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member chaewon = memberRepository.save(CHAEWON.create(WOMANS_CODE)); + Member karina = memberRepository.save(KARINA.create(WOMANS_CODE)); + + Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); + Team chaewonTeam = teamRepository.save(HONGDAE_TEAM_1.create(chaewon, create_3_woman())); + Team karinaTeam = teamRepository.save(HONGDAE_TEAM_1.create(karina, create_3_woman())); + saveTeamImages(chaewonTeam, karinaTeam); + + meetingRequestRepository.saveAll(List.of( + BASIC_REQUEST.create(karinaTeam, kaiTeam), + BASIC_REQUEST.create(chaewonTeam, kaiTeam)) + ); + + blockRepository.save(new Block(kai, chaewon)); + + // when + List receivedRequests = meetingReadRepository.findReceiveRequestList(kai.getMemberId()); + + // then + assertThat(receivedRequests).hasSize(1) + .extracting("memberCount", "region", "partnerTeamDeleted", + "teamProfileImageUrl", "leader.nickname") + .contains( + tuple(4, karinaTeam.getRegion(), false, + BASIC_TEAM_IMAGE.getTeamImages(), karina.getNickname() + ) + ); + } + + } + + private void saveTeamImages(Team... teams) { + for (Team team : teams) { + teamImageRepository.saveAll(BASIC_TEAM_IMAGE.createTeamImages(team)); + } } } \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/domain/member/BlockRepositoryTest.java b/src/test/java/com/e2i/wemeet/domain/member/BlockRepositoryTest.java new file mode 100644 index 00000000..27a99512 --- /dev/null +++ b/src/test/java/com/e2i/wemeet/domain/member/BlockRepositoryTest.java @@ -0,0 +1,90 @@ +package com.e2i.wemeet.domain.member; + +import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; +import static com.e2i.wemeet.support.fixture.MemberFixture.RIM; +import static com.e2i.wemeet.support.fixture.MemberFixture.SEYUN; +import static com.e2i.wemeet.support.fixture.TeamFixture.HONGDAE_TEAM_1; +import static com.e2i.wemeet.support.fixture.TeamMemberFixture.create_3_man; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import com.e2i.wemeet.domain.team.Team; +import com.e2i.wemeet.domain.team.TeamRepository; +import com.e2i.wemeet.support.module.AbstractRepositoryUnitTest; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class BlockRepositoryTest extends AbstractRepositoryUnitTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private BlockRepository blockRepository; + + @Autowired + private TeamRepository teamRepository; + + @DisplayName("차단 목록을 조회할 수 있다.") + @Test + void findAllByMemberId() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member rim = memberRepository.save(RIM.create(WOMANS_CODE)); + blockRepository.save(new Block(kai, seyun)); + blockRepository.save(new Block(kai, rim)); + + // when + List findBlock = blockRepository.findAllByMemberId(kai.getMemberId()); + + // then + assertThat(findBlock).hasSize(2) + .extracting("member", "blockMember") + .containsExactly( + tuple(kai, seyun), + tuple(kai, rim) + ); + } + + @DisplayName("차단한 사용자 ID 목록을 조회할 수 있다.") + @Test + void findAllBlockMemberId() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member rim = memberRepository.save(RIM.create(WOMANS_CODE)); + blockRepository.save(new Block(kai, seyun)); + blockRepository.save(new Block(kai, rim)); + + // when + List findBlockMemberIds = blockRepository.findBlockMemberIds(kai.getMemberId()); + + // then + assertThat(findBlockMemberIds).hasSize(2) + .containsExactly(seyun.getMemberId(), rim.getMemberId()); + } + + @DisplayName("내 팀 ID로 차단한 사용자 ID 목록을 조회할 수 있다.") + @Test + void findAllBlockMemberIdByTeamId() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member rim = memberRepository.save(RIM.create(WOMANS_CODE)); + blockRepository.save(new Block(kai, seyun)); + blockRepository.save(new Block(kai, rim)); + + Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); + + // when + List findBlockMemberIds = blockRepository.findBlockMemberIdsByTeamId(kaiTeam.getTeamId()); + + // then + assertThat(findBlockMemberIds).hasSize(2) + .containsExactly(seyun.getMemberId(), rim.getMemberId()); + } + +} \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/domain/team/TeamRepositoryTest.java b/src/test/java/com/e2i/wemeet/domain/team/TeamRepositoryTest.java new file mode 100644 index 00000000..8293aacd --- /dev/null +++ b/src/test/java/com/e2i/wemeet/domain/team/TeamRepositoryTest.java @@ -0,0 +1,52 @@ +package com.e2i.wemeet.domain.team; + +import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; +import static com.e2i.wemeet.support.fixture.MemberFixture.KARINA; +import static com.e2i.wemeet.support.fixture.TeamFixture.HONGDAE_TEAM_1; +import static com.e2i.wemeet.support.fixture.TeamMemberFixture.create_3_man; +import static com.e2i.wemeet.support.fixture.TeamMemberFixture.create_3_woman; +import static org.assertj.core.api.Assertions.assertThat; + +import com.e2i.wemeet.domain.member.Block; +import com.e2i.wemeet.domain.member.BlockRepository; +import com.e2i.wemeet.domain.member.Member; +import com.e2i.wemeet.domain.member.MemberRepository; +import com.e2i.wemeet.support.fixture.MemberFixture; +import com.e2i.wemeet.support.module.AbstractRepositoryUnitTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class TeamRepositoryTest extends AbstractRepositoryUnitTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private TeamRepository teamRepository; + + @Autowired + private BlockRepository blockRepository; + + @DisplayName("팀이 차단된 사용자의 팀인지 확인") + @Test + void isBlockedTeam() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member karina = memberRepository.save(KARINA.create(HANYANG_CODE)); + Member rim = memberRepository.save(MemberFixture.RIM.create(WOMANS_CODE)); + Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); + Team karinaTeam = teamRepository.save(HONGDAE_TEAM_1.create(karina, create_3_woman())); + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + + blockRepository.save(new Block(kai, karina)); + + // when + boolean isBlocked = teamRepository.isBlockedTeam(kai.getMemberId(), karinaTeam.getTeamId()); + boolean isNotBlocked = teamRepository.isBlockedTeam(kai.getMemberId(), rimTeam.getTeamId()); + + // then + assertThat(isBlocked).isTrue(); + assertThat(isNotBlocked).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImplTest.java b/src/test/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImplTest.java new file mode 100644 index 00000000..ef72e957 --- /dev/null +++ b/src/test/java/com/e2i/wemeet/domain/team/suggestion/SuggestionRepositoryImplTest.java @@ -0,0 +1,224 @@ +package com.e2i.wemeet.domain.team.suggestion; + +import static com.e2i.wemeet.domain.team.suggestion.SuggestionRepositoryImpl.SUGGESTION_TEAM_LIMIT; +import static com.e2i.wemeet.support.fixture.MemberFixture.JEONGYEOL; +import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; +import static com.e2i.wemeet.support.fixture.MemberFixture.RIM; +import static com.e2i.wemeet.support.fixture.MemberFixture.SEYUN; +import static com.e2i.wemeet.support.fixture.TeamFixture.HONGDAE_TEAM_1; +import static com.e2i.wemeet.support.fixture.TeamImagesFixture.BASIC_TEAM_IMAGE; +import static com.e2i.wemeet.support.fixture.TeamMemberFixture.create_3_man; +import static com.e2i.wemeet.support.fixture.TeamMemberFixture.create_3_woman; +import static org.assertj.core.api.Assertions.assertThat; + +import com.e2i.wemeet.domain.history.History; +import com.e2i.wemeet.domain.history.HistoryRepository; +import com.e2i.wemeet.domain.member.Block; +import com.e2i.wemeet.domain.member.BlockRepository; +import com.e2i.wemeet.domain.member.Member; +import com.e2i.wemeet.domain.member.MemberRepository; +import com.e2i.wemeet.domain.team.Team; +import com.e2i.wemeet.domain.team.TeamRepository; +import com.e2i.wemeet.domain.team.data.suggestion.SuggestionHistoryData; +import com.e2i.wemeet.domain.team.data.suggestion.SuggestionTeamData; +import com.e2i.wemeet.domain.team_image.TeamImageRepository; +import com.e2i.wemeet.support.module.AbstractRepositoryUnitTest; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +class SuggestionRepositoryImplTest extends AbstractRepositoryUnitTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private TeamRepository teamRepository; + + @Autowired + private TeamImageRepository teamImageRepository; + + @Autowired + @Qualifier("suggestionRepositoryImpl") + private SuggestionRepository suggestionRepository; + + @Autowired + private BlockRepository blockRepository; + + @Autowired + private HistoryRepository historyRepository; + + @DisplayName("오늘의 추천 팀을 랜덤으로 조회할 수 있다.") + @Test + void findSuggestionTeamForUser() { + // given + final Member rim = memberRepository.save(RIM.create(WOMANS_CODE)); + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, kaiTeam, seyunTeam); + + // when + List suggestion = suggestionRepository.findSuggestionTeamForUser(rim.getMemberId(), rim.getGender()); + + // then + assertThat(suggestion).hasSize(SUGGESTION_TEAM_LIMIT) + .extracting("team") + .containsAnyOf(jeonyeolTeam, kaiTeam, seyunTeam); + } + + @DisplayName("차단된 사용자는 추천되지 않는다.") + @Test + void findSuggestionTeamForUserWithoutBlock() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + + blockRepository.save(new Block(rim, seyun)); + + // when + List suggestion = suggestionRepository.findSuggestionTeamForUser(rim.getMemberId(), rim.getGender()); + + // then + assertThat(suggestion).hasSize(1) + .extracting("team") + .containsExactly(jeonyeolTeam); + } + + @DisplayName("오늘 추천받은 팀을 조회할 수 있다.") + @Test + void findHistory() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + + List suggestion = suggestionRepository.findSuggestionTeamForUser(rim.getMemberId(), rim.getGender()); + saveHistories(suggestion, rim); + + // when + LocalDateTime requestTime = LocalDateTime.now(); + List histories = suggestionRepository.findHistory(rim.getMemberId(), requestTime); + + // then + assertThat(histories).hasSize(SUGGESTION_TEAM_LIMIT) + .extracting("team") + .containsAnyOf(jeonyeolTeam, seyunTeam); + } + + @DisplayName("차단된 사용자는 히스토리에서 조회되지 않는다.") + @Test + void findHistoryWithoutBlockedMember() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + + List suggestion = suggestionRepository.findSuggestionTeamForUser(rim.getMemberId(), rim.getGender()); + saveHistories(suggestion, rim); + LocalDateTime requestTime = LocalDateTime.now(); + + // when + blockRepository.save(new Block(rim, seyun)); + List histories = suggestionRepository.findHistory(rim.getMemberId(), requestTime); + + // then + assertThat(histories).hasSize(1) + .extracting("team") + .containsExactly(jeonyeolTeam); + } + + @DisplayName("추천받은 팀 정보를 조회할 수 있다.") + @Test + void findSuggestionHistoryTeam() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + + List suggestion = suggestionRepository.findSuggestionTeamForUser(rim.getMemberId(), rim.getGender()); + saveHistories(suggestion, rim); + LocalDateTime requestTime = LocalDateTime.now(); + + // when + List histories = suggestionRepository.findSuggestionHistoryTeam(rim.getMemberId(), requestTime); + + // then + assertThat(histories).hasSize(suggestion.size()) + .extracting("teamId") + .containsAnyOf(jeonyeolTeam.getTeamId(), seyunTeam.getTeamId()); + } + + @DisplayName("차단한 사용자는 추천받은 팀을 조회할 때 제외된다.") + @Test + void findSuggestionHistoryTeamWithoutBlockMember() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + + List suggestion = suggestionRepository.findSuggestionTeamForUser(rim.getMemberId(), rim.getGender()); + saveHistories(suggestion, rim); + LocalDateTime requestTime = LocalDateTime.now(); + + // when + blockRepository.save(new Block(rim, seyun)); + List histories = suggestionRepository.findSuggestionHistoryTeam(rim.getMemberId(), requestTime); + + // then + assertThat(histories).hasSize(1) + .extracting("teamId") + .containsAnyOf(jeonyeolTeam.getTeamId()); + } + + private void saveHistories(List suggestion, Member rim) { + List suggestionHistories = suggestion.stream() + .map(suggestionTeamData -> History.builder() + .member(rim) + .team(suggestionTeamData.team()) + .isLiked(false) + .build()) + .toList(); + historyRepository.saveAll(suggestionHistories); + } + + private void saveTeamImages(Team... teams) { + for (Team team : teams) { + teamImageRepository.saveAll(BASIC_TEAM_IMAGE.createTeamImages(team)); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/service/heart/HeartServiceTest.java b/src/test/java/com/e2i/wemeet/service/heart/HeartServiceTest.java index 35127f3c..f8615114 100644 --- a/src/test/java/com/e2i/wemeet/service/heart/HeartServiceTest.java +++ b/src/test/java/com/e2i/wemeet/service/heart/HeartServiceTest.java @@ -1,5 +1,6 @@ package com.e2i.wemeet.service.heart; +import static com.e2i.wemeet.support.fixture.MemberFixture.JEONGYEOL; import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; import static com.e2i.wemeet.support.fixture.MemberFixture.RIM; import static com.e2i.wemeet.support.fixture.MemberFixture.SEYUN; @@ -13,6 +14,8 @@ import com.e2i.wemeet.domain.heart.Heart; import com.e2i.wemeet.domain.heart.HeartRepository; +import com.e2i.wemeet.domain.member.Block; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.member.Member; import com.e2i.wemeet.domain.member.MemberRepository; import com.e2i.wemeet.domain.team.Team; @@ -48,9 +51,13 @@ class HeartServiceTest extends AbstractServiceTest { @Autowired private TeamImageRepository teamImageRepository; + @Autowired private HeartRepository heartRepository; + @Autowired + private BlockRepository blockRepository; + @DisplayName("좋아요 보내기 테스트") @Nested @@ -206,6 +213,33 @@ void getSentHeart_WithoutTeam() { () -> heartService.getSentHeart(member.getMemberId(), LocalDateTime.now())) .isExactlyInstanceOf(TeamNotExistsException.class); } + + @DisplayName("차단한 사용자는 보낸 좋아요 내역을 조회할 때 제외된다.") + @Test + void getSentHeart_WithoutBlockMembers() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + saveHeartEntity(rimTeam, jeonyeolTeam, seyunTeam); + + blockRepository.save(new Block(rim, seyun)); + LocalDateTime requestTime = LocalDateTime.now(); + + // when + List sentHeart = heartService.getSentHeart(rim.getMemberId(), requestTime); + + // then + assertThat(sentHeart).hasSize(1) + .extracting("teamId") + .containsExactly(jeonyeolTeam.getTeamId()); + } } @DisplayName("받은 좋아요 조회 테스트") @@ -276,5 +310,48 @@ void getReceivedHeart_WithoutTeam() { () -> heartService.getReceivedHeart(member.getMemberId(), LocalDateTime.now())) .isExactlyInstanceOf(TeamNotExistsException.class); } + + @DisplayName("차단된 사용자는 받은 좋아요 목록에 조회되지 않는다.") + @Test + void getReceivedHeartWithoutBlockedMember() { + // given + Member rim = memberRepository.save(RIM.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member jeongyeol = memberRepository.save(JEONGYEOL.create(HANYANG_CODE)); + + Team rimTeam = teamRepository.save(HONGDAE_TEAM_1.create(rim, create_3_woman())); + Team jeonyeolTeam = teamRepository.save(HONGDAE_TEAM_1.create(jeongyeol, create_3_man())); + Team seyunTeam = teamRepository.save(HONGDAE_TEAM_1.create(seyun, create_3_man())); + saveTeamImages(rimTeam, jeonyeolTeam, seyunTeam); + + saveHeartEntity(jeonyeolTeam, rimTeam); + saveHeartEntity(seyunTeam, rimTeam); + + blockRepository.save(new Block(rim, seyun)); + LocalDateTime requestTime = LocalDateTime.now(); + + // when + List receivedHeart = heartService.getReceivedHeart(rim.getMemberId(), requestTime); + + // then + assertThat(receivedHeart).hasSize(1) + .extracting("teamId") + .containsExactly(jeonyeolTeam.getTeamId()); + } + } + + private void saveHeartEntity(Team team, Team... partnerTeam) { + for (Team partner : partnerTeam) { + heartRepository.save(Heart.builder() + .team(team) + .partnerTeam(partner) + .build()); + } + } + + private void saveTeamImages(Team... teams) { + for (Team team : teams) { + teamImageRepository.saveAll(BASIC_TEAM_IMAGE.createTeamImages(team)); + } } } \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/service/member/BlockServiceImplTest.java b/src/test/java/com/e2i/wemeet/service/member/BlockServiceImplTest.java new file mode 100644 index 00000000..c41c33d0 --- /dev/null +++ b/src/test/java/com/e2i/wemeet/service/member/BlockServiceImplTest.java @@ -0,0 +1,73 @@ +package com.e2i.wemeet.service.member; + +import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; +import static com.e2i.wemeet.support.fixture.MemberFixture.SEYUN; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +import com.e2i.wemeet.domain.member.Block; +import com.e2i.wemeet.domain.member.BlockRepository; +import com.e2i.wemeet.domain.member.Member; +import com.e2i.wemeet.domain.member.MemberRepository; +import com.e2i.wemeet.support.fixture.MemberFixture; +import com.e2i.wemeet.support.module.AbstractServiceTest; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +class BlockServiceImplTest extends AbstractServiceTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private BlockRepository blockRepository; + + @Autowired + private BlockService blockService; + + @DisplayName("다른 사용자를 차단할 수 있다.") + @Test + void block() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + + // when + Long blockMemberId = blockService.block(kai.getMemberId(), seyun.getMemberId()); + + // then + List findBlocks = blockRepository.findAllByMemberId(kai.getMemberId()); + assertThat(blockMemberId).isEqualTo(seyun.getMemberId()); + assertThat(findBlocks).hasSize(1) + .extracting("member", "blockMember") + .contains( + tuple(kai, seyun) + ); + } + + @DisplayName("차단한 사용자 목록을 조회할 수 있다.") + @Test + void readBlock() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member seyun = memberRepository.save(SEYUN.create(KOREA_CODE)); + Member rim = memberRepository.save(MemberFixture.RIM.create(WOMANS_CODE)); + blockRepository.save(new Block(kai, seyun)); + blockRepository.save(new Block(kai, rim)); + + // when + List blockIdList = blockService.readBlockList(kai.getMemberId()); + + // then + assertThat(blockIdList).hasSize(2) + .containsExactly( + seyun.getMemberId(), + rim.getMemberId() + ); + } + +} \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/service/team/TeamServiceImplTest.java b/src/test/java/com/e2i/wemeet/service/team/TeamServiceImplTest.java index 63ce31bd..c0c5a620 100644 --- a/src/test/java/com/e2i/wemeet/service/team/TeamServiceImplTest.java +++ b/src/test/java/com/e2i/wemeet/service/team/TeamServiceImplTest.java @@ -1,16 +1,20 @@ package com.e2i.wemeet.service.team; import static com.e2i.wemeet.support.fixture.MemberFixture.KAI; +import static com.e2i.wemeet.support.fixture.MemberFixture.KARINA; import static com.e2i.wemeet.support.fixture.MemberFixture.RIM; import static com.e2i.wemeet.support.fixture.TeamFixture.HONGDAE_TEAM_1; import static com.e2i.wemeet.support.fixture.TeamImagesFixture.BASIC_TEAM_IMAGE; import static com.e2i.wemeet.support.fixture.TeamMemberFixture.create_3_man; import static com.e2i.wemeet.support.fixture.TeamMemberFixture.create_3_woman; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.groups.Tuple.tuple; import com.e2i.wemeet.domain.heart.Heart; import com.e2i.wemeet.domain.heart.HeartRepository; +import com.e2i.wemeet.domain.member.Block; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.member.Member; import com.e2i.wemeet.domain.member.MemberRepository; import com.e2i.wemeet.domain.member.data.CollegeType; @@ -23,6 +27,7 @@ import com.e2i.wemeet.domain.team.data.Region; import com.e2i.wemeet.domain.team_image.TeamImageRepository; import com.e2i.wemeet.dto.response.team.TeamDetailResponseDto; +import com.e2i.wemeet.exception.badrequest.BlockedException; import com.e2i.wemeet.support.module.AbstractServiceTest; import java.time.LocalDateTime; import org.junit.jupiter.api.DisplayName; @@ -48,6 +53,9 @@ class TeamServiceImplTest extends AbstractServiceTest { @Autowired private HeartRepository heartRepository; + @Autowired + private BlockRepository blockRepository; + @DisplayName("TeamID로 Team 정보를 조회할 수 있다.") @Test void readByTeamId() { @@ -92,4 +100,21 @@ void readByTeamId() { .contains(kai.getMemberId(), kai.getNickname(), kai.getCollegeName(), kai.getCollegeInfo().getCollegeType(), kai.getProfileImage().getLowUrl()); } + + @DisplayName("차단된 사용자의 팀은 조회할 수 없다.") + @Test + void readByTeamId_withBlockTeam() { + // given + Member kai = memberRepository.save(KAI.create(ANYANG_CODE)); + Member karina = memberRepository.save(KARINA.create(HANYANG_CODE)); + Team kaiTeam = teamRepository.save(HONGDAE_TEAM_1.create(kai, create_3_man())); + Team karinaTeam = teamRepository.save(HONGDAE_TEAM_1.create(karina, create_3_woman())); + + blockRepository.save(new Block(kai, karina)); + LocalDateTime requestTime = LocalDateTime.now(); + + // when & then + assertThatThrownBy(() -> teamService.readByTeamId(kai.getMemberId(), karinaTeam.getTeamId(), requestTime)) + .isExactlyInstanceOf(BlockedException.class); + } } \ No newline at end of file diff --git a/src/test/java/com/e2i/wemeet/support/config/AbstractControllerUnitTest.java b/src/test/java/com/e2i/wemeet/support/config/AbstractControllerUnitTest.java index 7c58a276..6beb11e4 100644 --- a/src/test/java/com/e2i/wemeet/support/config/AbstractControllerUnitTest.java +++ b/src/test/java/com/e2i/wemeet/support/config/AbstractControllerUnitTest.java @@ -6,6 +6,7 @@ import com.e2i.wemeet.controller.credit.CreditController; import com.e2i.wemeet.controller.heart.HeartController; import com.e2i.wemeet.controller.meeting.MeetingController; +import com.e2i.wemeet.controller.member.BlockController; import com.e2i.wemeet.controller.member.MemberController; import com.e2i.wemeet.controller.member.RecommendController; import com.e2i.wemeet.controller.suggestion.SuggestionController; @@ -16,6 +17,7 @@ import com.e2i.wemeet.service.heart.HeartService; import com.e2i.wemeet.service.meeting.MeetingHandleService; import com.e2i.wemeet.service.meeting.MeetingListService; +import com.e2i.wemeet.service.member.BlockService; import com.e2i.wemeet.service.member.MemberService; import com.e2i.wemeet.service.member.RecommendService; import com.e2i.wemeet.service.member_image.MemberImageService; @@ -52,7 +54,8 @@ HeartController.class, CreditController.class, RecommendController.class, - TeamImageController.class + TeamImageController.class, + BlockController.class }) public abstract class AbstractControllerUnitTest { @@ -94,6 +97,8 @@ public abstract class AbstractControllerUnitTest { @MockBean protected TeamImageService teamImageService; + @MockBean + protected BlockService blockService; protected MockMvc mockMvc; diff --git a/src/test/java/com/e2i/wemeet/support/config/QueryDslTestConfig.java b/src/test/java/com/e2i/wemeet/support/config/QueryDslTestConfig.java index 159946c5..86fcbe89 100644 --- a/src/test/java/com/e2i/wemeet/support/config/QueryDslTestConfig.java +++ b/src/test/java/com/e2i/wemeet/support/config/QueryDslTestConfig.java @@ -2,6 +2,7 @@ import com.e2i.wemeet.domain.meeting.MeetingReadRepository; import com.e2i.wemeet.domain.meeting.MeetingReadRepositoryImpl; +import com.e2i.wemeet.domain.member.BlockRepository; import com.e2i.wemeet.domain.member.persist.PersistLoginRepository; import com.e2i.wemeet.domain.member.persist.PersistLoginRepositoryImpl; import com.e2i.wemeet.util.encryption.AdvancedEncryptionStandard; @@ -32,7 +33,7 @@ public PersistLoginRepository persistLoginRepository() { } @Bean - public MeetingReadRepository meetingReadRepository(EntityManager entityManager) { - return new MeetingReadRepositoryImpl(jpaQueryFactory(), entityManager); + public MeetingReadRepository meetingReadRepository(EntityManager entityManager, BlockRepository blockRepository) { + return new MeetingReadRepositoryImpl(jpaQueryFactory(), entityManager, blockRepository); } } diff --git a/src/test/java/com/e2i/wemeet/support/fixture/MemberFixture.java b/src/test/java/com/e2i/wemeet/support/fixture/MemberFixture.java index adb72175..c207cb0b 100644 --- a/src/test/java/com/e2i/wemeet/support/fixture/MemberFixture.java +++ b/src/test/java/com/e2i/wemeet/support/fixture/MemberFixture.java @@ -31,10 +31,12 @@ public enum MemberFixture { JEONGYEOL("정열", Gender.MAN, "+8210333344444", "2014p13@korea.ac.kr", KOREA.create(), Mbti.ESFJ, 100, false, "/v1/asdf", "/v1/jeong", true, Role.USER), - CHAEWON("채원", Gender.WOMAN, "+821089071365", "2020p13@woman.ac.kr", WOMAN.create(), Mbti.ISTJ, 100, false, - "/v1/asdf", "/v1/chaechae", true, Role.USER); + "/v1/asdf", "/v1/chaechae", true, Role.USER), + KARINA("카리나", Gender.WOMAN, "+821099374565", "2025p1234@woman.ac.kr", + WOMAN.create(), Mbti.ESTJ, 100, false, + "/v1/kaka", "/v1/karina", true, Role.USER); private final String nickname; private final Gender gender;