diff --git a/src/main/java/shop/kkeujeok/kkeujeokbackend/dashboard/domain/repository/DashboardCustomRepositoryImpl.java b/src/main/java/shop/kkeujeok/kkeujeokbackend/dashboard/domain/repository/DashboardCustomRepositoryImpl.java index 0037bb82..64c6e549 100644 --- a/src/main/java/shop/kkeujeok/kkeujeokbackend/dashboard/domain/repository/DashboardCustomRepositoryImpl.java +++ b/src/main/java/shop/kkeujeok/kkeujeokbackend/dashboard/domain/repository/DashboardCustomRepositoryImpl.java @@ -6,6 +6,7 @@ import static shop.kkeujeok.kkeujeokbackend.dashboard.team.domain.QTeamDashboardMemberMapping.teamDashboardMemberMapping; import static shop.kkeujeok.kkeujeokbackend.member.domain.QMember.member; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; import java.util.Set; @@ -76,7 +77,16 @@ public Set findCategoriesForDashboard(Member member) { public Page findForTeamDashboard(Member member, Pageable pageable) { long total = queryFactory .selectFrom(teamDashboard) - .where(teamDashboard._super.member.eq(member)) + .where( + teamDashboard._super.member.eq(member) + .or( + teamDashboard.id.in( + JPAExpressions.select(teamDashboardMemberMapping.teamDashboard.id) + .from(teamDashboardMemberMapping) + .where(teamDashboardMemberMapping.member.id.eq(member.getId())) + ) + ) + ) .stream() .count(); diff --git a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowController.java b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowController.java index 237af6a9..9ed84c0b 100644 --- a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowController.java +++ b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowController.java @@ -13,9 +13,7 @@ import org.springframework.web.bind.annotation.RestController; import shop.kkeujeok.kkeujeokbackend.global.annotation.CurrentUserEmail; import shop.kkeujeok.kkeujeokbackend.global.template.RspTemplate; -import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.request.FollowAcceptReqDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.request.FollowReqDto; -import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowAcceptResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowInfoListDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.MemberInfoForFollowListDto; @@ -39,8 +37,9 @@ public RspTemplate save(@CurrentUserEmail String email, } @PostMapping("/accept/{followId}") - public RspTemplate accept(@PathVariable Long followId) { - followService.accept(followId); + public RspTemplate accept(@CurrentUserEmail String email, + @PathVariable Long followId) { + followService.accept(email, followId); return new RspTemplate<>(HttpStatus.OK, "친구 추가 수락"); @@ -88,4 +87,4 @@ public RspTemplate findMyFollowsCount(@CurrentUserEmail String "내 팔로우 수 조회", followService.findMyFollowsCount(email)); } -} \ No newline at end of file +} diff --git a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/dto/response/FollowInfoResDto.java b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/dto/response/FollowInfoResDto.java index cc2abe62..7c2589cd 100644 --- a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/dto/response/FollowInfoResDto.java +++ b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/dto/response/FollowInfoResDto.java @@ -3,6 +3,7 @@ import lombok.Builder; import shop.kkeujeok.kkeujeokbackend.member.domain.Member; import shop.kkeujeok.kkeujeokbackend.member.follow.domain.Follow; +import shop.kkeujeok.kkeujeokbackend.member.follow.domain.FollowStatus; @Builder public record FollowInfoResDto( @@ -16,13 +17,14 @@ public static FollowInfoResDto of(Follow follow, Long myMemberId) { Member friend = follow.getToMember().getId().equals(myMemberId) ? follow.getFromMember() : follow.getToMember(); + boolean isFollow = follow.getFollowStatus().equals(FollowStatus.ACCEPT); return FollowInfoResDto.builder() .memberId(friend.getId()) .nickname(friend.getNickname()) .name(friend.getName()) .profileImage(friend.getPicture()) - .isFollow(true) + .isFollow(isFollow) .build(); } } diff --git a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/application/FollowService.java b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/application/FollowService.java index 37c0cb00..d45c44c3 100644 --- a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/application/FollowService.java +++ b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/application/FollowService.java @@ -20,6 +20,7 @@ import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.RecommendedFollowInfoResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.domain.Follow; import shop.kkeujeok.kkeujeokbackend.member.follow.domain.repository.FollowRepository; +import shop.kkeujeok.kkeujeokbackend.member.follow.exception.AlreadyFriendsException; import shop.kkeujeok.kkeujeokbackend.member.follow.exception.FollowAlreadyExistsException; import shop.kkeujeok.kkeujeokbackend.member.follow.exception.FollowNotFoundException; import shop.kkeujeok.kkeujeokbackend.notification.application.NotificationService; @@ -28,6 +29,8 @@ @RequiredArgsConstructor @Transactional(readOnly = true) public class FollowService { + private static final String FOLLOW_REQUEST_MESSAGE_TEMPLATE = "친구 추가 요청: %s님이 친구 추가 요청을 보냈습니다.followerId%d"; + private static final String FOLLOW_ACCEPT_MESSAGE_TEMPLATE = "친구 추가 수락: %s님이 %s님의 친구 추가를 수락하였습니다."; private final MemberRepository memberRepository; private final FollowRepository followRepository; @@ -43,8 +46,9 @@ public FollowResDto save(String email, FollowReqDto followReqDto) { Follow follow = followReqDto.toEntity(fromMember, toMember); followRepository.save(follow); - notificationService.sendNotification(toMember, - fromMember.getNickname() + "님이 친구 신청을 보냈습니다.followerId" + follow.getId()); + String followRequestMessage = String.format(FOLLOW_REQUEST_MESSAGE_TEMPLATE, fromMember.getNickname(), + follow.getId()); + notificationService.sendNotification(toMember, followRequestMessage); return FollowResDto.from(toMember); } @@ -56,11 +60,24 @@ private void validateFollowDoesNotExist(Member fromMember, Member toMember) { } @Transactional - public void accept(Long followId) { + public void accept(String email, Long followId) { + validateFollowStatusIsAccept(followId); + followRepository.acceptFollowingRequest(followId); - Member fromMember = followRepository.findById(followId).orElseThrow(FollowNotFoundException::new) + + Member toMember = memberRepository.findByEmail(email).orElseThrow(MemberNotFoundException::new); + Member fromMember = followRepository.findById(followId).orElseThrow(AlreadyFriendsException::new) .getFromMember(); - notificationService.sendNotification(fromMember, "followId" + followId); + + String followAcceptMessage = String.format(FOLLOW_ACCEPT_MESSAGE_TEMPLATE, fromMember.getNickname(), + toMember.getNickname()); + notificationService.sendNotification(fromMember, followAcceptMessage); + } + + private void validateFollowStatusIsAccept(Long followId) { + if (followRepository.existsAlreadyFollow(followId)) { + throw new AlreadyFriendsException(); + } } public FollowInfoListDto findFollowList(String email, Pageable pageable) { diff --git a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepository.java b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepository.java index cd5077d2..446a2678 100644 --- a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepository.java +++ b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepository.java @@ -25,4 +25,6 @@ public interface FollowCustomRepository { Page searchFollowListUsingKeywords(Long memberId, String keyword, Pageable pageable); MyFollowsResDto findMyFollowsCount(Long memberId); + + boolean existsAlreadyFollow(Long followId); } diff --git a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepositoryImpl.java b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepositoryImpl.java index 5bc59663..a040b120 100644 --- a/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepositoryImpl.java +++ b/src/main/java/shop/kkeujeok/kkeujeokbackend/member/follow/domain/repository/FollowCustomRepositoryImpl.java @@ -134,11 +134,9 @@ public Page findRecommendedFollowList(Long memberId potentialFriends = potentialFriends.stream().distinct().collect(Collectors.toList()); - // 친구 관계 여부 확인을 위한 로직 추가 List recommendedFollows = potentialFriends.stream() .filter(teamMember -> !teamMember.getId().equals(memberId)) // 본인 제외 - .map(teamMember -> { - // 현재 추천 대상 사용자가 팔로우 관계인지 확인 + .filter(teamMember -> { boolean isFollow = queryFactory .selectOne() .from(follow) @@ -148,9 +146,9 @@ public Page findRecommendedFollowList(Long memberId .and(follow.toMember.id.eq(memberId))) ) .fetchFirst() != null; - - return RecommendedFollowInfoResDto.from(teamMember, isFollow); + return !isFollow; }) + .map(teamMember -> RecommendedFollowInfoResDto.from(teamMember, false)) .collect(Collectors.toList()); int start = (int) pageable.getOffset(); @@ -184,7 +182,7 @@ public Page searchFollowListUsingKeywords(Long member member.nickname, member.name, member.picture, - follow.id.isNotNull() + follow.followStatus.eq(FollowStatus.ACCEPT) )) .from(member) .leftJoin(follow) @@ -233,4 +231,14 @@ public MyFollowsResDto findMyFollowsCount(Long memberId) { return MyFollowsResDto.from(followCount); } + + @Override + public boolean existsAlreadyFollow(Long followId) { + return queryFactory + .selectOne() + .from(follow) + .where(follow.id.eq(followId) + .and(follow.followStatus.eq(FollowStatus.ACCEPT))) + .fetchFirst() != null; + } } diff --git a/src/test/java/shop/kkeujeok/kkeujeokbackend/block/application/BlockServiceTest.java b/src/test/java/shop/kkeujeok/kkeujeokbackend/block/application/BlockServiceTest.java index 423b3a6f..31c60bdd 100644 --- a/src/test/java/shop/kkeujeok/kkeujeokbackend/block/application/BlockServiceTest.java +++ b/src/test/java/shop/kkeujeok/kkeujeokbackend/block/application/BlockServiceTest.java @@ -1,229 +1,3 @@ -인호 -inhooo00 -온라인 -김신아 — 2024. 10. 9. 오후 11:25 -쿨쿨띠얘 -쿨쿨 -쿨쿨띠예 -드르렁 드르렁 드르렁 대~~ -명지우 — 2024. 10. 9. 오후 11:57 -알고보니 혼잣말이었던거임 -~ -김신아 — 2024. 10. 10. 오전 12:03 -챌린지 매니절~ -쉿쉿 -자잔~ -흑흐ㄱ -흐흫ㄱㄱ흑ㄱ흐흑.. 흑긓흐ㅡㄱ흐흑 흐ㅡ흑 -일단 알겟음 개발 끝나면 console 지욹레 -지울게 -김신아 — 2024. 10. 10. 오전 12:18 -애들아 나빨래 널고싶은데 -나 나가도 돼..? -나 사실 빨래 2시간동안 묵혀두고잇어 -................................................... -허락해주세요 주인님 흑흑 -김동균 — 2024. 10. 10. 오전 12:18 -가세요 -김신아 — 2024. 10. 10. 오전 12:18 -빨래 널러 갈 수 있게 ㅎ락해주세요 -아싸라비용 -최기웅 — 2024. 10. 10. 오전 12:20 -ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ -명지우 — 2024. 10. 10. 오전 12:21 -빠이빠이 -김신아 — 오늘 오후 10:00 -ㅋㅋ ㅎㅇ -ㅇㅇ!! -예스~~ -오우 -헐 머임 -명지우 — 오늘 오후 10:02 -근데 언니 어디야?? -김신아 — 오늘 오후 10:03 -나 지금 -지하철 ㅜ -나 -친구 생일파티라 -합창연습 -하느라 조금 늦었네… -ㅇㅇ 내가 소프라노임 -☺️ -오늘 영상찍음 ㅋ -ㅇㅋ -뭐임 -동균이 ㅋㅋㅋㅋㅋ -이거 -누가짠겨? -전에! -? -김동균 — 오늘 오후 10:07 -ㄱㅇ -김신아 — 오늘 오후 10:07 -ㅇㅎ -얼른 배포해보자긔 -동의합니다 -야호~ -재채기누구임 -오 수고수고 -나도 다함 -근데 -그 sse가 됐다 말앗다해사 -동균이가 http버전2 -사용하면 -탭6개로 제한된대 -ㅋㅋ -ㅇㅇ -흐엉웅 -흐엉엉 -애들아 -내가 방금 코드 잘못고친거 생각남 -지금 아예 알림 인뜰더임 ㅋㅋ -ㅈㅅ -집가서 고침 -김동균 — 오늘 오후 10:13 -ㅇㅋ ㅋㅋ -인호 — 오늘 오후 10:13 -개웃기네 ㅋㅋ -김신아 — 오늘 오후 10:14 -응응 -어디간격?? -나 간격 줄엿는데 별로여서 늘린건데..하핫 -다시 늘맇게용 -꺅 -ㅇㅇ -내일 발표인데 -ㅅㅂ -아무것도 못랫ㅅ아 -흑흣흑흫흑흐흑 -🥹 -인호 — 오늘 오후 10:16 -이미지 -김신아 — 오늘 오후 10:18 -야호~~ -ㅋㅋㅋㅋㄱㅋㅋㄱㅋㄱㄱㅋㅋㄱㅋㄱㅋㄱㅋ -큭큭 -학겨가 엏른 돈줫으면 -배포댐 ! -?? -ㅇㅋ.. -나 15분뒤에내림 -헤헤 -아냐 -그 뒤에 오류나는 거 -내가 맡은거에서 -오루나면 -내가 달려가기로 했어 -야호~~ -ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ -👍 -응애 -김동균 — 오늘 오후 10:29 -package shop.kkeujeok.kkeujeokbackend.block.application; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.List; -import java.util.Optional; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.core.ValueOperations; -import shop.kkeujeok.kkeujeokbackend.block.api.dto.request.BlockSaveReqDto; -import shop.kkeujeok.kkeujeokbackend.block.api.dto.request.BlockSequenceUpdateReqDto; -import shop.kkeujeok.kkeujeokbackend.block.api.dto.request.BlockUpdateReqDto; -import shop.kkeujeok.kkeujeokbackend.block.api.dto.response.BlockInfoResDto; -import shop.kkeujeok.kkeujeokbackend.block.domain.Block; -import shop.kkeujeok.kkeujeokbackend.block.domain.Progress; -import shop.kkeujeok.kkeujeokbackend.block.domain.repository.BlockRepository; -import shop.kkeujeok.kkeujeokbackend.block.exception.InvalidProgressException; -import shop.kkeujeok.kkeujeokbackend.dashboard.domain.Dashboard; -import shop.kkeujeok.kkeujeokbackend.dashboard.domain.repository.DashboardRepository; -import shop.kkeujeok.kkeujeokbackend.dashboard.personal.domain.PersonalDashboard; -import shop.kkeujeok.kkeujeokbackend.global.entity.Status; -import shop.kkeujeok.kkeujeokbackend.member.domain.Member; -import shop.kkeujeok.kkeujeokbackend.member.domain.SocialType; -import shop.kkeujeok.kkeujeokbackend.member.domain.repository.MemberRepository; - -@ExtendWith(MockitoExtension.class) -class BlockServiceTest { - - @Mock - private BlockRepository blockRepository; - - @Mock - private MemberRepository memberRepository; - - @Mock - private DashboardRepository dashboardRepository; - - @Mock - private RedisTemplate redisTemplate; - - @InjectMocks - private BlockService blockService; - - private Member member; - private Block block; - private Block deleteBlock; - private Dashboard dashboard; - private BlockSaveReqDto blockSaveReqDto; - private BlockUpdateReqDto blockUpdateReqDto; - private BlockSequenceUpdateReqDto blockSequenceUpdateReqDto; - - @BeforeEach - void setUp() { - member = Member.builder() - .email("email") - .name("name") - .nickname("nickname") - .socialType(SocialType.GOOGLE) - .introduction("introduction") - .picture("picture") - .build(); - - when(memberRepository.findByEmail(anyString())).thenReturn(Optional.ofNullable(member)); - - dashboard = PersonalDashboard.builder() - .member(member) - .title("title") - .description("description") - .isPublic(false) - .category("category") - .build(); - - blockSaveReqDto = new BlockSaveReqDto(1L, "Title", "Contents", Progress.NOT_STARTED, "2024.07.03 13:23", - "2024.07.25 13:23"); - blockUpdateReqDto = new BlockUpdateReqDto("UpdateTitle", "UpdateContents", "2024.07.03 13:23", - "2024.07.28 16:40"); - - blockSequenceUpdateReqDto = new BlockSequenceUpdateReqDto( - dashboard.getId(), - List.of(1L, 2L), - List.of(3L, 4L), - List.of(5L, 6L) - ); - -... (214줄 남음) -접기 -message.txt -12KB - package shop.kkeujeok.kkeujeokbackend.block.application; import static org.assertj.core.api.Assertions.assertThat; @@ -538,5 +312,3 @@ void setUp() { } } -message.txt -12KB \ No newline at end of file diff --git a/src/test/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowControllerTest.java b/src/test/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowControllerTest.java index d4317e33..8f84eeae 100644 --- a/src/test/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowControllerTest.java +++ b/src/test/java/shop/kkeujeok/kkeujeokbackend/member/follow/api/FollowControllerTest.java @@ -25,6 +25,7 @@ import static shop.kkeujeok.kkeujeokbackend.global.restdocs.RestDocsHandler.requestFields; import static shop.kkeujeok.kkeujeokbackend.global.restdocs.RestDocsHandler.responseFields; +import java.util.Collections; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -39,17 +40,15 @@ import shop.kkeujeok.kkeujeokbackend.global.dto.PageInfoResDto; import shop.kkeujeok.kkeujeokbackend.global.error.ControllerAdvice; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.request.FollowReqDto; -import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowInfoListDto; +import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowInfoResDto; +import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.MemberInfoForFollowListDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.MemberInfoForFollowResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.MyFollowsResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.RecommendedFollowInfoListDto; -import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.FollowInfoResDto; import shop.kkeujeok.kkeujeokbackend.member.follow.api.dto.response.RecommendedFollowInfoResDto; -import java.util.Collections; - class FollowControllerTest extends ControllerTest { @InjectMocks @@ -103,7 +102,7 @@ void setUp(RestDocumentationContextProvider restDocumentation) { @DisplayName("POST 친구 요청 수락") @Test void 친구_요청_수락() throws Exception { - doNothing().when(followService).accept(anyLong()); + doNothing().when(followService).accept(anyString(), anyLong()); mockMvc.perform(post("/api/member/follow/accept/{followId}", 1L) .header("Authorization", "Bearer valid-token"))