Skip to content

Commit

Permalink
feat(block): Adding Blocking Features (#153)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
KAispread authored Sep 28, 2023
1 parent 924199a commit edb39a0
Show file tree
Hide file tree
Showing 28 changed files with 1,297 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -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<Long> blockMember(@MemberId Long memberId, @PathVariable Long blockMemberId) {
final Long blockedMemberId = blockService.block(memberId, blockMemberId);
return ResponseDto.success("Block Member Success", blockedMemberId);
}

@GetMapping
public ResponseDto<List<Long>> readBlockMembers(@MemberId Long memberId) {
final List<Long> blockList = blockService.readBlockList(memberId);
return ResponseDto.success("Read Block Member Success", blockList);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -19,12 +20,16 @@
public class HeartCustomRepositoryImpl implements HeartCustomRepository {

private final JPAQueryFactory queryFactory;
private final BlockRepository blockRepository;

@Override
public List<HeartTeamData> findSentHeart(Long teamId,
LocalDateTime requestedTime) {
LocalDateTime beforeTime = requestedTime.minusDays(1);

// 차단 목록 조회
List<Long> blockMemberIds = blockRepository.findBlockMemberIdsByTeamId(teamId);

return queryFactory
.select(
Projections.constructor(HeartTeamData.class, heart.partnerTeam.teamId,
Expand All @@ -40,12 +45,12 @@ public List<HeartTeamData> 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();
Expand All @@ -56,6 +61,9 @@ public List<HeartTeamData> findReceivedHeart(Long teamId,
LocalDateTime requestedTime) {
LocalDateTime beforeTime = requestedTime.minusDays(1);

// 차단 목록 조회
List<Long> blockMemberIds = blockRepository.findBlockMemberIdsByTeamId(teamId);

return queryFactory
.select(
Projections.constructor(HeartTeamData.class, heart.team.teamId,
Expand All @@ -75,6 +83,7 @@ public List<HeartTeamData> findReceivedHeart(Long teamId,
team.teamId.eq(teamId),
team.deletedAt.isNull(),
heart.createdAt.between(beforeTime, requestedTime),
member.memberId.notIn(blockMemberIds),
teamImage.sequence.eq(1)
)
.fetch();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -64,11 +66,14 @@ public Team findTeamReferenceById(final Long teamId) {
// 성사된 미팅 조회
@Override
public List<AcceptedMeetingResponseDto> findAcceptedMeetingList(final Long memberId) {
// 차단된 사용자 조회
List<Long> blockMemberIds = blockRepository.findBlockMemberIds(memberId);

// 내가 미팅 신청하고 성사되었을 때 목록
List<MeetingInformationDto> meetingList = findMeetingInformationWhatIRequested(memberId);
List<MeetingInformationDto> meetingList = findMeetingInformationWhatIRequested(memberId, blockMemberIds);

// 내가 미팅 신청받고 수락하여 성사되었을 때 목록
meetingList.addAll(findMeetingInformationWhatIReceived(memberId));
meetingList.addAll(findMeetingInformationWhatIReceived(memberId, blockMemberIds));

return meetingList.stream()
.map(meetingInformation -> AcceptedMeetingResponseDto.of(
Expand All @@ -78,8 +83,70 @@ meetingInformation, findTeamProfileImageUrl(meetingInformation.getTeamId())
.toList();
}

// 보낸 미팅 신청 조회
@Override
public List<SentMeetingResponseDto> findSentRequestList(final Long memberId) {
// 차단된 사용자 조회
List<Long> blockMemberIds = blockRepository.findBlockMemberIds(memberId);

List<MeetingRequestInformationDto> 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<ReceivedMeetingResponseDto> findReceiveRequestList(final Long memberId) {
// 차단된 사용자 조회
List<Long> blockMemberIds = blockRepository.findBlockMemberIds(memberId);

List<MeetingRequestInformationDto> 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<MeetingInformationDto> findMeetingInformationWhatIRequested(Long memberId) {
private List<MeetingInformationDto> findMeetingInformationWhatIRequested(final Long memberId, final List<Long> blockMemberIds) {
return selectMeetingInformationDto()
.from(meeting)
// My Team & Partner Team
Expand All @@ -92,13 +159,15 @@ private List<MeetingInformationDto> 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<MeetingInformationDto> findMeetingInformationWhatIReceived(Long memberId) {
private List<MeetingInformationDto> findMeetingInformationWhatIReceived(final Long memberId, final List<Long> blockMemberIds) {
return selectMeetingInformationDto()
.from(meeting)
// My Team & Partner Team
Expand All @@ -111,7 +180,9 @@ private List<MeetingInformationDto> 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();
}
Expand All @@ -137,58 +208,6 @@ private JPAQuery<MeetingInformationDto> selectMeetingInformationDto() {
));
}

// 보낸 미팅 신청 조회
@Override
public List<SentMeetingResponseDto> findSentRequestList(final Long memberId) {
List<MeetingRequestInformationDto> 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<ReceivedMeetingResponseDto> findReceiveRequestList(final Long memberId) {
List<MeetingRequestInformationDto> 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<MeetingRequestInformationDto> selectMeetingRequestInformationDto() {
return queryFactory.select(
new QMeetingRequestInformationDto(
Expand Down
39 changes: 39 additions & 0 deletions src/main/java/com/e2i/wemeet/domain/member/Block.java
Original file line number Diff line number Diff line change
@@ -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;
}

}
27 changes: 27 additions & 0 deletions src/main/java/com/e2i/wemeet/domain/member/BlockRepository.java
Original file line number Diff line number Diff line change
@@ -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<Block, Long> {

// 차단 목록 조회
@Query("select b from Block b join fetch b.blockMember where b.member.memberId = :memberId")
List<Block> findAllByMemberId(@Param("memberId") Long memberId);

// 차단한 사용자 목록 조회 (ID)
@Query("select b.blockMember.memberId from Block b where b.member.memberId = :memberId")
List<Long> 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<Long> findBlockMemberIdsByTeamId(@Param("teamId") Long teamId);

}
21 changes: 21 additions & 0 deletions src/main/java/com/e2i/wemeet/domain/member/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ public class Member extends BaseTimeEntity {
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<History> history = new ArrayList<>();

@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Block> blocks = new ArrayList<>();

@Builder
public Member(String nickname, Gender gender, String phoneNumber, String email,
CollegeInfo collegeInfo, Mbti mbti, Integer credit, Boolean allowMarketing,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}

}

Loading

0 comments on commit edb39a0

Please sign in to comment.