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 19bb387e..328fb23f 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,5 +1,7 @@ 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; import com.example.cp_main_be.domain.garden.garden.domain.repository.GardenBackgroundRepository; @@ -11,6 +13,9 @@ 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.member.user.service.UserService; +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.time.LocalDateTime; @@ -39,8 +44,10 @@ public class GardenService { private final UserService userService; private final ApplicationEventPublisher eventPublisher; private final GardenBackgroundRepository gardenBackgroundRepository; + private final AvatarRepository avatarRepository; private final UserRepository userRepository; private final FriendWateringLogRepository friendWateringLogRepository; + private final WishTreeRepository wishTreeRepository; public GardenResponse findGardenById(Long gardenId) { Garden garden = @@ -160,21 +167,55 @@ public void unlockNewGardenSlot(Long userId) { User user = userRepository .findById(userId) - .orElseThrow(() -> new CustomApiException(ErrorCode.NOT_FOUND)); + .orElseThrow(() -> new CustomApiException(ErrorCode.USER_NOT_FOUND)); - int currentGardens = user.getGardens().size(); + WishTree wishTree = + wishTreeRepository + .findByUserId(userId) + .orElseThrow(() -> new CustomApiException(ErrorCode.WISH_TREE_NOT_FOUND)); - // 최대 텃밭 개수 제한은 여전히 유효하므로 여기서 검사 - if (currentGardens >= MAX_GARDEN_COUNT) { - // 이미 최대치이므로 조용히 종료하거나 예외를 던질 수 있습니다. - // 여기서는 추가 생성을 막고 그냥 리턴합니다. - throw new CustomApiException(ErrorCode.GARDEN_SLOT_MAXED_OUT); - } + // 1. 사용자의 WishTree 포인트를 기준으로 현재 단계를 결정 + // User는 WishTree를 가지고 있고, WishTree가 포인트를 관리합니다. + WishTreeStage currentStage = WishTreeStage.getStageForPoints(wishTree.getPoints()); - // [기존 레벨 체크 로직 삭제!] + // 2. 현재 단계에서 가질 수 있는 최대 텃밭 개수를 가져옴 + long maxGardensForStage = currentStage.getMaxGardens(); - // TODO: 새로 생성된 텃밭의 기본 Avatar, Background 설정 로직 필요 - Garden newGarden = Garden.builder().user(user).slotNumber(currentGardens + 1).build(); + // 3. 사용자가 현재 보유한 텃밭 개수 확인 + int currentGardenCount = user.getGardens().size(); + + // 4. 최대 텃밭 개수에 도달했는지 확인 + if (currentGardenCount >= maxGardensForStage) { + // 현재 단계에서는 더 이상 텃밭을 생성할 수 없음 + // ErrorCode에 GARDEN_SLOT_LOCKED 추가가 필요할 수 있습니다. + throw new CustomApiException( + ErrorCode.GARDEN_SLOT_LOCKED, "현재 단계에서는 더 이상 텃밭을 만들 수 없습니다. 소원나무를 성장시켜주세요."); + } + + // 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(); gardenRepository.save(newGarden); diff --git a/src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java b/src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java index f06eec04..22c20c44 100644 --- a/src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java +++ b/src/main/java/com/example/cp_main_be/domain/member/user/presentation/UserController.java @@ -1,5 +1,6 @@ package com.example.cp_main_be.domain.member.user.presentation; +import com.example.cp_main_be.domain.garden.garden.domain.Garden; 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.member.user.dto.request.AvatarChangeRequest; @@ -13,6 +14,9 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.websocket.server.PathParam; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -80,6 +84,21 @@ public ResponseEntity> return ResponseEntity.ok(ApiResponse.success(levelStatusResponseDTO)); } + @Operation( + summary = "내 텃밭 ID 목록 조회", + description = "현재 로그인한 유저와 연결된 모든 텃밭의 ID 목록을 조회합니다. 텃밭 슬롯 번호 순으로 정렬됩니다.") + @GetMapping("/me/gardens") + public ResponseEntity>> getMyGardenIds( + @AuthenticationPrincipal User user) { + List gardenIds = + user.getGardens().stream() + .sorted(Comparator.comparing(Garden::getSlotNumber)) + .map(Garden::getId) + .collect(Collectors.toList()); + + return ResponseEntity.ok(ApiResponse.success(gardenIds)); + } + @Operation(summary = "유저 정보 조회", description = "유저 정보를 조회합니다") @GetMapping("/{userId}") public ResponseEntity> getUserInfo( 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 e49f2b90..ffa2b8f4 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 @@ -15,12 +15,15 @@ public enum ErrorCode { HttpStatus.BAD_REQUEST, "E40003", "오늘은 다른 사람의 정원에 더 이상 물을 줄 수 없습니다."), ALREADY_WATERED_GARDEN(HttpStatus.BAD_REQUEST, "E40004", "이 정원에는 오늘 이미 물을 주었습니다."), SUNLIGHT_COOL_DOWN(HttpStatus.BAD_REQUEST, "E40005", "오늘은 이미 햇빛을 주었습니다."), - GARDEN_SLOT_MAXED_OUT(HttpStatus.BAD_REQUEST, "E40006", "더 이상 텃밭을 추가할 수 없습니다."), + GARDEN_SLOT_MAXED_OUT( + HttpStatus.BAD_REQUEST, "E40006", "더 이상 텃밭을 추가할 수 없습니다."), // 최종 3개 도달 시 사용 가능 INVALID_FILE(HttpStatus.BAD_REQUEST, "E40007", "적절하지 않은 파일 내용/포맷입니다."), - FILE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "E40008", "파일 크기 제한을 넘었습니다."), + GARDEN_SLOT_LOCKED( + HttpStatus.BAD_REQUEST, "E40008", "현재 단계에서는 더 이상 텃밭을 만들 수 없습니다. 소원나무를 성장시켜주세요."), + FILE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST, "E40009", "파일 크기 제한을 넘었습니다."), INVALID_MISSION_TYPE_FOR_QUIZ_OPTION( - HttpStatus.BAD_REQUEST, "E40009", "퀴즈 타입의 미션에만 선지를 추가할 수 있습니다."), - INVALID_REQUEST(HttpStatus.BAD_REQUEST, "E40010", "잘못된 요청입니다."), + HttpStatus.BAD_REQUEST, "E40010", "퀴즈 타입의 미션에만 선지를 추가할 수 있습니다."), + INVALID_REQUEST(HttpStatus.BAD_REQUEST, "E40011", "잘못된 요청입니다."), // 403 Forbidden ACCESS_DENIED(HttpStatus.FORBIDDEN, "E40301", "요청에 대한 권한이 없습니다."), @@ -35,6 +38,8 @@ public enum ErrorCode { QUIZ_NOT_FOUND(HttpStatus.NOT_FOUND, "E40406", "해당 퀴즈를 찾을 수 없습니다."), REPORT_NOT_FOUND(HttpStatus.NOT_FOUND, "E40407", "해당 신고를 찾을 수 없습니다."), AVATAR_MASTER_NOT_FOUND(HttpStatus.NOT_FOUND, "E40408", "해당 아바타 원본을 찾을 수 없습니다."), + WISH_TREE_NOT_FOUND(HttpStatus.NOT_FOUND, "E40409", "소원나무 정보를 찾을 수 없습니다."), + DEFAULT_RESOURCE_NOT_FOUND(HttpStatus.NOT_FOUND, "E40410", "필수 기본 리소스를 찾을 수 없습니다."), // 417 Expectation Failed UPLOAD_FAILED(HttpStatus.EXPECTATION_FAILED, "E41701", "파일 업로드에 실패했습니다."), diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 2b3cf671..e9aa560e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -55,7 +55,7 @@ spring: password: jpa: hibernate: - ddl-auto: create-drop + ddl-auto: update defer-datasource-initialization: true h2: console: