diff --git a/src/main/java/com/example/egobook_be/domain/friend/controller/FriendController.java b/src/main/java/com/example/egobook_be/domain/friend/controller/FriendController.java index 219e029..8230d8b 100644 --- a/src/main/java/com/example/egobook_be/domain/friend/controller/FriendController.java +++ b/src/main/java/com/example/egobook_be/domain/friend/controller/FriendController.java @@ -1,9 +1,6 @@ package com.example.egobook_be.domain.friend.controller; -import com.example.egobook_be.domain.friend.dto.FriendRequestCreateReqDto; -import com.example.egobook_be.domain.friend.dto.FriendRequestListResDto; -import com.example.egobook_be.domain.friend.dto.FriendResDto; -import com.example.egobook_be.domain.friend.dto.FriendSearchResDto; +import com.example.egobook_be.domain.friend.dto.*; import com.example.egobook_be.domain.friend.service.FriendService; import com.example.egobook_be.global.response.GlobalResponse; import io.swagger.v3.oas.annotations.Operation; @@ -52,6 +49,7 @@ public ResponseEntity> requestFriend( 받은 친구 신청을 수락합니다. - 수락 시 양방향 친구 관계가 생성됩니다. + - 친구는 최대 10명까지만 추가할 수 있으며, 초과 시 수락이 실패합니다. """ ) @PostMapping("/requests/{requestId}/accept") @@ -129,7 +127,7 @@ public ResponseEntity> deleteFriend( """ ) @GetMapping("/requests/incoming") - public ResponseEntity>> getIncomingRequests( + public ResponseEntity> getIncomingRequests( @AuthenticationPrincipal(expression = "userAuthDto.userId") Long userId ) { return ResponseEntity.ok( @@ -168,7 +166,7 @@ public ResponseEntity>> getOutgoing """ ) @GetMapping - public ResponseEntity>> getFriends( + public ResponseEntity> getFriends( @AuthenticationPrincipal(expression = "userAuthDto.userId") @Parameter(hidden = true) Long userId ) { diff --git a/src/main/java/com/example/egobook_be/domain/friend/dto/FriendListResDto.java b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendListResDto.java new file mode 100644 index 0000000..2bb2e75 --- /dev/null +++ b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendListResDto.java @@ -0,0 +1,12 @@ +package com.example.egobook_be.domain.friend.dto; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record FriendListResDto( + int count, + List friends +) { +} diff --git a/src/main/java/com/example/egobook_be/domain/friend/dto/FriendRequestListResDto.java b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendRequestListResDto.java index 93a02a8..609f91f 100644 --- a/src/main/java/com/example/egobook_be/domain/friend/dto/FriendRequestListResDto.java +++ b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendRequestListResDto.java @@ -9,6 +9,7 @@ public record FriendRequestListResDto( Long requestId, Long userId, String nickname, + Integer level, LocalDateTime requestedAt ) { } diff --git a/src/main/java/com/example/egobook_be/domain/friend/dto/FriendRequestListWithCountResDto.java b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendRequestListWithCountResDto.java new file mode 100644 index 0000000..6c5c215 --- /dev/null +++ b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendRequestListWithCountResDto.java @@ -0,0 +1,12 @@ +package com.example.egobook_be.domain.friend.dto; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record FriendRequestListWithCountResDto( + int totalCount, + List requests +) { +} diff --git a/src/main/java/com/example/egobook_be/domain/friend/dto/FriendResDto.java b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendResDto.java index 7cbff6b..aca0096 100644 --- a/src/main/java/com/example/egobook_be/domain/friend/dto/FriendResDto.java +++ b/src/main/java/com/example/egobook_be/domain/friend/dto/FriendResDto.java @@ -5,6 +5,7 @@ @Builder public record FriendResDto( Long friendId, - String nickname + String nickname, + Integer level ) { } diff --git a/src/main/java/com/example/egobook_be/domain/friend/exception/FriendErrorCode.java b/src/main/java/com/example/egobook_be/domain/friend/exception/FriendErrorCode.java index cacde59..4ae6bef 100644 --- a/src/main/java/com/example/egobook_be/domain/friend/exception/FriendErrorCode.java +++ b/src/main/java/com/example/egobook_be/domain/friend/exception/FriendErrorCode.java @@ -20,6 +20,7 @@ public enum FriendErrorCode implements BaseErrorCode { */ ALREADY_FRIEND(HttpStatus.CONFLICT, "이미 친구 관계입니다."), ALREADY_REQUESTED(HttpStatus.CONFLICT, "이미 친구 신청을 보낸 상태입니다."), + FRIEND_LIMIT_EXCEEDED(HttpStatus.CONFLICT, "친구는 최대 10명까지만 추가할 수 있습니다."), /** * 400 BAD_REQUEST diff --git a/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRepository.java b/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRepository.java index 0631010..43d540e 100644 --- a/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRepository.java +++ b/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRepository.java @@ -17,10 +17,22 @@ public interface FriendRepository extends JpaRepository { List findByUser(User user); + long countByUser(User user); + @Query(""" select f.friend.id from Friend f where f.user = :user """) List findFriendIdsByUser(@Param("user") User user); + + @Query(""" + select f + from Friend f + join fetch f.friend u + where f.user = :user + """) + List findByUserWithFriend( + @Param("user") User user + ); } diff --git a/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRequestRepository.java b/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRequestRepository.java index f0dfeaa..3f68f18 100644 --- a/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRequestRepository.java +++ b/src/main/java/com/example/egobook_be/domain/friend/repository/FriendRequestRepository.java @@ -4,6 +4,8 @@ import com.example.egobook_be.domain.friend.enums.FriendRequestStatus; import com.example.egobook_be.domain.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; @@ -25,4 +27,21 @@ boolean existsBySenderAndReceiverAndStatus( List findByReceiverAndStatus(User receiver, FriendRequestStatus status); List findBySenderAndStatus(User sender, FriendRequestStatus status); + + long countByReceiverAndStatus( + User receiver, + FriendRequestStatus status + ); + + @Query(""" + select fr + from FriendRequest fr + join fetch fr.receiver r + where fr.sender = :sender + and fr.status = :status + """) + List findBySenderAndStatusWithReceiver( + @Param("sender") User sender, + @Param("status") FriendRequestStatus status + ); } diff --git a/src/main/java/com/example/egobook_be/domain/friend/service/FriendService.java b/src/main/java/com/example/egobook_be/domain/friend/service/FriendService.java index d2b0a07..b49a886 100644 --- a/src/main/java/com/example/egobook_be/domain/friend/service/FriendService.java +++ b/src/main/java/com/example/egobook_be/domain/friend/service/FriendService.java @@ -1,9 +1,6 @@ package com.example.egobook_be.domain.friend.service; -import com.example.egobook_be.domain.friend.dto.FriendRequestCreateReqDto; -import com.example.egobook_be.domain.friend.dto.FriendRequestListResDto; -import com.example.egobook_be.domain.friend.dto.FriendResDto; -import com.example.egobook_be.domain.friend.dto.FriendSearchResDto; +import com.example.egobook_be.domain.friend.dto.*; import com.example.egobook_be.domain.friend.entity.Friend; import com.example.egobook_be.domain.friend.entity.FriendRequest; import com.example.egobook_be.domain.friend.enums.FriendRequestStatus; @@ -87,6 +84,14 @@ public void acceptRequest(Long receiverId, Long requestId) { .findByIdAndReceiver(requestId, receiver) .orElseThrow(() -> new CustomException(FriendErrorCode.FRIEND_REQUEST_NOT_FOUND)); + User sender = request.getSender(); + + // 친구 수 제한 체크 (양쪽 모두) + if (friendRepository.countByUser(receiver) >= 10 + || friendRepository.countByUser(sender) >= 10) { + throw new CustomException(FriendErrorCode.FRIEND_LIMIT_EXCEEDED); + } + request.accept(); // 양방향 친구 관계 생성 @@ -149,22 +154,40 @@ public void deleteFriend(Long userId, Long friendId) { /** 내가 받은 친구 신청 목록 (승인 대기) **/ @Transactional(readOnly = true) - public List getIncomingRequests(Long userId) { + public FriendRequestListWithCountResDto getIncomingRequests(Long userId) { User receiver = userRepository.findById(userId) .orElseThrow(() -> new CustomException(FriendErrorCode.USER_NOT_FOUND)); - return friendRequestRepository - .findByReceiverAndStatus(receiver, FriendRequestStatus.PENDING) - .stream() - .map(req -> FriendRequestListResDto.builder() - .requestId(req.getId()) - .userId(req.getSender().getId()) - .nickname(req.getSender().getNickname()) - .requestedAt(req.getCreatedAt()) - .build() - ) + List requests = + friendRequestRepository.findByReceiverAndStatus( + receiver, + FriendRequestStatus.PENDING + ); + + int totalCount = (int) friendRequestRepository.countByReceiverAndStatus( + receiver, + FriendRequestStatus.PENDING + ); + + List list = requests.stream() + .map(req -> { + User sender = req.getSender(); + + return FriendRequestListResDto.builder() + .requestId(req.getId()) + .userId(sender.getId()) + .nickname(sender.getNickname()) + .level(sender.getLevel()) + .requestedAt(req.getCreatedAt()) + .build(); + }) .toList(); + + return FriendRequestListWithCountResDto.builder() + .totalCount(totalCount) + .requests(list) + .build(); } /** 내가 보낸 친구 신청 목록 **/ @@ -175,33 +198,46 @@ public List getOutgoingRequests(Long userId) { .orElseThrow(() -> new CustomException(FriendErrorCode.USER_NOT_FOUND)); return friendRequestRepository - .findBySenderAndStatus(sender, FriendRequestStatus.PENDING) + .findBySenderAndStatusWithReceiver(sender, FriendRequestStatus.PENDING) .stream() - .map(req -> FriendRequestListResDto.builder() - .requestId(req.getId()) - .userId(req.getReceiver().getId()) - .nickname(req.getReceiver().getNickname()) - .requestedAt(req.getCreatedAt()) - .build() - ) + .map(req -> { + User receiver = req.getReceiver(); + + return FriendRequestListResDto.builder() + .requestId(req.getId()) + .userId(receiver.getId()) + .nickname(receiver.getNickname()) + .level(receiver.getLevel()) + .requestedAt(req.getCreatedAt()) + .build(); + }) .toList(); } /** 친구 리스트 **/ @Transactional(readOnly = true) - public List getFriends(Long userId) { + public FriendListResDto getFriends(Long userId) { User user = userRepository.findById(userId) .orElseThrow(() -> new CustomException(FriendErrorCode.USER_NOT_FOUND)); - return friendRepository.findByUser(user) + List friends = friendRepository.findByUserWithFriend(user) .stream() - .map(friend -> FriendResDto.builder() - .friendId(friend.getFriend().getId()) - .nickname(friend.getFriend().getNickname()) - .build() - ) + .map(friend -> { + User friendUser = friend.getFriend(); + + return FriendResDto.builder() + .friendId(friendUser.getId()) + .nickname(friendUser.getNickname()) + .level(friendUser.getLevel()) + .build(); + }) .toList(); + + return FriendListResDto.builder() + .count(friends.size()) + .friends(friends) + .build(); } /** 친구 검색 **/