diff --git a/src/main/java/com/example/cp_main_be/domain/avatar/avatar/service/AvatarService.java b/src/main/java/com/example/cp_main_be/domain/avatar/avatar/service/AvatarService.java index 34db7c5d..80ebb823 100644 --- a/src/main/java/com/example/cp_main_be/domain/avatar/avatar/service/AvatarService.java +++ b/src/main/java/com/example/cp_main_be/domain/avatar/avatar/service/AvatarService.java @@ -10,12 +10,9 @@ import com.example.cp_main_be.domain.member.notification.service.NotificationService; import com.example.cp_main_be.domain.member.user.domain.User; import com.example.cp_main_be.domain.member.user.domain.repository.UserRepository; -import com.example.cp_main_be.domain.mission.wishTree.WishTree; import com.example.cp_main_be.domain.mission.wishTree.WishTreeRepository; -import com.example.cp_main_be.domain.mission.wishTree.WishTreeStage; import com.example.cp_main_be.global.common.CustomApiException; import com.example.cp_main_be.global.common.ErrorCode; -import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -73,24 +70,15 @@ public void createAvatar(Long userId, String nickname, String imageUrl, Long mas avatarRepository.save(newAvatar); - WishTree wishTree = - wishTreeRepository - .findByUserId(userId) + // 1. 잠겨있지 않으면서(isLocked=false) 아바타가 비어있는(avatar=null) 정원을 찾습니다. + Garden emptyGarden = + gardenRepository + .findFirstByUserAndIsLockedIsFalseAndAvatarIsNullOrderBySlotNumberAsc(user) .orElseThrow( - () -> new CustomApiException(ErrorCode.NOT_FOUND, "사용자의 소원나무를 찾을 수 없습니다.")); + () -> new CustomApiException(ErrorCode.GARDEN_NOT_FOUND, "배치할 수 있는 빈 정원이 없습니다.")); - WishTreeStage stage = wishTree.getStage(); - Long maxGardens = stage.getMaxGardens(); - - List userGardens = user.getGardens(); - if (userGardens.size() < maxGardens) { - Garden newGarden = - Garden.builder().user(user).slotNumber(userGardens.size() + 1).avatar(newAvatar).build(); - gardenRepository.save(newGarden); - } else { - // 이미 존재하는 'GARDEN_SLOT_MAXED_OUT' 에러 코드를 재사용하여 일관성을 유지합니다. - throw new CustomApiException(ErrorCode.GARDEN_SLOT_MAXED_OUT); - } + // 2. 해당 정원에 새로 생성한 아바타를 배치합니다. + emptyGarden.updateAvatar(newAvatar); } @Transactional(readOnly = true) diff --git a/src/main/java/com/example/cp_main_be/domain/avatar/image/service/ImageProcessingService.java b/src/main/java/com/example/cp_main_be/domain/avatar/image/service/ImageProcessingService.java index 5406a7d3..a006fb21 100644 --- a/src/main/java/com/example/cp_main_be/domain/avatar/image/service/ImageProcessingService.java +++ b/src/main/java/com/example/cp_main_be/domain/avatar/image/service/ImageProcessingService.java @@ -81,8 +81,8 @@ public String processImageWithAi(MultipartFile imageFile) { new CustomApiException(ErrorCode.AI_AVATAR_FAILED)); })) .bodyToMono(byte[].class) - .timeout(Duration.ofSeconds(30)) - .block(Duration.ofSeconds(30)); + .timeout(Duration.ofSeconds(300)) + .block(Duration.ofSeconds(300)); if (result == null || result.length == 0) { log.error("AI 서버에서 유효한 이미지 바이트를 받지 못했습니다 (null/empty)."); diff --git a/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/Garden.java b/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/Garden.java index 81e83326..84735184 100644 --- a/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/Garden.java +++ b/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/Garden.java @@ -75,6 +75,10 @@ public Garden(User user, Integer slotNumber, GardenBackground gardenBackground, this.avatar = avatar; // [추가] } + public void unlock() { + this.isLocked = false; + } + public void increaseWaterCount() { this.waterCount++; } diff --git a/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/repository/GardenRepository.java b/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/repository/GardenRepository.java index 9f4381fd..e4a494ef 100644 --- a/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/repository/GardenRepository.java +++ b/src/main/java/com/example/cp_main_be/domain/garden/garden/domain/repository/GardenRepository.java @@ -15,4 +15,12 @@ public interface GardenRepository extends JpaRepository { + "JOIN FETCH g.gardenBackground " + "WHERE g.user = :user") Optional findByUserWithDetails(@Param("user") User user); + + // 유저의 정원 중 잠겨있으면서 가장 낮은 슬롯 번호를 가진 정원 1개를 찾기 + Optional findFirstByUserAndIsLockedIsTrueOrderBySlotNumberAsc(User user); + + // 유저의 해금된 정원 개수 세기 + long countByUserAndIsLockedIsFalse(User user); + + Optional findFirstByUserAndIsLockedIsFalseAndAvatarIsNullOrderBySlotNumberAsc(User user); } diff --git a/src/main/java/com/example/cp_main_be/domain/garden/garden/presentation/GardenController.java b/src/main/java/com/example/cp_main_be/domain/garden/garden/presentation/GardenController.java index 3c27a6b3..9c4e9701 100644 --- a/src/main/java/com/example/cp_main_be/domain/garden/garden/presentation/GardenController.java +++ b/src/main/java/com/example/cp_main_be/domain/garden/garden/presentation/GardenController.java @@ -58,10 +58,17 @@ public ResponseEntity> sunlightGarden( @PutMapping("/slots/unlock") public ResponseEntity> unlockGarden() { User currentUser = userService.getCurrentUser(); - gardenService.unlockNewGardenSlot(currentUser.getId()); + gardenService.unlockNextGarden(currentUser.getId()); return ResponseEntity.ok(ApiResponse.success(null)); } + @Operation(summary = "텃밭 슬롯 해금", description = "해금 버튼 클릭 시 호출") + @PostMapping("/unlock") + public ResponseEntity unlockGarden(@AuthenticationPrincipal User user) { + gardenService.unlockNextGarden(user.getId()); + return ResponseEntity.ok().build(); + } + @Operation(summary = "텃밭 배경화면 update", description = "텃밭의 배경화면을 수정한다.") @PutMapping("/{gardenId}/background/{backgroundId}") public ResponseEntity> updateBackgroundImage( diff --git a/src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java b/src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java index d0ebd266..9a4da214 100644 --- a/src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java +++ b/src/main/java/com/example/cp_main_be/domain/garden/garden/service/GardenService.java @@ -1,6 +1,5 @@ package com.example.cp_main_be.domain.garden.garden.service; -import com.example.cp_main_be.domain.avatar.avatar.domain.Avatar; import com.example.cp_main_be.domain.avatar.avatar.domain.repository.AvatarRepository; import com.example.cp_main_be.domain.garden.garden.domain.Garden; import com.example.cp_main_be.domain.garden.garden.domain.GardenBackground; @@ -16,7 +15,6 @@ import com.example.cp_main_be.domain.mission.wishTree.WishTree; import com.example.cp_main_be.domain.mission.wishTree.WishTreeRepository; import com.example.cp_main_be.domain.mission.wishTree.WishTreeService; -import com.example.cp_main_be.domain.mission.wishTree.WishTreeStage; import com.example.cp_main_be.global.common.CustomApiException; import com.example.cp_main_be.global.common.ErrorCode; import com.example.cp_main_be.global.event.WishTreeEvolvedEvent; @@ -167,66 +165,27 @@ public void sunlightGarden(Long actorId, Long gardenId) { garden.recordSunlightTime(); // 햇빛 준 시간 기록 } - @Transactional - public void unlockNewGardenSlot(Long userId) { - User user = - userRepository - .findById(userId) - .orElseThrow(() -> new CustomApiException(ErrorCode.USER_NOT_FOUND)); - - WishTree wishTree = - wishTreeRepository - .findByUserId(userId) - .orElseThrow(() -> new CustomApiException(ErrorCode.WISH_TREE_NOT_FOUND)); - - // 1. 사용자의 WishTree 포인트를 기준으로 현재 단계를 결정 - // User는 WishTree를 가지고 있고, WishTree가 포인트를 관리합니다. - WishTreeStage currentStage = WishTreeStage.getStageForPoints(wishTree.getPoints()); - - // 2. 현재 단계에서 가질 수 있는 최대 텃밭 개수를 가져옴 - long maxGardensForStage = currentStage.getMaxGardens(); - - // 3. 사용자가 현재 보유한 텃밭 개수 확인 - int currentGardenCount = user.getGardens().size(); + public void unlockNextGarden(Long userId) { + // 1. 유저와 소원나무 정보 가져오기 + User user = userRepository.findById(userId).orElseThrow(/* ... */ ); + WishTree wishTree = wishTreeRepository.findByUserId(user.getId()).orElseThrow(/* ... */ ); - // 4. 최대 텃밭 개수에 도달했는지 확인 - if (currentGardenCount >= maxGardensForStage) { - // 현재 단계에서는 더 이상 텃밭을 생성할 수 없음 - // ErrorCode에 GARDEN_SLOT_LOCKED 추가가 필요할 수 있습니다. - throw new CustomApiException( - ErrorCode.GARDEN_SLOT_LOCKED, "현재 단계에서는 더 이상 텃밭을 만들 수 없습니다. 소원나무를 성장시켜주세요."); + // 2. 해금 가능한 상태인지 확인 + if (!wishTree.isUnlockable()) { + throw new CustomApiException(ErrorCode.GARDEN_SLOT_LOCKED, "정원을 해금할 수 있는 포인트가 부족합니다."); } - // TODO: 여기 뭔가 수정해야할 듯 아바타는 설정해서 가져오고 배경화면은 그냥 기본걸 씀 - // 5. 새로 생성될 텃밭의 기본 배경과 아바타를 설정 (ID 1L을 기본값으로 가정) - GardenBackground defaultBackground = - gardenBackgroundRepository - .findById(1L) - .orElseThrow( - () -> - new CustomApiException( - ErrorCode.DEFAULT_RESOURCE_NOT_FOUND, "기본 텃밭 배경을 찾을 수 없습니다.")); - Avatar defaultAvatar = - avatarRepository - .findById(1L) - .orElseThrow( - () -> - new CustomApiException( - ErrorCode.DEFAULT_RESOURCE_NOT_FOUND, "기본 아바타를 찾을 수 없습니다.")); - - // 6. 새로운 텃밭 생성 - Garden newGarden = - Garden.builder() - .user(user) - .slotNumber(currentGardenCount + 1) - .gardenBackground(defaultBackground) - .avatar(defaultAvatar) - .build(); + // 3. 소원나무 Stage를 다음 단계로 성장시키기 + wishTree.evolveStage(); - gardenRepository.save(newGarden); + // 4. 다음으로 잠겨있는 정원을 찾아 해금하기 + Garden gardenToUnlock = + gardenRepository + .findFirstByUserAndIsLockedIsTrueOrderBySlotNumberAsc(user) + .orElseThrow( + () -> new CustomApiException(ErrorCode.GARDEN_NOT_FOUND, "해금할 정원을 찾을 수 없습니다.")); - // User 엔티티의 gardens 리스트에도 추가 - user.addGarden(newGarden); + gardenToUnlock.unlock(); // Garden 엔티티의 isLocked를 false로 변경 } @Transactional @@ -301,6 +260,6 @@ public void cleanupOldWateringLogs() { @EventListener @Transactional public void handleWishTreeEvolved(WishTreeEvolvedEvent event) { - unlockNewGardenSlot(event.getUserId()); + unlockNextGarden(event.getUserId()); } } diff --git a/src/main/java/com/example/cp_main_be/domain/home/HomeResponseDto.java b/src/main/java/com/example/cp_main_be/domain/home/HomeResponseDto.java index 31f87a6f..7bad848b 100644 --- a/src/main/java/com/example/cp_main_be/domain/home/HomeResponseDto.java +++ b/src/main/java/com/example/cp_main_be/domain/home/HomeResponseDto.java @@ -35,6 +35,7 @@ public static class GardenSummaryInfo { private Integer gardenSlotNumber; private AvatarInfo avatar; // 각 정원에 배치된 아바타 정보 -> 해금안되면 null private boolean isLocked; + private boolean isUnlockable; private boolean isOwnerWateringAble; // 본인 정원에 물주기 가능한지 여부 -> 해금안되면 null private boolean isOwnerSunlightAble; // 본인 정원에 햇빛 주기 가능한지 여부 -> 해금안되면 null } diff --git a/src/main/java/com/example/cp_main_be/domain/home/service/HomeService.java b/src/main/java/com/example/cp_main_be/domain/home/service/HomeService.java index 0d838150..bfd2315f 100644 --- a/src/main/java/com/example/cp_main_be/domain/home/service/HomeService.java +++ b/src/main/java/com/example/cp_main_be/domain/home/service/HomeService.java @@ -66,18 +66,26 @@ public HomeResponseDto getHomeScreenData(Long userId) { .findById(userId) .orElseThrow(() -> new CustomApiException(ErrorCode.NOT_FOUND)); + WishTree wishTreeOfUser = user.getWishTree(); // 1. UserInfo 구성 int unreadNotificationCount = notificationRepository.countByReceiverAndIsReadFalse(user); HomeResponseDto.UserInfo userInfo = HomeResponseDto.UserInfo.builder() .id(user.getId()) .username(user.getNickname()) - .level(user.getLevel()) - .currentExp(user.getExperience()) - .requiredExpForNextLevel(calculateRequiredExpForLevel(user.getLevel() + 1)) + .level(wishTreeOfUser.getStage().ordinal() + 1) + .currentExp(wishTreeOfUser.getPoints()) + .requiredExpForNextLevel(wishTreeOfUser.getStage().getRequiredPointsForNextStage()) .unreadNotificationCount(unreadNotificationCount) .build(); + WishTree wishTree = + wishTreeRepository + .findByUserId(userId) + .orElseThrow(() -> new CustomApiException(ErrorCode.NOT_FOUND)); + + long unlockedGardenCount = user.getGardens().stream().filter(g -> !g.isLocked()).count(); + Map userGardens = user.getGardens().stream() .collect(Collectors.toMap(Garden::getSlotNumber, garden -> garden)); @@ -117,15 +125,22 @@ public HomeResponseDto getHomeScreenData(Long userId) { .gardenSlotNumber(slotNumber) .avatar(avatarInfo) .isLocked(false) + .isUnlockable(false) .isOwnerWateringAble(isOwnerWateringAble) .isOwnerSunlightAble(isOwnerSunlightAble) .build(); } else { + boolean isUnlockable = + garden != null + && wishTree.isUnlockable() + && garden.getSlotNumber() == unlockedGardenCount + 1; + return HomeResponseDto.GardenSummaryInfo.builder() .gardenId(garden != null ? garden.getId() : null) .gardenSlotNumber(slotNumber) .avatar(null) .isLocked(true) + .isUnlockable(isUnlockable) .isOwnerWateringAble(false) .isOwnerSunlightAble(false) .build(); diff --git a/src/main/java/com/example/cp_main_be/domain/member/auth/service/AuthService.java b/src/main/java/com/example/cp_main_be/domain/member/auth/service/AuthService.java index 09594a12..b56ed44f 100644 --- a/src/main/java/com/example/cp_main_be/domain/member/auth/service/AuthService.java +++ b/src/main/java/com/example/cp_main_be/domain/member/auth/service/AuthService.java @@ -1,5 +1,7 @@ package com.example.cp_main_be.domain.member.auth.service; +import com.example.cp_main_be.domain.garden.garden.domain.Garden; +import com.example.cp_main_be.domain.garden.garden.domain.repository.GardenRepository; import com.example.cp_main_be.domain.member.auth.domain.RefreshToken; import com.example.cp_main_be.domain.member.auth.domain.repository.RefreshTokenRepository; import com.example.cp_main_be.domain.member.auth.dto.request.RegistrationRequest; @@ -13,6 +15,7 @@ import com.example.cp_main_be.global.jwt.JwtTokenProvider; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.List; import java.util.UUID; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; @@ -29,6 +32,7 @@ public class AuthService { private final RefreshTokenRepository refreshTokenRepository; private final Logger logger = LoggerFactory.getLogger(AuthService.class); private final WishTreeService wishTreeService; + private final GardenRepository gardenRepository; /** 리프레시 토큰으로 액세스 토큰 재발급 + (권장) 리프레시 토큰 롤링 */ @Transactional @@ -79,6 +83,17 @@ public AnonymousRegistrationResponse registerNewUser( // 1. 사용자를 먼저 저장합니다. User savedUser = userRepository.save(newUser); + Garden firstGarden = + Garden.builder().user(savedUser).slotNumber(1).isLocked(false).build(); // 1번은 기본 해금 + Garden secondGarden = + Garden.builder().user(savedUser).slotNumber(2).isLocked(true).build(); // 2번은 잠김 + Garden thirdGarden = + Garden.builder().user(savedUser).slotNumber(3).isLocked(true).build(); // 3번은 잠김 + Garden fourthGarden = + Garden.builder().user(savedUser).slotNumber(4).isLocked(true).build(); // 4번은 잠김 + + gardenRepository.saveAll(List.of(firstGarden, secondGarden, thirdGarden, fourthGarden)); + // 2. 위시트리 관련 로직을 수행합니다. // 만약 여기서 예외가 발생하면, 위에서 저장한 newUser까지 모두 롤백됩니다. wishTreeService.addPointsToWishTree(savedUser.getId(), 0L); diff --git a/src/main/java/com/example/cp_main_be/domain/member/user/domain/User.java b/src/main/java/com/example/cp_main_be/domain/member/user/domain/User.java index cd40ce06..5515e772 100644 --- a/src/main/java/com/example/cp_main_be/domain/member/user/domain/User.java +++ b/src/main/java/com/example/cp_main_be/domain/member/user/domain/User.java @@ -3,6 +3,7 @@ import com.example.cp_main_be.domain.avatar.avatar.domain.Avatar; import com.example.cp_main_be.domain.garden.garden.domain.Garden; import com.example.cp_main_be.domain.mission.diary.domain.Diary; +import com.example.cp_main_be.domain.mission.wishTree.WishTree; import com.example.cp_main_be.domain.social.bookmark.domain.Bookmark; import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.*; @@ -83,6 +84,9 @@ public class User implements UserDetails { @Builder.Default private Boolean notificationEnabled = true; + @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private WishTree wishTree; + @PrePersist protected void onCreate() { this.createdAt = LocalDateTime.now(); diff --git a/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTree.java b/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTree.java index f23e9887..884d80d8 100644 --- a/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTree.java +++ b/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTree.java @@ -34,21 +34,40 @@ public WishTree(User user) { this.stage = WishTreeStage.SPROUT; } + @Column(nullable = false) + private boolean isUnlockable = false; + /** * 포인트 추가 및 성장 로직 * * @param amount 추가할 포인트 * @return 성장을 했는지 여부 */ - public boolean addPoints(Long amount) { - WishTreeStage previousStage = this.stage; - this.points += amount; - WishTreeStage newStage = WishTreeStage.getStageForPoints(this.points); - - if (newStage != previousStage) { - this.stage = newStage; - return true; // 성장했다! + public void addPoints(Long points) { + this.points += points; + + // 이미 해금 가능 상태이거나, 다음 스테이지가 없으면 아무것도 하지 않음 + if (this.isUnlockable || this.stage.getNextStage() == null) { + return; + } + + // 다음 스테이지의 요구 포인트를 넘었는지 확인 + WishTreeStage nextStage = this.stage.getNextStage(); + if (this.points >= this.stage.getRequiredPointsForNextStage()) { + this.isUnlockable = true; // 👈 Stage를 바로 바꾸는 대신, 해금 가능 상태로 변경 + } + } + + public void evolveStage() { + if (!this.isUnlockable) { + // 해금 불가능한 상태에서 호출 시 예외 처리 또는 로깅 + return; + } + + WishTreeStage nextStage = this.stage.getNextStage(); + if (nextStage != null) { + this.stage = nextStage; + this.isUnlockable = false; // 상태 플래그 초기화 } - return false; // 성장 안함 } } diff --git a/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTreeService.java b/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTreeService.java index d1562fd2..abc107cf 100644 --- a/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTreeService.java +++ b/src/main/java/com/example/cp_main_be/domain/mission/wishTree/WishTreeService.java @@ -2,7 +2,6 @@ import com.example.cp_main_be.domain.member.user.domain.User; import com.example.cp_main_be.domain.member.user.domain.repository.UserRepository; -import com.example.cp_main_be.global.event.WishTreeEvolvedEvent; import com.example.cp_main_be.global.exception.UserNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.context.ApplicationEventPublisher; @@ -24,17 +23,10 @@ public WishTree addPointsToWishTree(Long userId, Long points) { // 1. 유저의 소망 나무를 찾거나, 없으면 새로 생성 WishTree wishTree = findOrCreateWishTree(userId); - // 2. WishTree 엔티티에 포인트 추가 및 성장 여부 확인 - boolean hasEvolved = wishTree.addPoints(points); - - // 3. 만약 나무가 성장했다면, 추가 로직 실행 - if (hasEvolved) { - // 4. 만약 '나무' 단계로 성장했다면, 새로운 Garden 해금 - if (wishTree.getStage() == WishTreeStage.TREE) { - eventPublisher.publishEvent(new WishTreeEvolvedEvent(userId)); - // notificationService.send(userId, "새로운 텃밭이 열렸어요!"); - } - } + // 2. WishTree 엔티티에 포인트 추가 및 해금 가능 상태로 변경 (내부 로직) + wishTree.addPoints(points); + + // 3. Stage 성장 및 이벤트 발행 로직 모두 제거 return wishTree; } diff --git a/src/main/java/com/example/cp_main_be/domain/social/follow/service/FollowService.java b/src/main/java/com/example/cp_main_be/domain/social/follow/service/FollowService.java index f28e76d4..74043993 100644 --- a/src/main/java/com/example/cp_main_be/domain/social/follow/service/FollowService.java +++ b/src/main/java/com/example/cp_main_be/domain/social/follow/service/FollowService.java @@ -7,6 +7,8 @@ import com.example.cp_main_be.domain.social.follow.domain.Follow; import com.example.cp_main_be.domain.social.follow.domain.repository.FollowRepository; import com.example.cp_main_be.domain.social.follow.dto.FollowResponseDTO; +import com.example.cp_main_be.global.common.CustomApiException; +import com.example.cp_main_be.global.common.ErrorCode; import com.example.cp_main_be.global.exception.UserNotFoundException; import java.util.List; import lombok.RequiredArgsConstructor; @@ -32,6 +34,8 @@ public void followUser(Long followerId, Long followingId) { .findById(followingId) .orElseThrow(() -> new UserNotFoundException("팔로잉할 사용자를 찾을 수 없습니다.")); + if (follower.getId().equals(following.getId())) + throw new CustomApiException(ErrorCode.SELF_FOLLOWING_UNABLE); if (followRepository.existsByFollowerAndFollowing(follower, following)) { throw new RuntimeException("이미 팔로우한 사용자입니다."); // TODO: Custom Exception } @@ -76,8 +80,8 @@ public List getFollowers(Long userId) { .map( member -> FollowResponseDTO.builder() - .username(member.getUsername()) - .userImageUrl(member.getProfileImageUrl()) + .username(member.getNickname()) + .userImageUrl(member.getAvatarList().get(0).getImageUrl()) .userId(member.getId()) .build()) .toList(); @@ -96,8 +100,8 @@ public List getFollowing(Long userId) { .map( member -> FollowResponseDTO.builder() - .username(member.getUsername()) - .userImageUrl(member.getProfileImageUrl()) + .username(member.getNickname()) + .userImageUrl(member.getAvatarList().get(0).getImageUrl()) .userId(member.getId()) .build()) .toList(); diff --git a/src/main/java/com/example/cp_main_be/domain/social/guestbook/presentation/GuestbookController.java b/src/main/java/com/example/cp_main_be/domain/social/guestbook/presentation/GuestbookController.java index d11914c8..78447f57 100644 --- a/src/main/java/com/example/cp_main_be/domain/social/guestbook/presentation/GuestbookController.java +++ b/src/main/java/com/example/cp_main_be/domain/social/guestbook/presentation/GuestbookController.java @@ -34,11 +34,11 @@ public ResponseEntity> createGuestbook( return ResponseEntity.ok(ApiResponse.success(null)); } - @Operation(summary = "방명록 불러오기", description = "방명록의 글을 불러옵니다.") - @GetMapping("/guestbook/list") + @Operation(summary = "방명록 불러오기", description = "방명록의 글을 불러옵니다.userId가 로그인된 유저의것이면 자기것을 조회합니다.") + @GetMapping("/guestbook/{userId}/list") public ResponseEntity>> getMyGuestbookList( - @AuthenticationPrincipal User user) { - List guestbookResponse = guestbookService.getGuestbookList(user); + @PathVariable Long userId, @AuthenticationPrincipal User user) { + List guestbookResponse = guestbookService.getGuestbookList(userId, user); return ResponseEntity.ok(ApiResponse.success(guestbookResponse)); } } diff --git a/src/main/java/com/example/cp_main_be/domain/social/guestbook/service/GuestbookService.java b/src/main/java/com/example/cp_main_be/domain/social/guestbook/service/GuestbookService.java index cebee5e1..a38fe1b3 100644 --- a/src/main/java/com/example/cp_main_be/domain/social/guestbook/service/GuestbookService.java +++ b/src/main/java/com/example/cp_main_be/domain/social/guestbook/service/GuestbookService.java @@ -10,11 +10,14 @@ import com.example.cp_main_be.domain.social.guestbook.domain.repository.GuestbookRepository; import com.example.cp_main_be.domain.social.guestbook.dto.request.GuestbookRequest; import com.example.cp_main_be.domain.social.guestbook.dto.response.GuestbookResponse; +import com.example.cp_main_be.global.common.CustomApiException; +import com.example.cp_main_be.global.common.ErrorCode; import com.example.cp_main_be.global.exception.UserNotFoundException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; +import java.util.Objects; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -62,9 +65,15 @@ public void createGuestbook(Long writerId, Long ownerId, GuestbookRequest reques } } - public List getGuestbookList(User user) { + public List getGuestbookList(Long userId, User user) { List guestbookList = guestbookRepository.findAllByOwner(user); + if (!Objects.equals(userId, user.getId())) + guestbookList = + guestbookRepository.findAllByOwner( + userRepository + .findById(userId) + .orElseThrow(() -> new CustomApiException(ErrorCode.NOT_FOUND))); // 비어있는 리스트에 stream()을 호출해도 예외가 발생하지 않고 비어있는 stream이 반환됩니다. return guestbookList.stream() diff --git a/src/main/java/com/example/cp_main_be/global/common/ErrorCode.java b/src/main/java/com/example/cp_main_be/global/common/ErrorCode.java index 2f2f6cbe..e767dc99 100644 --- a/src/main/java/com/example/cp_main_be/global/common/ErrorCode.java +++ b/src/main/java/com/example/cp_main_be/global/common/ErrorCode.java @@ -24,6 +24,7 @@ public enum ErrorCode { INVALID_MISSION_TYPE_FOR_QUIZ_OPTION( HttpStatus.BAD_REQUEST, "E40010", "퀴즈 타입의 미션에만 선지를 추가할 수 있습니다."), INVALID_REQUEST(HttpStatus.BAD_REQUEST, "E40011", "잘못된 요청입니다."), + SELF_FOLLOWING_UNABLE(HttpStatus.BAD_REQUEST, "E40012", "자신을 팔로우할 수 없습니다."), // 403 Forbidden ACCESS_DENIED(HttpStatus.FORBIDDEN, "E40301", "요청에 대한 권한이 없습니다."),