diff --git a/src/main/java/EatPic/spring/domain/card/controller/CardController.java b/src/main/java/EatPic/spring/domain/card/controller/CardController.java index 0104480..6393f32 100644 --- a/src/main/java/EatPic/spring/domain/card/controller/CardController.java +++ b/src/main/java/EatPic/spring/domain/card/controller/CardController.java @@ -143,5 +143,13 @@ public ApiResponse> getRecommendedCards( return ApiResponse.onSuccess(cardService.getRecommendedCardPreviews(user.getId())); } + @GetMapping("/profile/{userId}") + @Operation(summary = "프로필 화면 피드 미리보기", description = "공유한 카드의 번호와 이미지 url 조회 API") + public ApiResponse getProfileCardsList(@PathVariable Long userId, + @RequestParam(required = false) Long cursor, + @RequestParam(defaultValue = "15") int size) { + return ApiResponse.onSuccess(cardService.getProfileCardList(userId, size, cursor)); + } + } diff --git a/src/main/java/EatPic/spring/domain/card/converter/CardConverter.java b/src/main/java/EatPic/spring/domain/card/converter/CardConverter.java index c5421f2..72fce44 100644 --- a/src/main/java/EatPic/spring/domain/card/converter/CardConverter.java +++ b/src/main/java/EatPic/spring/domain/card/converter/CardConverter.java @@ -157,4 +157,23 @@ public static CardDeleteResponse toCardDeleteResponse(Card card) { .successMessage("카드 삭제 성공") .build(); } + + public static CardResponse.ProfileCardDTO toProfileCardDto(Card card){ + return CardResponse.ProfileCardDTO.builder() + .cardId(card.getId()) + .cardImageUrl(card.getCardImageUrl()) + .build(); + } + + public static CardResponse.ProfileCardListDTO toProfileCardList(Long userId, Slice cardList) { + List list = cardList.getContent(); + return CardResponse.ProfileCardListDTO.builder() + .hasNext(cardList.hasNext()) + .nextCursor(cardList.hasNext() ? list.get(list.size() - 1).getId() : null) + .userId(userId) + .cardsList(cardList.getContent().stream() + .map(CardConverter::toProfileCardDto) + .toList()) + .build(); + } } diff --git a/src/main/java/EatPic/spring/domain/card/dto/response/CardResponse.java b/src/main/java/EatPic/spring/domain/card/dto/response/CardResponse.java index 64e9393..eff1d3b 100644 --- a/src/main/java/EatPic/spring/domain/card/dto/response/CardResponse.java +++ b/src/main/java/EatPic/spring/domain/card/dto/response/CardResponse.java @@ -293,6 +293,25 @@ public static class CardDeleteResponse { } + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class ProfileCardListDTO{ + private Long userId; + private boolean hasNext; + private Long nextCursor; + private List cardsList; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class ProfileCardDTO { + private Long cardId; + private String cardImageUrl; + } } diff --git a/src/main/java/EatPic/spring/domain/card/repository/CardRepository.java b/src/main/java/EatPic/spring/domain/card/repository/CardRepository.java index 13d39a9..cb2ef6a 100644 --- a/src/main/java/EatPic/spring/domain/card/repository/CardRepository.java +++ b/src/main/java/EatPic/spring/domain/card/repository/CardRepository.java @@ -110,4 +110,17 @@ SELECT ch.hashtag.id, COUNT(ch.card.id) """) List countCardsByHashtagIds(@Param("hashtagIds") List hashtagIds); + Slice findByUserIdAndIsSharedTrueAndIsDeletedFalseOrderByIdDesc(Long userId, Pageable pageable); + Slice findByUserIdAndIsSharedTrueAndIsDeletedFalseAndIdLessThanOrderByIdDesc(Long userId, Long cursor, Pageable pageable); + + @Query(""" + select count(c) + from Card c + where c.isDeleted = false + and c.isShared = true + and c.user.id = :userId +""") + Long countByUserIdAndIsDeletedFalseAndIsSharedTrue(@Param("userId") Long userId); + + } \ No newline at end of file diff --git a/src/main/java/EatPic/spring/domain/card/service/CardService.java b/src/main/java/EatPic/spring/domain/card/service/CardService.java index 8b6b823..eba237a 100644 --- a/src/main/java/EatPic/spring/domain/card/service/CardService.java +++ b/src/main/java/EatPic/spring/domain/card/service/CardService.java @@ -25,4 +25,5 @@ public interface CardService { CardDetailResponse updateCard(Long cardId, User user, CardUpdateRequest request); CardResponse.PagedCardFeedResponseDto getCardFeedByCursor(HttpServletRequest request, Long userId, int size, Long cursor); List getRecommendedCardPreviews(Long userId); + CardResponse.ProfileCardListDTO getProfileCardList(Long userId, int size, Long cursor); } diff --git a/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java b/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java index c6cbccf..8240ff1 100644 --- a/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java +++ b/src/main/java/EatPic/spring/domain/card/service/CardServiceImpl.java @@ -251,6 +251,19 @@ public CardFeedResponse getCardFeed(Long cardId, Long userId) { ); } + @Override + public CardResponse.ProfileCardListDTO getProfileCardList(Long userId, int size, Long cursor) { + Slice cardSlice; + Pageable pageable = PageRequest.of(0, size); + + if (cursor == null) { + cardSlice = cardRepository.findByUserIdAndIsSharedTrueAndIsDeletedFalseOrderByIdDesc(userId, pageable); + } else { + cardSlice = cardRepository.findByUserIdAndIsSharedTrueAndIsDeletedFalseAndIdLessThanOrderByIdDesc(userId, cursor, pageable); + } + return CardConverter.toProfileCardList(userId, cardSlice); + } + @Override @Transactional public CardDeleteResponse deleteCard(Long cardId, Long userId) { diff --git a/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java b/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java index 285d421..67a3f00 100644 --- a/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java +++ b/src/main/java/EatPic/spring/domain/user/controller/UserRestController.java @@ -79,4 +79,11 @@ public ApiResponse updateUserIntroduce( UserResponseDTO.ProfileDto updatedProfile = userService.updateIntroduce(request, introduce, user); return ApiResponse.onSuccess(updatedProfile); } + + @Operation(summary = "유저 프로필 조회") + @GetMapping("/profile/{userId}") + public ApiResponse getProfile(HttpServletRequest request, + @PathVariable Long userId) { + return ApiResponse.onSuccess(userService.getProfile(request,userId)); + } } diff --git a/src/main/java/EatPic/spring/domain/user/converter/UserConverter.java b/src/main/java/EatPic/spring/domain/user/converter/UserConverter.java index 5fb8b3b..21d4917 100644 --- a/src/main/java/EatPic/spring/domain/user/converter/UserConverter.java +++ b/src/main/java/EatPic/spring/domain/user/converter/UserConverter.java @@ -63,7 +63,6 @@ public static UserResponseDTO.ProfileDto toProfileIconDto(User user){ .isFollowing(true) .build(); } - // todo: 두개 비슷함 -> 합치기 public static UserResponseDTO.ProfileDto toProfileDto(User user, Boolean isFollowing){ return UserResponseDTO.ProfileDto.builder() .userId(user.getId()) @@ -73,6 +72,25 @@ public static UserResponseDTO.ProfileDto toProfileDto(User user, Boolean isFollo .isFollowing(isFollowing) .build(); } + public static UserResponseDTO.DetailProfileDto toDetailProfileDto( + User user, + Boolean isFollowing, + Long totalCard, + Long totalFollower, + Long totalFollowing) { + + return UserResponseDTO.DetailProfileDto.builder() + .userId(user.getId()) + .profileImageUrl(user.getProfileImageUrl()) + .nameId(user.getNameId()) + .nickname(user.getNickname()) + .isFollowing(isFollowing) + .introduce(user.getIntroduce()) + .totalCard(totalCard) + .totalFollower(totalFollower) + .totalFollowing(totalFollowing) + .build(); + } public static ReactionResponseDTO.CardReactionUserListDto toCardReactionUsersListDto(Long cardId, ReactionType reactionType, Page profileList){ return ReactionResponseDTO.CardReactionUserListDto.builder() diff --git a/src/main/java/EatPic/spring/domain/user/dto/response/UserResponseDTO.java b/src/main/java/EatPic/spring/domain/user/dto/response/UserResponseDTO.java index 61f7eee..d72fd56 100644 --- a/src/main/java/EatPic/spring/domain/user/dto/response/UserResponseDTO.java +++ b/src/main/java/EatPic/spring/domain/user/dto/response/UserResponseDTO.java @@ -44,6 +44,31 @@ public static class UserIconListResponseDto{ private List userIconList; } + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class DetailProfileDto { + @NotNull + private Long userId; + @NotNull + private String profileImageUrl; + @NotNull + private String nameId; + @NotNull + private String nickname; + @NotNull + private Boolean isFollowing; + @NotNull + private String introduce; + @NotNull + private Long totalCard; + @NotNull + private Long totalFollower; + @NotNull + private Long totalFollowing; + } + @Getter @Builder @AllArgsConstructor diff --git a/src/main/java/EatPic/spring/domain/user/repository/UserFollowRepository.java b/src/main/java/EatPic/spring/domain/user/repository/UserFollowRepository.java index 9ee64b9..635264b 100644 --- a/src/main/java/EatPic/spring/domain/user/repository/UserFollowRepository.java +++ b/src/main/java/EatPic/spring/domain/user/repository/UserFollowRepository.java @@ -23,4 +23,7 @@ public interface UserFollowRepository extends JpaRepository { List findFollowingUserIds(@Param("userId") Long userId); UserFollow findByUserAndTargetUser(User user, User target); + + Long countUserFollowByTargetUser(User targetUser); + Long countUserFollowByUser(User user); } diff --git a/src/main/java/EatPic/spring/domain/user/service/UserService.java b/src/main/java/EatPic/spring/domain/user/service/UserService.java index 0d26ad3..363ca3a 100644 --- a/src/main/java/EatPic/spring/domain/user/service/UserService.java +++ b/src/main/java/EatPic/spring/domain/user/service/UserService.java @@ -27,6 +27,7 @@ public interface UserService { UserResponseDTO.UserActionResponseDto followUser(HttpServletRequest request, Long targetUserId); UserResponseDTO.UserActionResponseDto unfollowUser(HttpServletRequest request, Long targetUserId); User getLoginUser(HttpServletRequest request); + UserResponseDTO.DetailProfileDto getProfile(HttpServletRequest request,Long userId); // 마이페이지 업데이트 관련 서비스 UserResponseDTO.ProfileDto updateUserProfileImage(HttpServletRequest request, MultipartFile profileImage, User user); diff --git a/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java b/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java index aedd598..87ffb73 100644 --- a/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java +++ b/src/main/java/EatPic/spring/domain/user/service/UserServiceImpl.java @@ -1,5 +1,6 @@ package EatPic.spring.domain.user.service; +import EatPic.spring.domain.card.repository.CardRepository; import EatPic.spring.domain.user.converter.UserConverter; import EatPic.spring.domain.user.dto.*; import EatPic.spring.domain.user.dto.request.LoginRequestDTO; @@ -44,6 +45,7 @@ public class UserServiceImpl implements UserService{ private final UserFollowRepository userFollowRepository; private final UserBlockRepository userBlockRepository; private final UserBadgeService userBadgeService; + private final CardRepository cardRepository; private final PasswordEncoder passwordEncoder; private final JwtTokenProvider jwtTokenProvider; @@ -189,6 +191,19 @@ public User getLoginUser(HttpServletRequest request) { .orElseThrow(() -> new ExceptionHandler(ErrorStatus.MEMBER_NOT_FOUND)); } + @Override + public UserResponseDTO.DetailProfileDto getProfile(HttpServletRequest request, Long userId) { + User me = getLoginUser(request); + + User user = userRepository.findById(userId).orElseThrow(()-> new ExceptionHandler(USER_NOT_FOUND)); + Boolean isFollowing = userFollowRepository.existsByUserAndTargetUser(me, user); + Long totalCard = cardRepository.countByUserIdAndIsDeletedFalseAndIsSharedTrue(userId); + Long totalFollower = userFollowRepository.countUserFollowByTargetUser(user); + Long totalFollowing = userFollowRepository.countUserFollowByUser(user); + + return UserConverter.toDetailProfileDto(user, isFollowing,totalCard,totalFollower,totalFollowing); + } + @Override public UserResponseDTO.UserActionResponseDto unfollowUser(HttpServletRequest request, Long targetUserId) { User user = getLoginUser(request); @@ -207,8 +222,13 @@ public UserResponseDTO.UserActionResponseDto unfollowUser(HttpServletRequest req @Override public UserResponseDTO.UserActionResponseDto followUser(HttpServletRequest request, Long targetUserId) { User user = getLoginUser(request); + if(user.getId().equals(targetUserId)) { + throw new ExceptionHandler(FOLLOW_FORBBIDEN); + } + User target = userRepository.findUserById(targetUserId); + UserFollow prev = userFollowRepository.findByUserAndTargetUser(user,target); UserFollow follow = UserFollow.builder().user(user).targetUser(target).build(); if(prev!=null && prev.getTargetUser().getId().equals(targetUserId)) { diff --git a/src/main/java/EatPic/spring/global/common/code/status/ErrorStatus.java b/src/main/java/EatPic/spring/global/common/code/status/ErrorStatus.java index bb0175e..083de71 100644 --- a/src/main/java/EatPic/spring/global/common/code/status/ErrorStatus.java +++ b/src/main/java/EatPic/spring/global/common/code/status/ErrorStatus.java @@ -65,6 +65,7 @@ public enum ErrorStatus implements BaseErrorCode { // 팔로우 관련 응답 FOLLOW_ALREADY_EXISTS(HttpStatus.BAD_REQUEST, "FOLLOW_001", "이미 팔로잉 중입니다"), FOLLOW_NOT_EXISTS(HttpStatus.BAD_REQUEST, "FOLLOW_002", "팔로우 기록이 없습니다"), + FOLLOW_FORBBIDEN(HttpStatus.FORBIDDEN, "FOLLOW_003", "자신을 팔로우 할 수 없습니다."), // 약관 관련 응답 TERM_NOT_FOUND(HttpStatus.NOT_FOUND, "TERM_001", "해당 약관을 찾을 수 없습니다.");