diff --git a/clokey-api/src/main/java/org/clokey/domain/member/dto/response/FollowMemberResponse.java b/clokey-api/src/main/java/org/clokey/domain/member/dto/response/FollowMemberResponse.java index 4f780480..469ce350 100644 --- a/clokey-api/src/main/java/org/clokey/domain/member/dto/response/FollowMemberResponse.java +++ b/clokey-api/src/main/java/org/clokey/domain/member/dto/response/FollowMemberResponse.java @@ -10,5 +10,6 @@ public record FollowMemberResponse( description = "팔로잉 or 팔로워의 PofileImageUrl", example = "https://example.com/profile/john.jpg") String profileImageUrl, + @Schema(description = "해당 멤버가 공개 계정인지?", example = "true") boolean isPublic, @Schema(description = "요청자가 이 멤버를 팔로우하고 있는가?", example = "true") boolean isFollowingMember, @Schema(description = "해당 멤버가 본인(요청자)인가?", example = "false") boolean isMe) {} diff --git a/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepositoryImpl.java b/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepositoryImpl.java index 080d1558..162ecaa7 100644 --- a/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepositoryImpl.java +++ b/clokey-api/src/main/java/org/clokey/domain/member/repository/FollowRepositoryImpl.java @@ -3,16 +3,19 @@ import static org.clokey.member.entity.QFollow.follow; import static org.clokey.member.entity.QMember.member; -import com.querydsl.core.types.Projections; +import com.querydsl.core.Tuple; +import com.querydsl.core.types.Expression; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import org.clokey.domain.member.dto.response.FollowMemberResponse; import org.clokey.member.entity.QBlock; import org.clokey.member.entity.QFollow; +import org.clokey.member.enums.Visibility; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; @@ -28,23 +31,25 @@ public class FollowRepositoryImpl implements FollowRepositoryCustom { public Slice findAllFollowingsByMemberId( Long currentId, Long targetId, Long lastFollowId, Integer size) { QFollow followSub = new QFollow("followSub"); + BooleanExpression isFollowingExpression = + JPAExpressions.selectOne() + .from(followSub) + .where( + followSub.followFrom.id.eq(currentId), + followSub.followTo.id.eq(member.id)) + .exists(); + BooleanExpression isMeExpression = member.id.eq(currentId); - List results = + List tuples = queryFactory .select( - Projections.constructor( - FollowMemberResponse.class, - follow.id, - member.id, - member.nickname, - member.profileImageUrl, - JPAExpressions.selectOne() - .from(followSub) - .where( - followSub.followFrom.id.eq(currentId), - followSub.followTo.id.eq(member.id)) - .exists(), - member.id.eq(currentId))) + follow.id, + member.id, + member.nickname, + member.profileImageUrl, + member.visibility, + isFollowingExpression, + isMeExpression) .from(follow) .join(follow.followTo, member) .where( @@ -55,6 +60,9 @@ public Slice findAllFollowingsByMemberId( .orderBy(follow.id.desc()) .fetch(); + List results = + mapToFollowMemberResponses(tuples, isFollowingExpression, isMeExpression); + return checkLastPage(size, results); } @@ -62,23 +70,25 @@ public Slice findAllFollowingsByMemberId( public Slice findAllFollowersByMemberId( Long currentId, Long targetId, Long lastFollowId, Integer size) { QFollow followSub = new QFollow("followSub"); + BooleanExpression isFollowingExpression = + JPAExpressions.selectOne() + .from(followSub) + .where( + followSub.followFrom.id.eq(currentId), + followSub.followTo.id.eq(member.id)) + .exists(); + BooleanExpression isMeExpression = member.id.eq(currentId); - List results = + List tuples = queryFactory .select( - Projections.constructor( - FollowMemberResponse.class, - follow.id, - member.id, - member.nickname, - member.profileImageUrl, - JPAExpressions.selectOne() - .from(followSub) - .where( - followSub.followFrom.id.eq(currentId), - followSub.followTo.id.eq(member.id)) - .exists(), - member.id.eq(currentId))) + follow.id, + member.id, + member.nickname, + member.profileImageUrl, + member.visibility, + isFollowingExpression, + isMeExpression) .from(follow) .join(follow.followFrom, member) .where( @@ -89,9 +99,33 @@ public Slice findAllFollowersByMemberId( .orderBy(follow.id.desc()) .fetch(); + List results = + mapToFollowMemberResponses(tuples, isFollowingExpression, isMeExpression); + return checkLastPage(size, results); } + private List mapToFollowMemberResponses( + List tuples, + Expression isFollowingExpression, + Expression isMeExpression) { + List responses = new ArrayList<>(tuples.size()); + + for (Tuple tuple : tuples) { + responses.add( + new FollowMemberResponse( + tuple.get(follow.id), + tuple.get(member.id), + tuple.get(member.nickname), + tuple.get(member.profileImageUrl), + Visibility.PUBLIC.equals(tuple.get(member.visibility)), + Boolean.TRUE.equals(tuple.get(isFollowingExpression)), + Boolean.TRUE.equals(tuple.get(isMeExpression)))); + } + + return responses; + } + private BooleanExpression blockFilter(Long currentId) { QBlock blockCheck = new QBlock("blockCheck"); diff --git a/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java b/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java index bd27d2ca..55a5760b 100644 --- a/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java +++ b/clokey-api/src/test/java/org/clokey/domain/member/controller/MemberControllerTest.java @@ -544,6 +544,7 @@ class 팔로잉_팔로워_목록_조회_요청_시 { 2L, "nickname1", "https://img.example.com/bg.jpg", + true, false, false), new FollowMemberResponse( @@ -551,6 +552,7 @@ class 팔로잉_팔로워_목록_조회_요청_시 { 1L, "nickname2", "https://img.example2.com/bg.jpg", + false, true, false)); @@ -569,7 +571,9 @@ class 팔로잉_팔로워_목록_조회_요청_시 { .andExpect(jsonPath("$.code").value("COMMON200")) .andExpect(jsonPath("$.message").value("성공입니다.")) .andExpect(jsonPath("$.result.content[0].followId").value(2L)) + .andExpect(jsonPath("$.result.content[0].isPublic").value(true)) .andExpect(jsonPath("$.result.content[1].followId").value(1L)) + .andExpect(jsonPath("$.result.content[1].isPublic").value(false)) .andExpect(jsonPath("$.result.isLast").value(true)); } @@ -584,6 +588,7 @@ class 팔로잉_팔로워_목록_조회_요청_시 { "nickname2", "https://img.example.com/bg.jpg", false, + false, false), new FollowMemberResponse( 1L, @@ -591,6 +596,7 @@ class 팔로잉_팔로워_목록_조회_요청_시 { "nickname1", "https://img.example2.com/bg.jpg", true, + true, true)); given(memberService.getFollows(1L, null, false, 5)) @@ -608,7 +614,9 @@ class 팔로잉_팔로워_목록_조회_요청_시 { .andExpect(jsonPath("$.code").value("COMMON200")) .andExpect(jsonPath("$.message").value("성공입니다.")) .andExpect(jsonPath("$.result.content[0].followId").value(2L)) + .andExpect(jsonPath("$.result.content[0].isPublic").value(false)) .andExpect(jsonPath("$.result.content[1].followId").value(1L)) + .andExpect(jsonPath("$.result.content[1].isPublic").value(true)) .andExpect(jsonPath("$.result.isLast").value(true)); } @@ -623,6 +631,7 @@ class 팔로잉_팔로워_목록_조회_요청_시 { "nickname1", "https://img.example.com/bg.jpg", true, + true, false)); given(memberService.getFollows(1L, null, true, 1)) @@ -641,6 +650,7 @@ class 팔로잉_팔로워_목록_조회_요청_시 { .andExpect(jsonPath("$.code").value("COMMON200")) .andExpect(jsonPath("$.message").value("성공입니다.")) .andExpect(jsonPath("$.result.content[0].followId").value(2L)) + .andExpect(jsonPath("$.result.content[0].isPublic").value(true)) .andExpect(jsonPath("$.result.isLast").value(false)); }