From 7372d19a84374b16515e2ba4a11f39f2ef772594 Mon Sep 17 00:00:00 2001 From: Juri-Jang Date: Mon, 24 Nov 2025 13:08:17 +0900 Subject: [PATCH 1/6] =?UTF-8?q?refactor/=EC=84=B1=EA=B3=B5=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umc9th/domain/review/controller/ReviewController.java | 2 +- .../umc9th/global/apiPayload/code/SuccessCode.java | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java b/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java index 1826bbf..74c09aa 100644 --- a/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java +++ b/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java @@ -26,6 +26,6 @@ public ResponseEntity> searchReviews( @RequestParam String query ) { ReviewSearchResDto result = reviewQueryService.searchReview(type, query); - return ResponseEntity.ok(ApiResponse.success(SuccessCode.REVIEW_SEARCHED, result)); + return ResponseEntity.ok(ApiResponse.success(SuccessCode.READ_SUCCESS, result)); } } diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java index 06ae2cb..33d4abf 100644 --- a/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java @@ -13,13 +13,7 @@ public enum SuccessCode { CREATED("요청이 성공적으로 처리되었으며, 리소스가 생성되었습니다."), READ_SUCCESS("데이터 조회에 성공했습니다."), UPDATE_SUCCESS("데이터 수정에 성공했습니다."), - DELETE_SUCCESS("데이터 삭제에 성공했습니다."), - - /** 리뷰 성공 코드 */ - REVIEW_CREATED("리뷰가 성공적으로 등록되었습니다."), - REVIEW_UPDATED("리뷰가 수정되었습니다."), - REVIEW_DELETED("리뷰가 삭제되었습니다."), - REVIEW_SEARCHED("리뷰 검색이 성공적으로 완료되었습니다.") + DELETE_SUCCESS("데이터 삭제에 성공했습니다.") ; private final String message; From 8e21271fb2cfd070713e58d96b28b13415a0bf5d Mon Sep 17 00:00:00 2001 From: Juri-Jang Date: Mon, 24 Nov 2025 13:45:41 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor/=EA=B3=B5=ED=86=B5=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20=EB=B0=8F?= =?UTF-8?q?=20=EC=84=B1=EA=B3=B5,=20=EC=9D=91=EB=8B=B5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umc9th/global/apiPayload/ApiResponse.java | 10 +++-- .../global/apiPayload/code/BaseErrorCode.java | 10 +++++ .../apiPayload/code/BaseSuccessCode.java | 9 +++++ .../apiPayload/code/GeneralErrorCode.java | 38 ++++++++++++++++++ .../global/apiPayload/code/SuccessCode.java | 32 +++++++++++---- .../exception/GeneralException.java | 12 ++++++ .../handler/GeneralExceptionAdvice.java | 39 +++++++++++++++++++ 7 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/example/umc9th/global/apiPayload/code/BaseErrorCode.java create mode 100644 src/main/java/com/example/umc9th/global/apiPayload/code/BaseSuccessCode.java create mode 100644 src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java create mode 100644 src/main/java/com/example/umc9th/global/apiPayload/exception/GeneralException.java create mode 100644 src/main/java/com/example/umc9th/global/apiPayload/handler/GeneralExceptionAdvice.java diff --git a/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java b/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java index ad3387c..7569e5f 100644 --- a/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java +++ b/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java @@ -1,5 +1,6 @@ package com.example.umc9th.global.apiPayload; +import com.example.umc9th.global.apiPayload.code.BaseErrorCode; import com.example.umc9th.global.apiPayload.code.SuccessCode; import lombok.AllArgsConstructor; import lombok.Getter; @@ -8,14 +9,15 @@ @AllArgsConstructor public class ApiResponse { private boolean success; + private String code; private String message; private T data; - public static ApiResponse success(SuccessCode code, T data) { - return new ApiResponse<>(true, code.getMessage(), data); + public static ApiResponse onSuccess(SuccessCode code) { + return new ApiResponse<>(true, code.getCode(), code.getMessage(), null); } - public static ApiResponse error(String message) { - return new ApiResponse<>(false, message, null); + public static ApiResponse onFailure(BaseErrorCode code) { + return new ApiResponse<>(false, code.getCode(), code.getMessage(), null); } } \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/BaseErrorCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/BaseErrorCode.java new file mode 100644 index 0000000..d0d553f --- /dev/null +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/BaseErrorCode.java @@ -0,0 +1,10 @@ +package com.example.umc9th.global.apiPayload.code; + +import org.springframework.http.HttpStatus; + +public interface BaseErrorCode { + + HttpStatus getStatus(); + String getCode(); + String getMessage(); +} diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/BaseSuccessCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/BaseSuccessCode.java new file mode 100644 index 0000000..19d1b22 --- /dev/null +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/BaseSuccessCode.java @@ -0,0 +1,9 @@ +package com.example.umc9th.global.apiPayload.code; + +import org.springframework.http.HttpStatus; + +public interface BaseSuccessCode { + HttpStatus getStatus(); + String getCode(); + String getMessage(); +} diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java new file mode 100644 index 0000000..8dd47e1 --- /dev/null +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java @@ -0,0 +1,38 @@ +package com.example.umc9th.global.apiPayload.code; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum GeneralErrorCode implements BaseErrorCode{ + + BAD_REQUEST(HttpStatus.BAD_REQUEST, + "COMMON400_1", + "잘못된 요청입니다."), + UNAUTHORIZED(HttpStatus.UNAUTHORIZED, + "AUTH401_1", + "인증이 필요합니다."), + FORBIDDEN(HttpStatus.FORBIDDEN, + "AUTH403_1", + "요청이 거부되었습니다."), + NOT_FOUND(HttpStatus.NOT_FOUND, + "COMMON404_1", + "요청한 리소스를 찾을 수 없습니다."), + + // user 관련 + USER_NOT_FOUND(HttpStatus.NOT_FOUND, + "USER404_1", + "존재하지 않는 사용자입니다."), + + + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, + "COMMON500_1", + "예기치 않은 서버 에러가 발생했습니다."), + ; + + private final HttpStatus status; + private final String code; + private final String message; +} \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java index 33d4abf..7deeda2 100644 --- a/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/SuccessCode.java @@ -1,20 +1,36 @@ package com.example.umc9th.global.apiPayload.code; - import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor -public enum SuccessCode { +public enum SuccessCode implements BaseSuccessCode { - /** 공통 성공 코드 */ - SUCCESS("요청이 성공적으로 처리되었습니다."), - CREATED("요청이 성공적으로 처리되었으며, 리소스가 생성되었습니다."), - READ_SUCCESS("데이터 조회에 성공했습니다."), - UPDATE_SUCCESS("데이터 수정에 성공했습니다."), - DELETE_SUCCESS("데이터 삭제에 성공했습니다.") + _OK(HttpStatus.OK, "COMMON_200", "요청이 성공적으로 처리되었습니다."), + _CREATED(HttpStatus.CREATED, "COMMON_201", "요청이 성공적으로 처리되었으며, 리소스가 생성되었습니다."), + READ_SUCCESS(HttpStatus.OK, "COMMON_200", "데이터 조회에 성공했습니다."), + UPDATE_SUCCESS(HttpStatus.OK, "COMMON_200", "데이터 수정에 성공했습니다."), + DELETE_SUCCESS(HttpStatus.OK, "COMMON_200", "데이터 삭제에 성공했습니다.") ; + private final HttpStatus status; + private final String code; private final String message; + + @Override + public HttpStatus getStatus() { + return status; + } + + @Override + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } } \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/global/apiPayload/exception/GeneralException.java b/src/main/java/com/example/umc9th/global/apiPayload/exception/GeneralException.java new file mode 100644 index 0000000..bd7517f --- /dev/null +++ b/src/main/java/com/example/umc9th/global/apiPayload/exception/GeneralException.java @@ -0,0 +1,12 @@ +package com.example.umc9th.global.apiPayload.exception; + +import com.example.umc9th.global.apiPayload.code.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class GeneralException extends RuntimeException { + + private final BaseErrorCode code; +} diff --git a/src/main/java/com/example/umc9th/global/apiPayload/handler/GeneralExceptionAdvice.java b/src/main/java/com/example/umc9th/global/apiPayload/handler/GeneralExceptionAdvice.java new file mode 100644 index 0000000..779ea69 --- /dev/null +++ b/src/main/java/com/example/umc9th/global/apiPayload/handler/GeneralExceptionAdvice.java @@ -0,0 +1,39 @@ +package com.example.umc9th.global.apiPayload.handler; + +import com.example.umc9th.global.apiPayload.ApiResponse; +import com.example.umc9th.global.apiPayload.code.BaseErrorCode; +import com.example.umc9th.global.apiPayload.code.GeneralErrorCode; +import com.example.umc9th.global.apiPayload.exception.GeneralException; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GeneralExceptionAdvice { + + // 애플리케이션에서 발생하는 커스텀 예외를 처리 + @ExceptionHandler(GeneralException.class) + public ResponseEntity> handleException( + GeneralException ex + ) { + return ResponseEntity.status(ex.getCode().getStatus()) + .body(ApiResponse.onFailure( + ex.getCode() + ) + ); + } + + // 그 외의 정의되지 않은 모든 예외 처리 + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException( + Exception ex + ) { + BaseErrorCode code = GeneralErrorCode.INTERNAL_SERVER_ERROR; + + return ResponseEntity.status(code.getStatus()) + .body(ApiResponse.onFailure( + code + ) + ); + } +} \ No newline at end of file From 7c04d9d610b1a340f93354c5b0f1d15e6c8fff99 Mon Sep 17 00:00:00 2001 From: Juri-Jang Date: Mon, 24 Nov 2025 14:22:27 +0900 Subject: [PATCH 3/6] =?UTF-8?q?mission/#8=20=EA=B0=80=EA=B2=8C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/RestaurantController.java | 31 ++++++++++++++++++ .../converter/RestaurantConverter.java | 17 ++++++++++ .../restaurants/dto/RestaurantReqDto.java | 13 ++++++++ .../repository/LocationRepository.java | 7 ++++ .../repository/RestaurantRepository.java | 7 ++++ .../service/RestaurantService.java | 32 +++++++++++++++++++ .../umc9th/global/apiPayload/ApiResponse.java | 4 +++ .../apiPayload/code/GeneralErrorCode.java | 10 ++++++ 8 files changed, 121 insertions(+) create mode 100644 src/main/java/com/example/umc9th/domain/restaurants/controller/RestaurantController.java create mode 100644 src/main/java/com/example/umc9th/domain/restaurants/converter/RestaurantConverter.java create mode 100644 src/main/java/com/example/umc9th/domain/restaurants/dto/RestaurantReqDto.java create mode 100644 src/main/java/com/example/umc9th/domain/restaurants/repository/LocationRepository.java create mode 100644 src/main/java/com/example/umc9th/domain/restaurants/repository/RestaurantRepository.java create mode 100644 src/main/java/com/example/umc9th/domain/restaurants/service/RestaurantService.java diff --git a/src/main/java/com/example/umc9th/domain/restaurants/controller/RestaurantController.java b/src/main/java/com/example/umc9th/domain/restaurants/controller/RestaurantController.java new file mode 100644 index 0000000..a957e5c --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/restaurants/controller/RestaurantController.java @@ -0,0 +1,31 @@ +package com.example.umc9th.domain.restaurants.controller; + +import com.example.umc9th.domain.restaurants.dto.RestaurantReqDto; +import com.example.umc9th.domain.restaurants.entity.Restaurant; +import com.example.umc9th.domain.restaurants.service.RestaurantService; +import com.example.umc9th.global.apiPayload.ApiResponse; +import com.example.umc9th.global.apiPayload.code.SuccessCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/restaurants") +@RequiredArgsConstructor +public class RestaurantController { + private final RestaurantService restaurantService; + + @PostMapping + public ResponseEntity> createRestaurant( + @RequestBody RestaurantReqDto request + ) { + Restaurant newRestaurant = restaurantService.createRestaurant(request); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(ApiResponse.onSuccess(SuccessCode._CREATED, newRestaurant.getId())); + } +} diff --git a/src/main/java/com/example/umc9th/domain/restaurants/converter/RestaurantConverter.java b/src/main/java/com/example/umc9th/domain/restaurants/converter/RestaurantConverter.java new file mode 100644 index 0000000..0e1cb80 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/restaurants/converter/RestaurantConverter.java @@ -0,0 +1,17 @@ +package com.example.umc9th.domain.restaurants.converter; + +import com.example.umc9th.domain.restaurants.dto.RestaurantReqDto; +import com.example.umc9th.domain.restaurants.entity.Location; +import com.example.umc9th.domain.restaurants.entity.Restaurant; + +public class RestaurantConverter { + + public static Restaurant toRestaurant(RestaurantReqDto reqDto, Location location) { + return Restaurant.builder() + .name(reqDto.getName()) + .detailAddress(reqDto.getDetailAddress()) + .category(reqDto.getCategory()) + .location(location) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/domain/restaurants/dto/RestaurantReqDto.java b/src/main/java/com/example/umc9th/domain/restaurants/dto/RestaurantReqDto.java new file mode 100644 index 0000000..bd23a2c --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/restaurants/dto/RestaurantReqDto.java @@ -0,0 +1,13 @@ +package com.example.umc9th.domain.restaurants.dto; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class RestaurantReqDto { + private String name; + private String detailAddress; + private String category; + private Long locationId; +} \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/domain/restaurants/repository/LocationRepository.java b/src/main/java/com/example/umc9th/domain/restaurants/repository/LocationRepository.java new file mode 100644 index 0000000..a00edd4 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/restaurants/repository/LocationRepository.java @@ -0,0 +1,7 @@ +package com.example.umc9th.domain.restaurants.repository; + +import com.example.umc9th.domain.restaurants.entity.Location; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface LocationRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/umc9th/domain/restaurants/repository/RestaurantRepository.java b/src/main/java/com/example/umc9th/domain/restaurants/repository/RestaurantRepository.java new file mode 100644 index 0000000..0d6f9ed --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/restaurants/repository/RestaurantRepository.java @@ -0,0 +1,7 @@ +package com.example.umc9th.domain.restaurants.repository; + +import com.example.umc9th.domain.restaurants.entity.Restaurant; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RestaurantRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/umc9th/domain/restaurants/service/RestaurantService.java b/src/main/java/com/example/umc9th/domain/restaurants/service/RestaurantService.java new file mode 100644 index 0000000..27fdd2d --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/restaurants/service/RestaurantService.java @@ -0,0 +1,32 @@ +package com.example.umc9th.domain.restaurants.service; + +import com.example.umc9th.domain.restaurants.converter.RestaurantConverter; +import com.example.umc9th.domain.restaurants.dto.RestaurantReqDto; +import com.example.umc9th.domain.restaurants.entity.Location; +import com.example.umc9th.domain.restaurants.entity.Restaurant; +import com.example.umc9th.domain.restaurants.repository.LocationRepository; +import com.example.umc9th.domain.restaurants.repository.RestaurantRepository; +import com.example.umc9th.global.apiPayload.code.GeneralErrorCode; +import com.example.umc9th.global.apiPayload.exception.GeneralException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class RestaurantService { + + private final RestaurantRepository restaurantRepository; + private final LocationRepository locationRepository; + + @Transactional + public Restaurant createRestaurant(RestaurantReqDto reqDto) { + Location location = locationRepository.findById(reqDto.getLocationId()) + .orElseThrow(() -> new GeneralException(GeneralErrorCode.NOT_FOUND)); + + Restaurant restaurant = RestaurantConverter.toRestaurant(reqDto, location); + + return restaurantRepository.save(restaurant); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java b/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java index 7569e5f..1a32528 100644 --- a/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java +++ b/src/main/java/com/example/umc9th/global/apiPayload/ApiResponse.java @@ -13,6 +13,10 @@ public class ApiResponse { private String message; private T data; + public static ApiResponse onSuccess(SuccessCode code, T data) { + return new ApiResponse<>(true, code.getCode(), code.getMessage(), data); + } + public static ApiResponse onSuccess(SuccessCode code) { return new ApiResponse<>(true, code.getCode(), code.getMessage(), null); } diff --git a/src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java b/src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java index 8dd47e1..7ca0f97 100644 --- a/src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java +++ b/src/main/java/com/example/umc9th/global/apiPayload/code/GeneralErrorCode.java @@ -26,6 +26,16 @@ public enum GeneralErrorCode implements BaseErrorCode{ "USER404_1", "존재하지 않는 사용자입니다."), + // restaurant 관련 + RESTAURANT_NOT_FOUND(HttpStatus.NOT_FOUND, + "RESTAURANT404_1", + "존재하지 않는 음식점입니다."), + + // location 관련 + LOCATION_NOT_FOUND(HttpStatus.NOT_FOUND, + "LOCATION404_1", + "존재하지 않는 지역입니다."), + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500_1", From e24310575b3e9ad4ea242a3f400f3a1a5fec31e0 Mon Sep 17 00:00:00 2001 From: Juri-Jang Date: Mon, 24 Nov 2025 14:26:34 +0900 Subject: [PATCH 4/6] =?UTF-8?q?mission/#8=20=EB=A6=AC=EB=B7=B0=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../review/controller/ReviewController.java | 23 ++++++++----- .../review/converter/ReviewConverter.java | 13 ++++++++ .../domain/review/dto/ReviewCreateReqDto.java | 15 +++++++++ .../domain/review/service/ReviewService.java | 32 ++++++++++++++----- 4 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/example/umc9th/domain/review/dto/ReviewCreateReqDto.java diff --git a/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java b/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java index 74c09aa..b76b4af 100644 --- a/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java +++ b/src/main/java/com/example/umc9th/domain/review/controller/ReviewController.java @@ -1,24 +1,21 @@ package com.example.umc9th.domain.review.controller; +import com.example.umc9th.domain.review.dto.ReviewCreateReqDto; import com.example.umc9th.domain.review.dto.ReviewSearchResDto; -import com.example.umc9th.domain.review.entity.Review; import com.example.umc9th.domain.review.service.ReviewQueryService; +import com.example.umc9th.domain.review.service.ReviewService; import com.example.umc9th.global.apiPayload.ApiResponse; import com.example.umc9th.global.apiPayload.code.SuccessCode; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; +import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/reviews") @RequiredArgsConstructor public class ReviewController { private final ReviewQueryService reviewQueryService; + private final ReviewService reviewService; @GetMapping("/search") public ResponseEntity> searchReviews( @@ -26,6 +23,16 @@ public ResponseEntity> searchReviews( @RequestParam String query ) { ReviewSearchResDto result = reviewQueryService.searchReview(type, query); - return ResponseEntity.ok(ApiResponse.success(SuccessCode.READ_SUCCESS, result)); + return ResponseEntity.ok(ApiResponse.onSuccess(SuccessCode.READ_SUCCESS, result)); + } + + @PostMapping() + public ResponseEntity> createReview(@RequestBody ReviewCreateReqDto req) { + long userId = 1; //임시 유저 + reviewService.createReview(req, userId); + + return ResponseEntity.ok(ApiResponse.onSuccess(SuccessCode._CREATED)); } + + } diff --git a/src/main/java/com/example/umc9th/domain/review/converter/ReviewConverter.java b/src/main/java/com/example/umc9th/domain/review/converter/ReviewConverter.java index a566bab..97f28e5 100644 --- a/src/main/java/com/example/umc9th/domain/review/converter/ReviewConverter.java +++ b/src/main/java/com/example/umc9th/domain/review/converter/ReviewConverter.java @@ -1,9 +1,12 @@ package com.example.umc9th.domain.review.converter; +import com.example.umc9th.domain.restaurants.entity.Restaurant; +import com.example.umc9th.domain.review.dto.ReviewCreateReqDto; import com.example.umc9th.domain.review.dto.ReviewDetailDto; import com.example.umc9th.domain.review.dto.ReviewSearchResDto; import com.example.umc9th.domain.review.entity.Review; import com.example.umc9th.domain.review.entity.mapping.ReviewImage; +import com.example.umc9th.domain.user.entity.User; import java.util.List; @@ -50,4 +53,14 @@ private static List extractImageUrls(List images) { .map(ReviewImage::getImgUrl) .toList(); } + + /** ReviewCreateReqDto → Review */ + public static Review toReview(ReviewCreateReqDto reqDto, User user, Restaurant restaurant) { + return Review.builder() + .user(user) + .rating(reqDto.getRating()) + .content(reqDto.getContent()) + .restaurant(restaurant) + .build(); + } } diff --git a/src/main/java/com/example/umc9th/domain/review/dto/ReviewCreateReqDto.java b/src/main/java/com/example/umc9th/domain/review/dto/ReviewCreateReqDto.java new file mode 100644 index 0000000..d454be8 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/review/dto/ReviewCreateReqDto.java @@ -0,0 +1,15 @@ +package com.example.umc9th.domain.review.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Builder +@Getter +public class ReviewCreateReqDto { + private long restaurantId; + private String content; + private float rating; + private List imgUrl; +} diff --git a/src/main/java/com/example/umc9th/domain/review/service/ReviewService.java b/src/main/java/com/example/umc9th/domain/review/service/ReviewService.java index c83c309..462e2df 100644 --- a/src/main/java/com/example/umc9th/domain/review/service/ReviewService.java +++ b/src/main/java/com/example/umc9th/domain/review/service/ReviewService.java @@ -1,25 +1,41 @@ package com.example.umc9th.domain.review.service; import com.example.umc9th.domain.restaurants.entity.Restaurant; +import com.example.umc9th.domain.restaurants.repository.RestaurantRepository; +import com.example.umc9th.domain.review.converter.ReviewConverter; +import com.example.umc9th.domain.review.dto.ReviewCreateReqDto; import com.example.umc9th.domain.review.entity.Review; import com.example.umc9th.domain.review.repository.ReviewRepository; import com.example.umc9th.domain.user.entity.User; +import com.example.umc9th.domain.user.repository.UserRepository; +import com.example.umc9th.global.apiPayload.code.GeneralErrorCode; +import com.example.umc9th.global.apiPayload.exception.GeneralException; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service +@RequiredArgsConstructor public class ReviewService { - private ReviewRepository reviewRepository; + private final ReviewRepository reviewRepository; + private final UserRepository userRepository; + private final RestaurantRepository restaurantRepository; - public void createReview(Restaurant restaurant, User user, int rating, String content){ + @Transactional + public void createReview(ReviewCreateReqDto reqDto, long userId){ + // User 엔티티 조회 + User user = userRepository.findById(userId) + .orElseThrow(() -> new GeneralException(GeneralErrorCode.USER_NOT_FOUND)); - Review review = Review.builder() - .user(user) - .rating(rating) - .content(content) - .restaurant(restaurant) - .build(); + // Restaurant 엔티티 조회 + Restaurant restaurant = restaurantRepository.findById(reqDto.getRestaurantId()) + .orElseThrow(() -> new GeneralException(GeneralErrorCode.NOT_FOUND)); // RESTAURANT_NOT_FOUND가 없으면 NOT_FOUND 사용 + // Converter를 사용하여 Review + Review review = ReviewConverter.toReview(reqDto, user, restaurant); + + // 4. 저장 reviewRepository.save(review); } } From 789a699ff684cc82d8feb2c9c10f8575183da9a0 Mon Sep 17 00:00:00 2001 From: Juri-Jang Date: Mon, 24 Nov 2025 14:38:34 +0900 Subject: [PATCH 5/6] =?UTF-8?q?mission/#8=20=EB=AF=B8=EC=85=98=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mission/controller/MissionController.java | 32 +++++++++++++++++++ .../mission/converter/MissionConverter.java | 18 +++++++++++ .../domain/mission/dto/MissionReqDto.java | 14 ++++++++ .../mission/repository/MissionRepository.java | 7 ++++ .../mission/service/MissionService.java | 32 +++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java create mode 100644 src/main/java/com/example/umc9th/domain/mission/converter/MissionConverter.java create mode 100644 src/main/java/com/example/umc9th/domain/mission/dto/MissionReqDto.java create mode 100644 src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java create mode 100644 src/main/java/com/example/umc9th/domain/mission/service/MissionService.java diff --git a/src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java b/src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java new file mode 100644 index 0000000..8a1bfe3 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/mission/controller/MissionController.java @@ -0,0 +1,32 @@ +package com.example.umc9th.domain.mission.controller; + +import com.example.umc9th.domain.mission.dto.MissionReqDto; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.domain.mission.service.MissionService; +import com.example.umc9th.global.apiPayload.ApiResponse; +import com.example.umc9th.global.apiPayload.code.SuccessCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/missions") +@RequiredArgsConstructor +public class MissionController { + + private final MissionService missionService; + + @PostMapping + public ResponseEntity> createMission( + @RequestBody MissionReqDto request + ) { + Mission newMission = missionService.createMission(request); + + return ResponseEntity.status(HttpStatus.CREATED) + .body(ApiResponse.onSuccess(SuccessCode._CREATED, newMission.getId())); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/domain/mission/converter/MissionConverter.java b/src/main/java/com/example/umc9th/domain/mission/converter/MissionConverter.java new file mode 100644 index 0000000..076115a --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/mission/converter/MissionConverter.java @@ -0,0 +1,18 @@ +package com.example.umc9th.domain.mission.converter; + +import com.example.umc9th.domain.mission.dto.MissionReqDto; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.domain.restaurants.entity.Restaurant; + +public class MissionConverter { + + public static Mission toMission(MissionReqDto reqDto, Restaurant restaurant) { + return Mission.builder() + .deadline(reqDto.getDeadline()) + .content(reqDto.getContent()) + .point(reqDto.getPoint()) + .restaurant(restaurant) + .build(); + } +} + diff --git a/src/main/java/com/example/umc9th/domain/mission/dto/MissionReqDto.java b/src/main/java/com/example/umc9th/domain/mission/dto/MissionReqDto.java new file mode 100644 index 0000000..afce2eb --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/mission/dto/MissionReqDto.java @@ -0,0 +1,14 @@ +package com.example.umc9th.domain.mission.dto; + +import lombok.Builder; +import lombok.Getter; +import java.util.Date; + +@Builder +@Getter +public class MissionReqDto { + private Long restaurantId; + private Date deadline; + private String content; + private float point; +} diff --git a/src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java b/src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java new file mode 100644 index 0000000..b03054f --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/mission/repository/MissionRepository.java @@ -0,0 +1,7 @@ +package com.example.umc9th.domain.mission.repository; + +import com.example.umc9th.domain.mission.entity.Mission; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MissionRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/umc9th/domain/mission/service/MissionService.java b/src/main/java/com/example/umc9th/domain/mission/service/MissionService.java new file mode 100644 index 0000000..539aecf --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/mission/service/MissionService.java @@ -0,0 +1,32 @@ +package com.example.umc9th.domain.mission.service; + +import com.example.umc9th.domain.mission.converter.MissionConverter; +import com.example.umc9th.domain.mission.dto.MissionReqDto; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.domain.mission.repository.MissionRepository; +import com.example.umc9th.domain.restaurants.entity.Restaurant; +import com.example.umc9th.domain.restaurants.repository.RestaurantRepository; +import com.example.umc9th.global.apiPayload.code.GeneralErrorCode; +import com.example.umc9th.global.apiPayload.exception.GeneralException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MissionService { + + private final MissionRepository missionRepository; + private final RestaurantRepository restaurantRepository; + + @Transactional + public Mission createMission(MissionReqDto reqDto) { + Restaurant restaurant = restaurantRepository.findById(reqDto.getRestaurantId()) + .orElseThrow(() -> new GeneralException(GeneralErrorCode.RESTAURANT_NOT_FOUND)); + + Mission mission = MissionConverter.toMission(reqDto, restaurant); + + return missionRepository.save(mission); + } +} \ No newline at end of file From ccdbb88648e26c9ae5edae1239ff089cbf831ca0 Mon Sep 17 00:00:00 2001 From: Juri-Jang Date: Mon, 24 Nov 2025 15:09:00 +0900 Subject: [PATCH 6/6] =?UTF-8?q?mission/#8=20=EB=8F=84=EC=A0=84=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 26 +++++++++++++++++ .../user/converter/UserMissionConverter.java | 17 +++++++++++ .../repository/UserMissionRepository.java | 3 ++ .../user/service/UserMissionService.java | 29 +++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 src/main/java/com/example/umc9th/domain/user/controller/UserController.java create mode 100644 src/main/java/com/example/umc9th/domain/user/converter/UserMissionConverter.java diff --git a/src/main/java/com/example/umc9th/domain/user/controller/UserController.java b/src/main/java/com/example/umc9th/domain/user/controller/UserController.java new file mode 100644 index 0000000..e180d90 --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/user/controller/UserController.java @@ -0,0 +1,26 @@ +package com.example.umc9th.domain.user.controller; + +import com.example.umc9th.domain.user.entity.mapping.UserMission; +import com.example.umc9th.domain.user.service.UserMissionService; +import com.example.umc9th.global.apiPayload.ApiResponse; +import com.example.umc9th.global.apiPayload.code.SuccessCode; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/users") +@RequiredArgsConstructor +public class UserController { + private final UserMissionService userMissionService; + + @PostMapping("/{userId}/missions/{missionId}/challenge") + public ResponseEntity> challengeMission( + @PathVariable Long userId, + @PathVariable Long missionId + ) { + UserMission newUserMission = userMissionService.challengeMission(userId, missionId); + + return ResponseEntity.ok(ApiResponse.onSuccess(SuccessCode._CREATED, newUserMission.getId())); + } +} diff --git a/src/main/java/com/example/umc9th/domain/user/converter/UserMissionConverter.java b/src/main/java/com/example/umc9th/domain/user/converter/UserMissionConverter.java new file mode 100644 index 0000000..56e861e --- /dev/null +++ b/src/main/java/com/example/umc9th/domain/user/converter/UserMissionConverter.java @@ -0,0 +1,17 @@ +package com.example.umc9th.domain.user.converter; + +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.domain.user.entity.User; +import com.example.umc9th.domain.user.entity.mapping.UserMission; + +public class UserMissionConverter { + + public static UserMission toUserMission(User user, Mission mission) { + return UserMission.builder() + .user(user) + .mission(mission) + .isDone(false) // 도전 시점에는 아직 미완료 상태 + .managerNumber(0) // 초기 매니저 번호는 0으로 설정 + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/umc9th/domain/user/repository/UserMissionRepository.java b/src/main/java/com/example/umc9th/domain/user/repository/UserMissionRepository.java index c55ceb6..2fa7c2b 100644 --- a/src/main/java/com/example/umc9th/domain/user/repository/UserMissionRepository.java +++ b/src/main/java/com/example/umc9th/domain/user/repository/UserMissionRepository.java @@ -1,7 +1,9 @@ package com.example.umc9th.domain.user.repository; +import com.example.umc9th.domain.mission.entity.Mission; import com.example.umc9th.domain.user.dto.UserMissionResDto; +import com.example.umc9th.domain.user.entity.User; import com.example.umc9th.domain.user.entity.mapping.UserMission; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -51,4 +53,5 @@ Page findOngoingMissions( @Param("userId") Long userId, Pageable pageable); + boolean existsByUserAndMissionAndIsDone(User user, Mission mission, boolean isDone); } diff --git a/src/main/java/com/example/umc9th/domain/user/service/UserMissionService.java b/src/main/java/com/example/umc9th/domain/user/service/UserMissionService.java index aecb9b8..2dbaedb 100644 --- a/src/main/java/com/example/umc9th/domain/user/service/UserMissionService.java +++ b/src/main/java/com/example/umc9th/domain/user/service/UserMissionService.java @@ -1,23 +1,52 @@ package com.example.umc9th.domain.user.service; +import com.example.umc9th.domain.mission.entity.Mission; +import com.example.umc9th.domain.mission.repository.MissionRepository; +import com.example.umc9th.domain.user.converter.UserMissionConverter; import com.example.umc9th.domain.user.dto.UserMissionResDto; +import com.example.umc9th.domain.user.entity.User; +import com.example.umc9th.domain.user.entity.mapping.UserMission; import com.example.umc9th.domain.user.repository.UserMissionRepository; import com.example.umc9th.domain.user.repository.UserRepository; +import com.example.umc9th.global.apiPayload.code.GeneralErrorCode; +import com.example.umc9th.global.apiPayload.exception.GeneralException; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor +@Transactional(readOnly = true) public class UserMissionService { private final UserRepository userRepository; private final UserMissionRepository userMissionRepository; + private final MissionRepository missionRepository; // 미션페이지 유저 미션 조회 public Page getMissionsByStatus(Long userId, boolean isDone, int page, int size) { PageRequest pageable = PageRequest.of(page, size); return userMissionRepository.findMissionsByUserIdAndStatus(userId, isDone, pageable); } + + @Transactional + public UserMission challengeMission(Long userId, Long missionId) { + + User user = userRepository.findById(userId) + .orElseThrow(() -> new GeneralException(GeneralErrorCode.USER_NOT_FOUND)); + + Mission mission = missionRepository.findById(missionId) + .orElseThrow(() -> new GeneralException(GeneralErrorCode.NOT_FOUND)); + + // UserMission 중복 도전 확인 + if (userMissionRepository.existsByUserAndMissionAndIsDone(user, mission, false)) { + throw new GeneralException(GeneralErrorCode.BAD_REQUEST); + } + + UserMission userMission = UserMissionConverter.toUserMission(user, mission); + + return userMissionRepository.save(userMission); + } }