diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/data/dto/FriendRelationDto.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/data/dto/FriendRelationDto.java new file mode 100644 index 000000000..37fe261c9 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/data/dto/FriendRelationDto.java @@ -0,0 +1,9 @@ +package site.timecapsulearchive.core.domain.friend.data.dto; + +public record FriendRelationDto( + Boolean isFriend, + Boolean isFriendInviteToFriend, + Boolean isFriendInviteToMe +) { + +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/data/dto/FriendRelations.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/data/dto/FriendRelations.java new file mode 100644 index 000000000..d4d9b83b4 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/data/dto/FriendRelations.java @@ -0,0 +1,16 @@ +package site.timecapsulearchive.core.domain.friend.data.dto; + +import java.util.Map; + +public class FriendRelations { + + private final Map friendRelationMap; + + public FriendRelations(Map friendRelationMap) { + this.friendRelationMap = friendRelationMap; + } + + public FriendRelationDto getFriendRelation(final Long friendId) { + return friendRelationMap.get(friendId); + } +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepository.java index 73a9bb6d4..98b3efd01 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepository.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Optional; import org.springframework.data.domain.Slice; +import site.timecapsulearchive.core.domain.friend.data.dto.FriendRelations; import site.timecapsulearchive.core.domain.friend.data.dto.FriendSummaryDto; import site.timecapsulearchive.core.domain.friend.data.dto.SearchFriendSummaryDto; import site.timecapsulearchive.core.domain.friend.data.dto.SearchFriendSummaryDtoByTag; @@ -34,4 +35,6 @@ Slice findFriends( final FriendBeforeGroupInviteRequest request); List findFriendIds(final List groupMemberIds, final Long memberId); + + FriendRelations findFriendRelations(List memberIds, Long ownerId); } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepositoryImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepositoryImpl.java index 79ce9c88b..e09cbe3f9 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepositoryImpl.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/friend/repository/member_friend/MemberFriendQueryRepositoryImpl.java @@ -1,5 +1,6 @@ package site.timecapsulearchive.core.domain.friend.repository.member_friend; +import static com.querydsl.core.group.GroupBy.groupBy; import static site.timecapsulearchive.core.domain.friend.entity.QMemberFriend.memberFriend; import static site.timecapsulearchive.core.domain.member.entity.QMember.member; @@ -17,6 +18,8 @@ import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Repository; +import site.timecapsulearchive.core.domain.friend.data.dto.FriendRelationDto; +import site.timecapsulearchive.core.domain.friend.data.dto.FriendRelations; import site.timecapsulearchive.core.domain.friend.data.dto.FriendSummaryDto; import site.timecapsulearchive.core.domain.friend.data.dto.SearchFriendSummaryDto; import site.timecapsulearchive.core.domain.friend.data.dto.SearchFriendSummaryDtoByTag; @@ -199,4 +202,44 @@ public List findFriendIds(final List groupMemberIds, final Long memb memberFriend.friend.id.in(groupMemberIds).and(memberFriend.owner.id.eq(memberId))) .fetch(); } + + @Override + public FriendRelations findFriendRelations(final List memberIds, + final Long ownerId) { + final QFriendInvite friendInviteToFriend = new QFriendInvite(FRIEND_INVITE_TO_FRIEND_PATH); + final QFriendInvite friendInviteToMe = new QFriendInvite(FRIEND_INVITE_TO_ME_PATH); + + return new FriendRelations( + jpaQueryFactory + .select( + member.id, + memberFriend.id.isNotNull(), + friendInviteToFriend.isNotNull(), + friendInviteToMe.isNotNull() + ) + .from(member) + .leftJoin(memberFriend) + .on(memberFriend.friend.id.eq(member.id).and(memberFriend.owner.id.eq(ownerId)), + memberFriend.deletedAt.isNull()) + .leftJoin(friendInviteToFriend) + .on(friendInviteToFriend.friend.id.eq(member.id) + .and(friendInviteToFriend.owner.id.eq(ownerId)), + friendInviteToFriend.deletedAt.isNull()) + .leftJoin(friendInviteToMe) + .on(friendInviteToMe.owner.id.eq(member.id) + .and(friendInviteToMe.friend.id.eq(ownerId)), + friendInviteToMe.deletedAt.isNull()) + .where(member.id.in(memberIds)) + .transform( + groupBy(member.id).as( + Projections.constructor( + FriendRelationDto.class, + memberFriend.id.isNotNull(), + friendInviteToFriend.isNotNull(), + friendInviteToMe.isNotNull() + ) + ) + ) + ); + } } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupDetailTotalDto.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupDetailTotalDto.java index a9c60bf56..a589f9209 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupDetailTotalDto.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupDetailTotalDto.java @@ -1,8 +1,10 @@ package site.timecapsulearchive.core.domain.group.data.dto; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.List; import java.util.function.Function; +import site.timecapsulearchive.core.domain.friend.data.dto.FriendRelations; import site.timecapsulearchive.core.domain.group.data.response.GroupDetailResponse; import site.timecapsulearchive.core.domain.group.data.response.GroupMemberWithRelationResponse; @@ -19,12 +21,15 @@ public record GroupDetailTotalDto( public static GroupDetailTotalDto as( final GroupDetailDto groupDetailDto, final Long groupCapsuleTotalCount, - final List friendIds + final FriendRelations friendRelations ) { - final List membersWithRelation = groupDetailDto.members() - .stream() - .map(dto -> dto.toRelationDto(friendIds)) - .toList(); + List membersWithRelation = Collections.emptyList(); + if (friendRelations != null) { + membersWithRelation = groupDetailDto.members() + .stream() + .map(dto -> dto.toRelationDto(friendRelations.getFriendRelation(dto.memberId()))) + .toList(); + } return new GroupDetailTotalDto( groupDetailDto.groupName(), diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberDto.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberDto.java index 2701cbf21..28c7e0cbe 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberDto.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberDto.java @@ -1,7 +1,7 @@ package site.timecapsulearchive.core.domain.group.data.dto; -import java.util.List; import java.util.function.Function; +import site.timecapsulearchive.core.domain.friend.data.dto.FriendRelationDto; import site.timecapsulearchive.core.domain.group.data.response.GroupMemberInfoResponse; public record GroupMemberDto( @@ -12,14 +12,16 @@ public record GroupMemberDto( Boolean isOwner ) { - public GroupMemberWithRelationDto toRelationDto(final List friendIds) { + public GroupMemberWithRelationDto toRelationDto(final FriendRelationDto friendRelationDto) { return GroupMemberWithRelationDto.builder() .memberId(memberId) .profileUrl(profileUrl) .nickname(nickname) .tag(tag) .isOwner(isOwner) - .isFriend(friendIds.contains(memberId)) + .isFriend(friendRelationDto.isFriend()) + .isFriendInviteToFriend(friendRelationDto.isFriendInviteToFriend()) + .isFriendInviteToMe(friendRelationDto.isFriendInviteToMe()) .build(); } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberWithRelationDto.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberWithRelationDto.java index 1c8ce2fd8..f32f09f32 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberWithRelationDto.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupMemberWithRelationDto.java @@ -10,7 +10,9 @@ public record GroupMemberWithRelationDto( String nickname, String tag, Boolean isOwner, - Boolean isFriend + Boolean isFriend, + Boolean isFriendInviteToFriend, + Boolean isFriendInviteToMe ) { public GroupMemberWithRelationResponse toResponse() { @@ -21,6 +23,8 @@ public GroupMemberWithRelationResponse toResponse() { .tag(tag) .isOwner(isOwner) .isFriend(isFriend) + .isFriendInviteToFriend(isFriendInviteToFriend) + .isFriendInviteToMe(isFriendInviteToMe) .build(); } } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/response/GroupMemberWithRelationResponse.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/response/GroupMemberWithRelationResponse.java index e4feca504..c2cc19ae0 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/response/GroupMemberWithRelationResponse.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/response/GroupMemberWithRelationResponse.java @@ -23,7 +23,13 @@ public record GroupMemberWithRelationResponse( Boolean isOwner, @Schema(description = "친구 여부") - Boolean isFriend + Boolean isFriend, + + @Schema(description = "친구한테 친구 요청 유무") + Boolean isFriendInviteToFriend, + + @Schema(description = "친구로부터 친구 요청 유무") + Boolean isFriendInviteToMe ) { } \ No newline at end of file diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepository.java index d9d6b63cf..928c3a0ee 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepository.java @@ -20,7 +20,7 @@ Slice findGroupSummaries( List getTotalGroupMemberCount(final List groupIds); - Optional findGroupDetailByGroupIdAndMemberId(final Long groupId, + Optional findGroupDetailByGroupIdAndMemberIdExcludeMemberId(final Long groupId, final Long memberId); Optional getTotalGroupMemberCount(Long groupId); diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryImpl.java index 18ac63a38..c477f6ee0 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryImpl.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryImpl.java @@ -12,7 +12,6 @@ import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; @@ -85,54 +84,48 @@ public List getTotalGroupMemberCount(final List groupIds) { .fetch(); } - /** - * 사용자를 제외한 그룹원 정보와 그룹의 상세정보를 반환한다. - * - * @param groupId 상세정보를 찾을 그룹 아이디 - * @param memberId 사용자 아이디 - * @return 그룹의 상세정보({@code memberId} 제외 그룹원) - */ @Override - public Optional findGroupDetailByGroupIdAndMemberId(final Long groupId, + public Optional findGroupDetailByGroupIdAndMemberIdExcludeMemberId( + final Long groupId, final Long memberId) { - GroupDetailDto groupDetailDtoIncludeMe = - jpaQueryFactory - .selectFrom(group) - .join(memberGroup).on(memberGroup.group.eq(group), - memberGroup.deletedAt.isNull()) - .join(member).on(member.eq(memberGroup.member), - member.deletedAt.isNull()) - .where(group.id.eq(groupId)) - .transform( - groupBy(group.id).as( - Projections.constructor( - GroupDetailDto.class, - group.groupName, - group.groupDescription, - group.groupProfileUrl, - group.createdAt, - Expressions.asBoolean(Boolean.FALSE), - list( - Projections.constructor( - GroupMemberDto.class, - member.id, - member.profileUrl, - member.nickname, - member.tag, - memberGroup.isOwner - ) + GroupDetailDto groupDetailDtoIncludeMe = jpaQueryFactory + .selectFrom(group) + .join(memberGroup).on(memberGroup.group.eq(group), + memberGroup.deletedAt.isNull()) + .join(member).on(member.eq(memberGroup.member), + member.deletedAt.isNull()) + .where(group.id.eq(groupId)) + .transform( + groupBy(group.id).as( + Projections.constructor( + GroupDetailDto.class, + group.groupName, + group.groupDescription, + group.groupProfileUrl, + group.createdAt, + Expressions.asBoolean(Boolean.FALSE), + list( + Projections.constructor( + GroupMemberDto.class, + member.id, + member.profileUrl, + member.nickname, + member.tag, + memberGroup.isOwner ) ) ) ) - .get(groupId); + ) + .get(groupId); - if (Objects.isNull(groupDetailDtoIncludeMe)) { + if (groupDetailDtoIncludeMe == null) { return Optional.empty(); } boolean isOwner = false; - List groupMemberDtosExcludeMe = new ArrayList<>(); + List groupMemberDtosExcludeMe = new ArrayList<>( + groupDetailDtoIncludeMe.members().size()); for (GroupMemberDto dto : groupDetailDtoIncludeMe.members()) { if (!dto.memberId().equals(memberId)) { groupMemberDtosExcludeMe.add(dto); diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/query/GroupQueryService.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/query/GroupQueryService.java index 946473dcc..179b4a65f 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/query/GroupQueryService.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/query/GroupQueryService.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import site.timecapsulearchive.core.domain.capsule.group_capsule.repository.GroupCapsuleQueryRepository; +import site.timecapsulearchive.core.domain.friend.data.dto.FriendRelations; import site.timecapsulearchive.core.domain.friend.repository.member_friend.MemberFriendRepository; import site.timecapsulearchive.core.domain.group.data.dto.CompleteGroupSummaryDto; import site.timecapsulearchive.core.domain.group.data.dto.GroupDetailDto; @@ -62,7 +63,7 @@ public Slice findGroupsSlice( } public GroupDetailTotalDto findGroupDetailByGroupId(final Long memberId, final Long groupId) { - final GroupDetailDto groupDetailDto = groupRepository.findGroupDetailByGroupIdAndMemberId( + final GroupDetailDto groupDetailDto = groupRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( groupId, memberId).orElseThrow(GroupNotFoundException::new); final Long groupCapsuleCount = groupCapsuleQueryRepository.findGroupCapsuleCount(groupId); @@ -70,9 +71,13 @@ public GroupDetailTotalDto findGroupDetailByGroupId(final Long memberId, final L final List groupMemberIds = groupDetailDto.members().stream() .map(GroupMemberDto::memberId) .toList(); - final List friendIds = memberFriendRepository.findFriendIds(groupMemberIds, memberId); - return GroupDetailTotalDto.as(groupDetailDto, groupCapsuleCount, friendIds); + FriendRelations friendRelations = null; + if (!groupMemberIds.isEmpty()) { + friendRelations = memberFriendRepository.findFriendRelations(groupMemberIds, memberId); + } + + return GroupDetailTotalDto.as(groupDetailDto, groupCapsuleCount, friendRelations); } public List findAllOwnerGroupsByMemberId(Long memberId) { diff --git a/backend/core/src/test/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryTest.java b/backend/core/src/test/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryTest.java index 261ce6df8..070003c13 100644 --- a/backend/core/src/test/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryTest.java +++ b/backend/core/src/test/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryTest.java @@ -155,7 +155,7 @@ void setup(@Autowired EntityManager entityManager) { void 그룹_아이디와_멤버_아이디로_그룹을_조회하면_그룹_상세가_반환된다() { //given //when - GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( + GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( groupId, ownerId).orElseThrow(); //then @@ -166,7 +166,7 @@ void setup(@Autowired EntityManager entityManager) { void 그룹_아이디와_멤버_아이디로_그룹을_조회하면_그룹_정보를_볼_수_있다() { //given //when - GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( + GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( groupId, ownerId).orElseThrow(); //then @@ -183,7 +183,7 @@ void setup(@Autowired EntityManager entityManager) { void 그룹_아이디와_멤버_아이디_그룹을_조회하면_그룹원들의_정보를_볼_수_있다() { //given //when - GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( + GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( groupId, groupMemberId).orElseThrow(); //then @@ -203,7 +203,7 @@ void setup(@Autowired EntityManager entityManager) { void 그룹_아이디와_멤버_아이디로_그룹을_조회하면_본인은_포함되어_조회되지_않는다() { //given //when - GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( + GroupDetailDto groupDetail = groupQueryRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( groupId, groupMemberId).orElseThrow(); List groupMemberDtos = groupDetail.members(); @@ -219,7 +219,7 @@ void setup(@Autowired EntityManager entityManager) { void 그룹_아이디로_그룹을_상세정보를_조회하면_사용자를_제외한_그룹원의_정보를_볼_수_있다() { //given //when - GroupDetailDto groupDetailDto = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( + GroupDetailDto groupDetailDto = groupQueryRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( groupId, ownerId) .orElseThrow(); @@ -233,30 +233,18 @@ void setup(@Autowired EntityManager entityManager) { Long notExistGroupId = 999L; //when - Optional groupDetailDto = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( + Optional groupDetailDto = groupQueryRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( notExistGroupId, ownerId); //then assertThat(groupDetailDto).isEmpty(); } - @Test - void 그룹장이_그룹을_상세정보를_조회하면_그룹의_수정권한을_가진다() { - //given - //when - GroupDetailDto groupDetailDto = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( - groupId, ownerId) - .orElseThrow(); - - //then - assertThat(groupDetailDto.isOwner()).isTrue(); - } - @Test void 그룹원이_그룹을_상세정보를_조회하면_그룹의_수정권한이_없다() { //given //when - GroupDetailDto groupDetailDto = groupQueryRepository.findGroupDetailByGroupIdAndMemberId( + GroupDetailDto groupDetailDto = groupQueryRepository.findGroupDetailByGroupIdAndMemberIdExcludeMemberId( groupId, groupMemberId) .orElseThrow();