diff --git a/src/main/java/com/wayble/server/review/controller/ReviewController.java b/src/main/java/com/wayble/server/review/controller/ReviewController.java index 4c493c15..2fde91d2 100644 --- a/src/main/java/com/wayble/server/review/controller/ReviewController.java +++ b/src/main/java/com/wayble/server/review/controller/ReviewController.java @@ -38,7 +38,9 @@ public class ReviewController { public CommonResponse registerReview( @PathVariable Long waybleZoneId, @RequestBody @Valid ReviewRegisterDto dto, - @RequestHeader(value = "Authorization", required = false) String authorizationHeader // 테스트를 위해 임시로 토큰 없이도 접근하도록 설정 + + // TODO: 로그인 구현 후 Authorization 헤더 필수로 변경 필요 + @RequestHeader(value = "Authorization", required = false) String authorizationHeader ) { reviewService.registerReview(waybleZoneId, dto, authorizationHeader); return CommonResponse.success("리뷰가 등록되었습니다."); diff --git a/src/main/java/com/wayble/server/user/controller/UserPlaceController.java b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java new file mode 100644 index 00000000..0fb80e43 --- /dev/null +++ b/src/main/java/com/wayble/server/user/controller/UserPlaceController.java @@ -0,0 +1,44 @@ +package com.wayble.server.user.controller; + +import com.wayble.server.common.exception.ApplicationException; +import com.wayble.server.common.response.CommonResponse; +import com.wayble.server.user.dto.UserPlaceRequestDto; +import com.wayble.server.user.exception.UserErrorCase; +import com.wayble.server.user.service.UserPlaceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/users/{userId}/places") +@RequiredArgsConstructor +public class UserPlaceController { + + private final UserPlaceService userPlaceService; + + @PostMapping + @Operation(summary = "유저 장소 저장", description = "유저가 웨이블존을 장소로 저장합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "장소 저장 성공"), + @ApiResponse(responseCode = "400", description = "이미 저장한 장소입니다."), + @ApiResponse(responseCode = "404", description = "해당 유저 또는 웨이블존이 존재하지 않음") + }) + public CommonResponse saveUserPlace( + @PathVariable Long userId, + @RequestBody @Valid UserPlaceRequestDto request, + + // TODO: 로그인 구현 후 Authorization 헤더 필수로 변경 필요 + @RequestHeader(value = "Authorization", required = false) String authorizationHeader + ) { + // Path variable과 request body의 userId 일치 여부 확인 + if (!userId.equals(request.userId())) { + throw new ApplicationException(UserErrorCase.INVALID_USER_ID); + } + + userPlaceService.saveUserPlace(request); + return CommonResponse.success("장소가 저장되었습니다."); + } +} diff --git a/src/main/java/com/wayble/server/user/dto/UserPlaceRequestDto.java b/src/main/java/com/wayble/server/user/dto/UserPlaceRequestDto.java new file mode 100644 index 00000000..5285ab76 --- /dev/null +++ b/src/main/java/com/wayble/server/user/dto/UserPlaceRequestDto.java @@ -0,0 +1,9 @@ +package com.wayble.server.user.dto; + +import jakarta.validation.constraints.NotNull; + +public record UserPlaceRequestDto( + @NotNull Long userId, + @NotNull Long waybleZoneId, + @NotNull String title +) {} diff --git a/src/main/java/com/wayble/server/user/entity/User.java b/src/main/java/com/wayble/server/user/entity/User.java index 009f8280..d150505a 100644 --- a/src/main/java/com/wayble/server/user/entity/User.java +++ b/src/main/java/com/wayble/server/user/entity/User.java @@ -58,5 +58,6 @@ public class User extends BaseEntity { // TODO 프로필 이미지 관련 작업 필요 - // TODO 내가 저장한 장소 관련 작업 필요 + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) + private List userPlaces = new ArrayList<>(); } diff --git a/src/main/java/com/wayble/server/user/exception/UserErrorCase.java b/src/main/java/com/wayble/server/user/exception/UserErrorCase.java index 24b7bd15..0bb69bf3 100644 --- a/src/main/java/com/wayble/server/user/exception/UserErrorCase.java +++ b/src/main/java/com/wayble/server/user/exception/UserErrorCase.java @@ -8,7 +8,10 @@ @RequiredArgsConstructor public enum UserErrorCase implements ErrorCase { - USER_NOT_FOUND(400, 1001, "사용자를 찾을 수 없습니다."); + USER_NOT_FOUND(404, 1001, "사용자를 찾을 수 없습니다."), + WAYBLE_ZONE_NOT_FOUND(404, 1002, "해당 웨이블존을 찾을 수 없습니다."), + PLACE_ALREADY_SAVED(400, 1003, "이미 저장한 장소입니다."), + INVALID_USER_ID(400, 1004, "요청 경로의 유저 ID와 바디의 유저 ID가 일치하지 않습니다."); private final Integer httpStatusCode; private final Integer errorCode; diff --git a/src/main/java/com/wayble/server/user/repository/UserPlaceRepository.java b/src/main/java/com/wayble/server/user/repository/UserPlaceRepository.java new file mode 100644 index 00000000..8c14f4be --- /dev/null +++ b/src/main/java/com/wayble/server/user/repository/UserPlaceRepository.java @@ -0,0 +1,10 @@ +package com.wayble.server.user.repository; + +import com.wayble.server.user.entity.UserPlace; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserPlaceRepository extends JpaRepository { + Optional findByUser_IdAndTitle(Long userId, String title); +} diff --git a/src/main/java/com/wayble/server/user/repository/UserPlaceWaybleZoneMappingRepository.java b/src/main/java/com/wayble/server/user/repository/UserPlaceWaybleZoneMappingRepository.java new file mode 100644 index 00000000..763c2c8a --- /dev/null +++ b/src/main/java/com/wayble/server/user/repository/UserPlaceWaybleZoneMappingRepository.java @@ -0,0 +1,8 @@ +package com.wayble.server.user.repository; + +import com.wayble.server.user.entity.UserPlaceWaybleZoneMapping; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserPlaceWaybleZoneMappingRepository extends JpaRepository { + boolean existsByUserPlace_User_IdAndWaybleZone_Id(Long userId, Long zoneId); +} diff --git a/src/main/java/com/wayble/server/user/service/UserPlaceService.java b/src/main/java/com/wayble/server/user/service/UserPlaceService.java new file mode 100644 index 00000000..55539022 --- /dev/null +++ b/src/main/java/com/wayble/server/user/service/UserPlaceService.java @@ -0,0 +1,59 @@ +package com.wayble.server.user.service; + + +import com.wayble.server.common.exception.ApplicationException; +import com.wayble.server.user.dto.UserPlaceRequestDto; +import com.wayble.server.user.entity.User; +import com.wayble.server.user.entity.UserPlace; +import com.wayble.server.user.entity.UserPlaceWaybleZoneMapping; +import com.wayble.server.user.exception.UserErrorCase; +import com.wayble.server.user.repository.UserPlaceRepository; +import com.wayble.server.user.repository.UserPlaceWaybleZoneMappingRepository; +import com.wayble.server.user.repository.UserRepository; +import com.wayble.server.wayblezone.entity.WaybleZone; +import com.wayble.server.wayblezone.repository.WaybleZoneRepository; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class UserPlaceService { + + private final UserRepository userRepository; + private final WaybleZoneRepository waybleZoneRepository; + private final UserPlaceRepository userPlaceRepository; + private final UserPlaceWaybleZoneMappingRepository mappingRepository; + + @Transactional + public void saveUserPlace(UserPlaceRequestDto request) { + // 유저 존재 확인 + User user = userRepository.findById(request.userId()) + .orElseThrow(() -> new ApplicationException(UserErrorCase.USER_NOT_FOUND)); + + // 웨이블존 존재 확인 + WaybleZone waybleZone = waybleZoneRepository.findById(request.waybleZoneId()) + .orElseThrow(() -> new ApplicationException(UserErrorCase.WAYBLE_ZONE_NOT_FOUND)); + + // 중복 저장 확인 + boolean alreadySaved = mappingRepository.existsByUserPlace_User_IdAndWaybleZone_Id(request.userId(), request.waybleZoneId()); + if (alreadySaved) { + throw new ApplicationException(UserErrorCase.PLACE_ALREADY_SAVED); + } + + // 저장 + UserPlace userPlace = userPlaceRepository.save( + UserPlace.builder() + .title(request.title()) + .user(user) + .build() + ); + + mappingRepository.save( + UserPlaceWaybleZoneMapping.builder() + .userPlace(userPlace) + .waybleZone(waybleZone) + .build() + ); + } +} \ No newline at end of file