From b72cae400b489393790f51d9c684bbfe4af8f3d2 Mon Sep 17 00:00:00 2001 From: YunJungHun <76200940+yunjunghun0116@users.noreply.github.com> Date: Thu, 1 Aug 2024 23:48:37 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B6=A9=EB=82=A8=EB=8C=80=20BE=5F=EC=9C=A4?= =?UTF-8?q?=EC=A0=95=ED=9B=88=206=EC=A3=BC=EC=B0=A8=20=EA=B3=BC=EC=A0=9C?= =?UTF-8?q?=20(1=EB=8B=A8=EA=B3=84)=20(#146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add: 주문하기 코드를 옮겨온다. 옮겨온다. * refactor: gitignore 변경 * remove: 동시성 테스트 진행으로 인해 발생하는 build 과부하 방지를 위해 동시성테스트 제거 동시성 테스트 제거 * add: 기존 동시성테스트 추가 동시성 테스트 추가 * add: ci-cd 파일 추가 ci-cd 파일 추가 * rename: workflows -> workflow * rename: ci-cd to gradle * Create gradle.yml * remove: workflow remove * add: CI with java * refactor: 자동으로 application.yml 파일 생성하도록 추가 * add: make application.yml 설정파일 생성 * rename: application 설정파일 이름 재설정 * remove: 동시성 테스트 삭제 * change: setup jdk * remove: deploy 코드 제거 * add: make application.yml 방식 추가 * refactor: application.yml 만드는 코드 수정 * add: deploy gradle 추가 * rename: secret deploy server로 ip 보호 * add: EC2 서버에 키를 알려줌 * add: 호스트키 신뢰하도록 설정 * rename: 호스트키 이름 변경 * add: HOST 추가 * add: 호스트키 검증 무시 * add: redirect-token-uri를 따로 관리해서 배포환경과 다른값 설정할 수 있도록 함 * refactor: 이름 명시 * refactor: 배포 코드 수정 * refactor: 절대 경로 설정 * add: 배포시 중간에 sleep 10 추가 * remove: 그냥 실행되는 부분 제거 * add: 기존 실행중인 8080포트 죽이기 * refactor: 절대경로로 설정 * refactor: application.yml이 아닌 application.properties에 추가작성함으로써 하나로 통일관리 * refactor: 설정값 변경 * add: 동시성테스트의 크기를 줄여 부하를 적게하여 업로드하기 쉽도록 변경 테스트코드 사이즈 변경 * refactor: test부하 일부 증가 test부하 일부 증가 * refactor: API 명세 팀원들과 통일 API 명세-URL을 팀원들과 통일 * test: 수정된 API 명세에 알맞게 controller URL 변경 * docs: Swagger 문서 작성을 위한 ApiResponse(Auth, Category) 작성 완료 AuthApi, CategoryApi ApiResponse 정의 완료 * docs: API 명세 통일에 따른 응답 반환 응답 반환하기 * remove: 중복되는 @Hidden 삭제 * infra: 스프링 배포 환경시 nohup 스프링 실행부분을 세션과 분리하여 종료 * infra: 백그라운드에서 실행 및 exit 으로 자동으로 배포까지하도록 설정 * infra: 백그라운드에서 스프링이 정상적으로 돌아가도록 gradle.yml 수정 * add: 스프링실행시 세션을 정상적으로 종료할 수 있도록 한다 * remove: Member-Role을 삭제 * add: 예외 Response를 한번 더 감싸서 status와 함께 반환 예외 Response 를 status, message 함께 반환 * add: API 명세서 중 응답이 포함되지 않는 부분은 응답에서 제외한다. Schema-hidden=true 설정을 통해 응답에서 제외했다. * refactor: 팀원들간 API 명세 통일 작업 수행 API 명세 통일 작업 수행 * docs: README.md 진행한 부분 체크 진행한 부분 체크작업 수행 * add: ADD 요청시 created 에 URL 을 담고, body를 반환하도록 함 body를 반환하도록 함 * remove: 사용하지 않는 예외 코드 삭제 사용하지 않는 예외코드 삭제 * refactor: List로 반환한 정보를 Page로 반환하도록 변경 List to Page 변경 * refactor: Page -> PageResponse 라는 DTO를 만들어 반환하도록 변경 DTO로 응답 타입 변환 과정 거침 * docs: API 명세서 PageResponse 로 변경 API 명세서 변경 * refactor: API 수정사항 반영 API 수정 사항 반영 --- .github/workflows/gradle.yml | 11 +- README.md | 7 + src/main/java/gift/client/KakaoApiClient.java | 11 +- src/main/java/gift/config/SwaggerConfig.java | 65 -------- src/main/java/gift/config/WebConfig.java | 6 +- .../config/properties/KakaoProperties.java | 2 +- .../gift/controller/CategoryController.java | 21 +-- .../gift/controller/GiftOrderController.java | 31 +++- .../java/gift/controller/KakaoController.java | 23 +-- .../gift/controller/MemberController.java | 5 +- .../gift/controller/OptionController.java | 55 +++---- .../gift/controller/ProductController.java | 35 +++-- .../controller/WishProductController.java | 30 ++-- .../java/gift/controller/api/AuthApi.java | 44 ++++++ .../java/gift/controller/api/CategoryApi.java | 60 ++++++++ .../gift/controller/api/GiftOrderApi.java | 50 ++++++ .../java/gift/controller/api/KakaoApi.java | 24 +++ .../java/gift/controller/api/MemberApi.java | 20 +++ .../java/gift/controller/api/OptionApi.java | 64 ++++++++ .../java/gift/controller/api/ProductApi.java | 61 ++++++++ .../gift/controller/api/WishProductApi.java | 51 +++++++ .../gift/controller/auth/AuthController.java | 31 +++- .../gift/controller/auth/AuthInterceptor.java | 5 +- src/main/java/gift/dto/auth/AuthResponse.java | 4 +- .../java/gift/dto/auth/RegisterRequest.java | 9 +- .../dto/category/CategoryInformation.java | 7 - .../gift/dto/category/CategoryResponse.java | 8 +- .../dto/giftorder/GiftOrderPageResponse.java | 6 + .../gift/dto/giftorder/GiftOrderResponse.java | 19 ++- .../gift/dto/kakao/KakaoAuthInformation.java | 5 +- .../gift/dto/option/OptionAddRequest.java | 21 --- .../gift/dto/option/OptionInformation.java | 7 - ...nUpdateRequest.java => OptionRequest.java} | 2 +- .../java/gift/dto/option/OptionResponse.java | 6 +- .../gift/dto/product/ProductAddRequest.java | 27 ++++ .../dto/product/ProductBasicInformation.java | 6 +- .../gift/dto/product/ProductPageResponse.java | 6 + .../gift/dto/product/ProductResponse.java | 16 +- ...Request.java => ProductUpdateRequest.java} | 2 +- .../wishproduct/WishProductAddRequest.java | 12 -- .../wishproduct/WishProductPageResponse.java | 6 + .../dto/wishproduct/WishProductRequest.java | 9 ++ .../dto/wishproduct/WishProductResponse.java | 11 +- .../wishproduct/WishProductUpdateRequest.java | 9 -- .../exception/AlreadyExistsException.java | 7 + .../gift/exception/ExceptionResponse.java | 4 + .../exception/GlobalExceptionHandler.java | 67 ++++---- .../exception/InvalidKakaoTokenException.java | 7 - src/main/java/gift/model/Member.java | 25 +-- src/main/java/gift/model/MemberRole.java | 6 - src/main/java/gift/model/WishProduct.java | 14 +- .../gift/repository/GiftOrderRepository.java | 7 +- .../gift/repository/OauthTokenRepository.java | 7 +- .../gift/repository/OptionRepository.java | 3 +- .../gift/repository/ProductRepository.java | 4 + .../repository/WishProductRepository.java | 4 +- .../java/gift/service/CategoryService.java | 5 +- .../java/gift/service/GiftOrderService.java | 20 ++- src/main/java/gift/service/KakaoService.java | 20 +-- src/main/java/gift/service/OptionService.java | 48 +++--- .../java/gift/service/ProductService.java | 74 +++++---- .../java/gift/service/WishProductService.java | 56 +++---- .../java/gift/service/auth/AuthService.java | 7 +- src/main/resources/application.properties | 2 + src/main/resources/data.sql | 8 +- .../controller/CategoryControllerTest.java | 57 ++++--- .../gift/controller/MemberControllerTest.java | 16 +- .../gift/controller/OptionControllerTest.java | 82 +++++----- .../controller/ProductControllerTest.java | 143 ++++++++++-------- .../controller/WishProductControllerTest.java | 113 ++++---------- .../controller/auth/AuthControllerTest.java | 94 ++++-------- .../AuthTestReflectionComponent.java | 12 -- .../gift/service/CategoryServiceTest.java | 2 +- .../java/gift/service/MemberServiceTest.java | 2 +- .../java/gift/service/OptionServiceTest.java | 85 +++++++---- .../java/gift/service/ProductServiceTest.java | 43 +++--- .../gift/service/WishProductServiceTest.java | 58 +++---- .../gift/service/auth/AuthServiceTest.java | 42 ++--- 78 files changed, 1161 insertions(+), 893 deletions(-) create mode 100644 src/main/java/gift/controller/api/AuthApi.java create mode 100644 src/main/java/gift/controller/api/CategoryApi.java create mode 100644 src/main/java/gift/controller/api/GiftOrderApi.java create mode 100644 src/main/java/gift/controller/api/KakaoApi.java create mode 100644 src/main/java/gift/controller/api/MemberApi.java create mode 100644 src/main/java/gift/controller/api/OptionApi.java create mode 100644 src/main/java/gift/controller/api/ProductApi.java create mode 100644 src/main/java/gift/controller/api/WishProductApi.java delete mode 100644 src/main/java/gift/dto/category/CategoryInformation.java create mode 100644 src/main/java/gift/dto/giftorder/GiftOrderPageResponse.java delete mode 100644 src/main/java/gift/dto/option/OptionAddRequest.java delete mode 100644 src/main/java/gift/dto/option/OptionInformation.java rename src/main/java/gift/dto/option/{OptionUpdateRequest.java => OptionRequest.java} (95%) create mode 100644 src/main/java/gift/dto/product/ProductAddRequest.java create mode 100644 src/main/java/gift/dto/product/ProductPageResponse.java rename src/main/java/gift/dto/product/{ProductRequest.java => ProductUpdateRequest.java} (96%) delete mode 100644 src/main/java/gift/dto/wishproduct/WishProductAddRequest.java create mode 100644 src/main/java/gift/dto/wishproduct/WishProductPageResponse.java create mode 100644 src/main/java/gift/dto/wishproduct/WishProductRequest.java delete mode 100644 src/main/java/gift/dto/wishproduct/WishProductUpdateRequest.java create mode 100644 src/main/java/gift/exception/AlreadyExistsException.java create mode 100644 src/main/java/gift/exception/ExceptionResponse.java delete mode 100644 src/main/java/gift/exception/InvalidKakaoTokenException.java delete mode 100644 src/main/java/gift/model/MemberRole.java diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 9842d3d29..36629f30a 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -27,9 +27,8 @@ jobs: - name: 설정파일 생성 run: | - touch ./src/main/resources/application.yml - echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application.yml - cat ./src/main/resources/application.yml + echo "${{ secrets.APPLICATION_PROPERTIES }}" >> ./src/main/resources/application.properties + cat ./src/main/resources/application.properties - name: gradlew 권한 추가 run: chmod +x gradlew @@ -49,10 +48,12 @@ jobs: echo "$SSH_KEY" > key.pem chmod 400 key.pem scp -o StrictHostKeyChecking=no -i key.pem build/libs/*.jar $DEPLOY_USER@$DEPLOY_SERVER:~/spring-gift-point/build/libs/ - scp -o StrictHostKeyChecking=no -i key.pem ./src/main/resources/application.yml $DEPLOY_USER@$DEPLOY_SERVER:~/spring-gift-point/src/main/resources/application.yml + scp -o StrictHostKeyChecking=no -i key.pem ./src/main/resources/application.properties $DEPLOY_USER@$DEPLOY_SERVER:~/spring-gift-point/src/main/resources/application.properties ssh -o StrictHostKeyChecking=no -i key.pem $DEPLOY_USER@$DEPLOY_SERVER " sudo lsof -t -i:8080 | xargs -r sudo kill -9 - nohup java -jar ~/spring-gift-point/build/libs/spring-gift-0.0.1-SNAPSHOT.jar &" + nohup java -jar ~/spring-gift-point/build/libs/spring-gift-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 & + exit + " && echo "스프링 프로젝트가 서버에서 정상적으로 돌아가고 있습니다." dependency-submission: diff --git a/README.md b/README.md index 3382e4c0c..9b15a2285 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,13 @@ - [X] 주문하기 코드를 옮겨온다. +#### 1단계 + +- [X] URL 주소를 일치시킨다. +- [X] DTO 를 일치시킨다. +- [X] 반환하는 응답을 일정하게 상태코드를 통일시킨다. +- [X] 예외에 해당하는 응답을 Exception Handler 에서는 ExceptionResponse 으로 감싸서 반환하도록 한다. + ### 나만의 HTTP RULE | HTTP Method | 사용상황 | 반환(상태코드) | diff --git a/src/main/java/gift/client/KakaoApiClient.java b/src/main/java/gift/client/KakaoApiClient.java index 06d088b8d..c2cdf78bb 100644 --- a/src/main/java/gift/client/KakaoApiClient.java +++ b/src/main/java/gift/client/KakaoApiClient.java @@ -11,7 +11,7 @@ import gift.dto.kakao.template.KakaoTemplateContent; import gift.dto.kakao.template.KakaoTemplateLink; import gift.exception.BadRequestException; -import gift.exception.InvalidKakaoTokenException; +import gift.exception.UnauthorizedAccessException; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Component; @@ -63,10 +63,7 @@ public KakaoTokenResponse getRefreshedTokenResponse(String refreshToken) { .body(body) .retrieve() .onStatus(statusCode -> statusCode.equals(HttpStatus.UNAUTHORIZED), (req, res) -> { - throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); - }) - .onStatus(statusCode -> statusCode.equals(HttpStatus.BAD_REQUEST), (req, res) -> { - throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); + throw new UnauthorizedAccessException("유효하지 않은 카카오 리프레시 토큰입니다."); }) .body(String.class); @@ -102,7 +99,7 @@ public void sendSelfMessageOrder(String accessToken, GiftOrderResponse giftOrder .body(body) .retrieve() .onStatus(statusCode -> statusCode.equals(HttpStatus.UNAUTHORIZED), (req, res) -> { - throw new InvalidKakaoTokenException(INVALID_TOKEN_MESSAGE); + throw new UnauthorizedAccessException(INVALID_TOKEN_MESSAGE); }) .body(String.class); } catch (JsonProcessingException exception) { @@ -122,7 +119,7 @@ private KakaoTemplate getCommerceTemplate(GiftOrderResponse giftOrderResponse) { var objectType = "commerce"; var link = new KakaoTemplateLink("https://gift.kakao.com/product/2370524"); var content = new KakaoTemplateContent(giftOrderResponse.message(), "https://img1.kakaocdn.net/thumb/C320x320@2x.fwebp.q82/?fname=https%3A%2F%2Fst.kakaocdn.net%2Fproduct%2Fgift%2Fproduct%2F20240417111629_616eccb9d4cd464fa06d3430947dce15.jpg", giftOrderResponse.message(), link); - var commerce = new KakaoTemplateCommerce(giftOrderResponse.optionInformation().productName() + "[" + giftOrderResponse.optionInformation().name() + "]", giftOrderResponse.optionInformation().price() * giftOrderResponse.quantity()); + var commerce = new KakaoTemplateCommerce(giftOrderResponse.productBasicInformation().name() + "[" + giftOrderResponse.optionResponse().name() + "]", giftOrderResponse.productBasicInformation().price() * giftOrderResponse.quantity()); return new KakaoTemplate(objectType, content, commerce); } } diff --git a/src/main/java/gift/config/SwaggerConfig.java b/src/main/java/gift/config/SwaggerConfig.java index 0788aca7b..24d0fdd51 100644 --- a/src/main/java/gift/config/SwaggerConfig.java +++ b/src/main/java/gift/config/SwaggerConfig.java @@ -1,17 +1,10 @@ package gift.config; import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.parameters.HeaderParameter; -import io.swagger.v3.oas.models.responses.ApiResponse; -import org.springdoc.core.customizers.OpenApiCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.Set; - @Configuration public class SwaggerConfig { @@ -23,62 +16,4 @@ public OpenAPI openAPI() { .description("프론트엔드와 협업을 위한 API 문서"); return new OpenAPI().info(info); } - - @Bean - public OpenApiCustomizer openApiCustomizer() { - return openApi -> { - openApi.getPaths() - .forEach((path, pathItem) -> { - setBaseOperationResponse(path, pathItem); - setOperationResponse(pathItem.getPut(), path, "PUT"); - setOperationResponse(pathItem.getPost(), path, "POST"); - setOperationResponse(pathItem.getDelete(), path, "DELETE"); - }); - }; - } - - private void setBaseOperationResponse(String path, PathItem pathItem) { - var excludePaths = Set.of("/api/members/oauth/kakao", "/api/members/login", "/api/members/register", "/api/kakao/get-oauth"); - - for (var operation : pathItem.readOperations()) { - var successResponse = new ApiResponse().description("성공"); - operation.getResponses() - .addApiResponse("200", successResponse); - - if (!excludePaths.contains(path)) { - var header = new HeaderParameter() - .name("Authorization") - .required(Boolean.TRUE); - operation.addParametersItem(header); - - var unauthorizedResponse = new ApiResponse().description("잘못된 인증정보"); - operation.getResponses().addApiResponse("401", unauthorizedResponse); - } - } - } - - private void setOperationResponse(Operation operation, String path, String method) { - if (operation == null) return; - if (method.equals("POST") && path.contains("/add")) { - var createdResponse = new ApiResponse().description("생성 성공"); - operation.getResponses() - .addApiResponse("201", createdResponse); - operation.getResponses() - .remove("200"); - } - if (method.equals("PUT")) { - var updatedResponse = new ApiResponse().description("업데이트 성공"); - operation.getResponses() - .addApiResponse("204", updatedResponse); - operation.getResponses() - .remove("200"); - } - if (method.equals("DELETE")) { - var updatedResponse = new ApiResponse().description("삭제 성공"); - operation.getResponses() - .addApiResponse("204", updatedResponse); - operation.getResponses() - .remove("200"); - } - } } diff --git a/src/main/java/gift/config/WebConfig.java b/src/main/java/gift/config/WebConfig.java index 792560643..6552dc9be 100644 --- a/src/main/java/gift/config/WebConfig.java +++ b/src/main/java/gift/config/WebConfig.java @@ -19,9 +19,7 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/**") .excludePathPatterns("/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**") - .excludePathPatterns("/api/members/oauth/kakao") - .excludePathPatterns("/api/members/login") - .excludePathPatterns("/api/members/register") - .excludePathPatterns("/api/kakao/get-oauth"); + .excludePathPatterns("/api/members/login/**") + .excludePathPatterns("/api/members/register"); } } diff --git a/src/main/java/gift/config/properties/KakaoProperties.java b/src/main/java/gift/config/properties/KakaoProperties.java index 6bd8999e5..c48d8d866 100644 --- a/src/main/java/gift/config/properties/KakaoProperties.java +++ b/src/main/java/gift/config/properties/KakaoProperties.java @@ -3,5 +3,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "kakao") -public record KakaoProperties(String restApiKey, String redirectUri, String tokenUri) { +public record KakaoProperties(String restApiKey, String redirectUri, String tokenUri, String oauthBaseUri) { } diff --git a/src/main/java/gift/controller/CategoryController.java b/src/main/java/gift/controller/CategoryController.java index 94bd7306b..25921e76e 100644 --- a/src/main/java/gift/controller/CategoryController.java +++ b/src/main/java/gift/controller/CategoryController.java @@ -1,13 +1,10 @@ package gift.controller; +import gift.controller.api.CategoryApi; import gift.dto.category.CategoryRequest; import gift.dto.category.CategoryResponse; import gift.service.CategoryService; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -23,8 +20,7 @@ @RestController @RequestMapping("/api/categories") -@Tag(name = "CATEGORY") -public class CategoryController { +public class CategoryController implements CategoryApi { private final CategoryService categoryService; @@ -32,13 +28,13 @@ public CategoryController(CategoryService categoryService) { this.categoryService = categoryService; } - @PostMapping("/add") - public ResponseEntity addCategory(@Valid @RequestBody CategoryRequest categoryRequest) { + @PostMapping + public ResponseEntity addCategory(@Valid @RequestBody CategoryRequest categoryRequest) { var category = categoryService.addCategory(categoryRequest); - return ResponseEntity.created(URI.create("/api/categories/" + category.id())).build(); + return ResponseEntity.created(URI.create("/api/categories/" + category.id())).body(category); } - @PutMapping("/update/{id}") + @PutMapping("/{id}") public ResponseEntity updateCategory(@PathVariable Long id, @Valid @RequestBody CategoryRequest categoryRequest) { categoryService.updateCategory(id, categoryRequest); return ResponseEntity.noContent().build(); @@ -51,9 +47,8 @@ public ResponseEntity getCategory(@PathVariable Long id) { } @GetMapping - public ResponseEntity> getCategories( - @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { - var categories = categoryService.getCategories(pageable); + public ResponseEntity> getCategories() { + var categories = categoryService.getCategories(); return ResponseEntity.ok(categories); } diff --git a/src/main/java/gift/controller/GiftOrderController.java b/src/main/java/gift/controller/GiftOrderController.java index 1f016d30a..5dd56b756 100644 --- a/src/main/java/gift/controller/GiftOrderController.java +++ b/src/main/java/gift/controller/GiftOrderController.java @@ -1,8 +1,13 @@ package gift.controller; +import gift.controller.api.GiftOrderApi; +import gift.dto.giftorder.GiftOrderPageResponse; +import gift.dto.giftorder.GiftOrderRequest; import gift.dto.giftorder.GiftOrderResponse; import gift.service.GiftOrderService; -import io.swagger.v3.oas.annotations.tags.Tag; +import gift.service.KakaoService; +import gift.service.OptionService; +import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; @@ -10,21 +15,33 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.List; +import java.net.URI; @RestController -@RequestMapping("/api/giftOrders") -@Tag(name = "GIFT_ORDER") -public class GiftOrderController { +@RequestMapping("/api/orders") +public class GiftOrderController implements GiftOrderApi { private final GiftOrderService giftOrderService; + private final OptionService optionService; + private final KakaoService kakaoService; - public GiftOrderController(GiftOrderService giftOrderService) { + public GiftOrderController(GiftOrderService giftOrderService, OptionService optionService, KakaoService kakaoService) { this.giftOrderService = giftOrderService; + this.optionService = optionService; + this.kakaoService = kakaoService; + } + + @PostMapping + public ResponseEntity orderOption(@RequestAttribute("memberId") Long memberId, @Valid @RequestBody GiftOrderRequest giftOrderRequest) { + var order = optionService.orderOption(memberId, giftOrderRequest); + kakaoService.sendOrderResponseWithKakaoMessage(memberId, order); + return ResponseEntity.created(URI.create("/api/orders/" + order.id())).body(order); } @GetMapping("/{id}") @@ -34,7 +51,7 @@ public ResponseEntity getOrder(@PathVariable Long id) { } @GetMapping - public ResponseEntity> getOrders(@RequestAttribute("memberId") Long memberId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { + public ResponseEntity getOrders(@RequestAttribute("memberId") Long memberId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { var orders = giftOrderService.getGiftOrders(memberId, pageable); return ResponseEntity.ok(orders); } diff --git a/src/main/java/gift/controller/KakaoController.java b/src/main/java/gift/controller/KakaoController.java index 6d21344d7..e00927aef 100644 --- a/src/main/java/gift/controller/KakaoController.java +++ b/src/main/java/gift/controller/KakaoController.java @@ -1,9 +1,8 @@ package gift.controller; import gift.config.properties.KakaoProperties; +import gift.controller.api.KakaoApi; import gift.service.KakaoService; -import io.swagger.v3.oas.annotations.Hidden; -import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -17,12 +16,10 @@ @RestController @RequestMapping("/api/kakao") -@Tag(name = "KAKAO") -public class KakaoController { +public class KakaoController implements KakaoApi { private final KakaoService kakaoService; private final KakaoProperties kakaoProperties; - private static final String OAUTH_BASE_URL = "https://kauth.kakao.com/oauth/authorize?response_type=code&scope=account_email,talk_message"; public KakaoController(KakaoService kakaoService, KakaoProperties kakaoProperties) { this.kakaoService = kakaoService; @@ -35,7 +32,6 @@ public ResponseEntity redirectSetToken(@RequestAttribute("memberId") Long return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY); } - @Hidden @GetMapping("/token") public ResponseEntity setToken(@RequestParam String code, @RequestParam String state) { var memberId = Long.valueOf(state); @@ -43,22 +39,9 @@ public ResponseEntity setToken(@RequestParam String code, @RequestParam St return ResponseEntity.noContent().build(); } - @GetMapping("/get-oauth") - public ResponseEntity redirectOAuth() { - var headers = getRedirectHeader(kakaoProperties.redirectUri()); - return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY); - } - - private HttpHeaders getRedirectHeader(String redirectUri) { - var headers = new HttpHeaders(); - String redirectLocation = OAUTH_BASE_URL + "&client_id=" + kakaoProperties.restApiKey() + "&redirect_uri=" + redirectUri; - headers.setLocation(URI.create(redirectLocation)); - return headers; - } - private HttpHeaders getRedirectHeader(String redirectUri, Long memberId) { var headers = new HttpHeaders(); - String redirectLocation = OAUTH_BASE_URL + "&client_id=" + kakaoProperties.restApiKey() + "&redirect_uri=" + redirectUri + "&state=" + memberId; + String redirectLocation = kakaoProperties.oauthBaseUri() + "&client_id=" + kakaoProperties.restApiKey() + "&redirect_uri=" + redirectUri + "&state=" + memberId; headers.setLocation(URI.create(redirectLocation)); return headers; } diff --git a/src/main/java/gift/controller/MemberController.java b/src/main/java/gift/controller/MemberController.java index 3a17f828b..a118b3e9b 100644 --- a/src/main/java/gift/controller/MemberController.java +++ b/src/main/java/gift/controller/MemberController.java @@ -1,7 +1,7 @@ package gift.controller; +import gift.controller.api.MemberApi; import gift.service.MemberService; -import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestAttribute; @@ -10,8 +10,7 @@ @RestController @RequestMapping("/api/members") -@Tag(name = "MEMBER") -public class MemberController { +public class MemberController implements MemberApi { private final MemberService memberService; diff --git a/src/main/java/gift/controller/OptionController.java b/src/main/java/gift/controller/OptionController.java index 13b33c8a6..b49aaedfb 100644 --- a/src/main/java/gift/controller/OptionController.java +++ b/src/main/java/gift/controller/OptionController.java @@ -1,73 +1,60 @@ package gift.controller; -import gift.dto.giftorder.GiftOrderRequest; -import gift.dto.option.OptionAddRequest; +import gift.controller.api.OptionApi; +import gift.dto.option.OptionRequest; import gift.dto.option.OptionResponse; -import gift.dto.option.OptionUpdateRequest; -import gift.service.KakaoService; import gift.service.OptionService; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; -import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.net.URI; import java.util.List; @RestController -@RequestMapping("/api/options") -@Tag(name = "OPTION") -public class OptionController { +@RequestMapping("/api/products/{productId}/options") +public class OptionController implements OptionApi { private final OptionService optionService; - private final KakaoService kakaoService; - - public OptionController(OptionService optionService, KakaoService kakaoService) { + public OptionController(OptionService optionService) { this.optionService = optionService; - this.kakaoService = kakaoService; } - @PostMapping("/add") - public ResponseEntity addOption(@Valid @RequestBody OptionAddRequest optionAddRequest) { - var option = optionService.addOption(optionAddRequest); - return ResponseEntity.created(URI.create("/api/options/" + option.id())).build(); + @PostMapping + public ResponseEntity addOption(@PathVariable Long productId, @Valid @RequestBody OptionRequest optionRequest) { + var option = optionService.addOption(productId, optionRequest); + return ResponseEntity.created(URI.create("/api/products/" + productId + "/options/" + option.id())).body(option); } - @PostMapping("/order") - public ResponseEntity orderOption(@RequestAttribute("memberId") Long memberId, @Valid @RequestBody GiftOrderRequest giftOrderRequest) { - var order = optionService.orderOption(memberId, giftOrderRequest); - kakaoService.sendSelfMessageOrder(memberId, order); - return ResponseEntity.created(URI.create("/api/giftOrders/" + order.id())).build(); + @PutMapping("/{id}") + public ResponseEntity updateOption(@PathVariable Long productId, @PathVariable Long id, @Valid @RequestBody OptionRequest optionUpdateRequest) { + optionService.updateOption(productId, id, optionUpdateRequest); + return ResponseEntity.noContent().build(); } - @PutMapping("/update/{id}") - public ResponseEntity updateOption(@PathVariable Long id, @Valid @RequestBody OptionUpdateRequest optionUpdateRequest) { - optionService.updateOption(id, optionUpdateRequest); - return ResponseEntity.noContent().build(); + @GetMapping("/{id}") + public ResponseEntity getOption(@PathVariable Long productId, @PathVariable Long id) { + var option = optionService.getOption(productId, id); + return ResponseEntity.ok(option); } @GetMapping - public ResponseEntity> getOptions(@RequestParam Long productId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { - var options = optionService.getOptions(productId, pageable); + public ResponseEntity> getOptions(@PathVariable Long productId) { + var options = optionService.getOptions(productId); return ResponseEntity.ok(options); } @DeleteMapping("/{id}") - public ResponseEntity deleteOption(@PathVariable Long id) { - optionService.deleteOption(id); + public ResponseEntity deleteOption(@PathVariable Long productId, @PathVariable Long id) { + optionService.deleteOption(productId, id); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/gift/controller/ProductController.java b/src/main/java/gift/controller/ProductController.java index 7a31c9203..05d89679b 100644 --- a/src/main/java/gift/controller/ProductController.java +++ b/src/main/java/gift/controller/ProductController.java @@ -1,10 +1,11 @@ package gift.controller; -import gift.dto.product.ProductRequest; +import gift.controller.api.ProductApi; +import gift.dto.product.ProductAddRequest; +import gift.dto.product.ProductPageResponse; import gift.dto.product.ProductResponse; -import gift.model.MemberRole; +import gift.dto.product.ProductUpdateRequest; import gift.service.ProductService; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -15,18 +16,16 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.net.URI; -import java.util.List; @RestController @RequestMapping("/api/products") -@Tag(name = "PRODUCT") -public class ProductController { +public class ProductController implements ProductApi { private final ProductService productService; @@ -34,15 +33,15 @@ public ProductController(ProductService productService) { this.productService = productService; } - @PostMapping("/add") - public ResponseEntity addProduct(@Valid @RequestBody ProductRequest productRequest, @RequestAttribute("memberRole") String memberRole) { - var product = productService.addProduct(productRequest, MemberRole.valueOf(memberRole)); - return ResponseEntity.created(URI.create("/api/products/" + product.id())).build(); + @PostMapping + public ResponseEntity addProduct(@Valid @RequestBody ProductAddRequest productAddRequest) { + var product = productService.addProduct(productAddRequest); + return ResponseEntity.created(URI.create("/api/products/" + product.id())).body(product); } - @PutMapping("/update/{id}") - public ResponseEntity updateProduct(@PathVariable Long id, @Valid @RequestBody ProductRequest productRequest) { - productService.updateProduct(id, productRequest); + @PutMapping("/{id}") + public ResponseEntity updateProduct(@PathVariable Long id, @Valid @RequestBody ProductUpdateRequest productUpdateRequest) { + productService.updateProduct(id, productUpdateRequest); return ResponseEntity.noContent().build(); } @@ -53,8 +52,12 @@ public ResponseEntity getProduct(@PathVariable Long id) { } @GetMapping - public ResponseEntity> getProducts(@PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { - var products = productService.getProducts(pageable); + public ResponseEntity getProducts(@RequestParam(required = false) Long categoryId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { + if (categoryId == null) { + var products = productService.getProducts(pageable); + return ResponseEntity.ok(products); + } + var products = productService.getProducts(categoryId, pageable); return ResponseEntity.ok(products); } diff --git a/src/main/java/gift/controller/WishProductController.java b/src/main/java/gift/controller/WishProductController.java index 143138d96..4d51c28d3 100644 --- a/src/main/java/gift/controller/WishProductController.java +++ b/src/main/java/gift/controller/WishProductController.java @@ -1,10 +1,10 @@ package gift.controller; -import gift.dto.wishproduct.WishProductAddRequest; +import gift.controller.api.WishProductApi; +import gift.dto.wishproduct.WishProductPageResponse; +import gift.dto.wishproduct.WishProductRequest; import gift.dto.wishproduct.WishProductResponse; -import gift.dto.wishproduct.WishProductUpdateRequest; import gift.service.WishProductService; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -14,19 +14,16 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestAttribute; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.net.URI; -import java.util.List; @RestController @RequestMapping("/api/wishes") -@Tag(name = "WISH_PRODUCT") -public class WishProductController { +public class WishProductController implements WishProductApi { private final WishProductService wishProductService; @@ -34,21 +31,20 @@ public WishProductController(WishProductService wishProductService) { this.wishProductService = wishProductService; } - @PostMapping("/add") - public ResponseEntity addWishProduct(@Valid @RequestBody WishProductAddRequest wishProductAddRequest, @RequestAttribute("memberId") Long memberId) { - var wishProduct = wishProductService.addWishProduct(wishProductAddRequest, memberId); - return ResponseEntity.created(URI.create("/api/wishes/" + wishProduct.id())).build(); + @PostMapping + public ResponseEntity addWishProduct(@Valid @RequestBody WishProductRequest wishProductRequest, @RequestAttribute("memberId") Long memberId) { + var wishProduct = wishProductService.addWishProduct(wishProductRequest, memberId); + return ResponseEntity.created(URI.create("/api/wishes/" + wishProduct.id())).body(wishProduct); } - @PutMapping("/update/{id}") - public ResponseEntity updateWishProduct(@PathVariable Long id, @Valid @RequestBody WishProductUpdateRequest wishProductUpdateRequest) { - wishProductService.updateWishProduct(id, wishProductUpdateRequest); - return ResponseEntity.noContent().build(); + @GetMapping("/{id}") + public ResponseEntity getWishProduct(@RequestAttribute("memberId") Long memberId, @PathVariable Long id) { + var wishProduct = wishProductService.getWishProduct(memberId, id); + return ResponseEntity.ok(wishProduct); } @GetMapping - public ResponseEntity> getWishProducts(@RequestAttribute("memberId") Long memberId, - @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { + public ResponseEntity getWishProducts(@RequestAttribute("memberId") Long memberId, @PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) { var wishProducts = wishProductService.getWishProducts(memberId, pageable); return ResponseEntity.ok(wishProducts); } diff --git a/src/main/java/gift/controller/api/AuthApi.java b/src/main/java/gift/controller/api/AuthApi.java new file mode 100644 index 000000000..20cfe1680 --- /dev/null +++ b/src/main/java/gift/controller/api/AuthApi.java @@ -0,0 +1,44 @@ +package gift.controller.api; + +import gift.dto.auth.AuthResponse; +import gift.dto.auth.LoginRequest; +import gift.dto.auth.RegisterRequest; +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; + +@Tag(name = "회원 API") +public interface AuthApi { + + @Operation(summary = "새로 회원 가입을 진행하고 토큰을 받는다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "회원 가입 성공", content = @Content(schema = @Schema(implementation = AuthResponse.class))), + @ApiResponse(responseCode = "409", description = "회원 가입 실패(사유 : 이미 존재하는 이메일입니다. )", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity register(RegisterRequest registerRequest); + + @Operation(summary = "로그인을 하고 토큰을 받는다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "로그인 성공", content = @Content(schema = @Schema(implementation = AuthResponse.class))), + @ApiResponse(responseCode = "401", description = "회원 등록 실패(사유 : 이메일이나 비밀번호가 올바르지 않습니다. )", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity login(LoginRequest loginRequest); + + @Operation(summary = "카카오 로그인을 통해 회원을 인증하고 토큰을 받는다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "로그인 성공", content = @Content(schema = @Schema(implementation = AuthResponse.class))), + @ApiResponse(responseCode = "401", description = "회원 등록 실패(사유 : 이미 가입된 이메일입니다. )", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity redirectKakaoLogin(); + + @Hidden + ResponseEntity loginWithKakaoAuth(String code); +} diff --git a/src/main/java/gift/controller/api/CategoryApi.java b/src/main/java/gift/controller/api/CategoryApi.java new file mode 100644 index 000000000..4476649e6 --- /dev/null +++ b/src/main/java/gift/controller/api/CategoryApi.java @@ -0,0 +1,60 @@ +package gift.controller.api; + +import gift.dto.category.CategoryRequest; +import gift.dto.category.CategoryResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; + +import java.util.List; + +@Tag(name = "카테고리 API") +public interface CategoryApi { + @Operation(summary = "새 카테고리를 생성한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "카테고리 생성 성공", content = @Content(schema = @Schema(implementation = CategoryResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "409", description = "카테고리 생성 실패(사유 : 이미 존재하는 이름입니다. )", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity addCategory(CategoryRequest categoryRequest); + + @Operation(summary = "기존 카테고리를 수정한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "카테고리 수정 성공"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "409", description = "카테고리 수정 실패(사유 : 이미 존재하는 이름입니다. )"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity updateCategory(Long id, CategoryRequest categoryRequest); + + @Operation(summary = "특정 카테고리를 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "특정 카테고리 조회 성공", content = @Content(schema = @Schema(implementation = CategoryResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getCategory(Long id); + + @Operation(summary = "모든 카테고리를 페이지 단위로 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "모든 카테고리 조회 성공", content = @Content(array = @ArraySchema(schema = @Schema(implementation = CategoryResponse.class)))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity> getCategories(); + + @Operation(summary = "특정 카테고리를 삭제한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "카테고리 삭제 성공"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "404", description = "카테고리 삭제 실패(사유 : 존재하지 않는 ID 입니다.)"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity deleteCategory(Long id); +} diff --git a/src/main/java/gift/controller/api/GiftOrderApi.java b/src/main/java/gift/controller/api/GiftOrderApi.java new file mode 100644 index 000000000..f573adc48 --- /dev/null +++ b/src/main/java/gift/controller/api/GiftOrderApi.java @@ -0,0 +1,50 @@ +package gift.controller.api; + +import gift.dto.giftorder.GiftOrderPageResponse; +import gift.dto.giftorder.GiftOrderRequest; +import gift.dto.giftorder.GiftOrderResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; + +@Tag(name = "상품 주문 API") +public interface GiftOrderApi { + + @Operation(summary = "회원의 새 주문을 생성한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "주문 생성 성공", content = @Content(schema = @Schema(implementation = GiftOrderResponse.class))), + @ApiResponse(responseCode = "401", description = "주문 생성 실패(사유 : 카카오 토큰이 만료되었거나, 허용되지 않은 요청입니다.)", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity orderOption(Long memberId, GiftOrderRequest giftOrderRequest); + + @Operation(summary = "회원의 특정 주문을 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "특정 주문 조회 성공", content = @Content(schema = @Schema(implementation = GiftOrderResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getOrder(Long id); + + @Operation(summary = "회원의 모든 주문을 페이지 단위로 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "모든 주문 조회 성공", content = @Content(schema = @Schema(implementation = GiftOrderPageResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getOrders(Long memberId, Pageable pageable); + + @Operation(summary = "특정 주문을 삭제한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "주문 삭제 성공"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "404", description = "주문 삭제 실패(사유 : 존재하지 않는 ID 입니다.)"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity deleteOrder(Long id); +} diff --git a/src/main/java/gift/controller/api/KakaoApi.java b/src/main/java/gift/controller/api/KakaoApi.java new file mode 100644 index 000000000..022346d86 --- /dev/null +++ b/src/main/java/gift/controller/api/KakaoApi.java @@ -0,0 +1,24 @@ +package gift.controller.api; + +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; + +@Tag(name = "인증 토큰 API") +public interface KakaoApi { + + @Operation(summary = "카카오 로그인을 통해 회원을 인증하고 토큰을 등록한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "토큰 등록 성공"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "404", description = "토큰 등록 실패(사유 : 존재하지 않는 이용자 정보입니다.)"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity redirectSetToken(Long memberId); + + @Hidden + ResponseEntity setToken(String code, String state); +} diff --git a/src/main/java/gift/controller/api/MemberApi.java b/src/main/java/gift/controller/api/MemberApi.java new file mode 100644 index 000000000..2bc3796ca --- /dev/null +++ b/src/main/java/gift/controller/api/MemberApi.java @@ -0,0 +1,20 @@ +package gift.controller.api; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; + +@Tag(name = "회원 API") +public interface MemberApi { + + @Operation(summary = "회원을 인증하고 탈퇴를 진행한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "회원 탈퇴 성공"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "404", description = "회원 탈퇴 실패(사유 : 존재하지 않는 ID 입니다.)"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity deleteMember(Long memberId); +} diff --git a/src/main/java/gift/controller/api/OptionApi.java b/src/main/java/gift/controller/api/OptionApi.java new file mode 100644 index 000000000..dc3db84fc --- /dev/null +++ b/src/main/java/gift/controller/api/OptionApi.java @@ -0,0 +1,64 @@ +package gift.controller.api; + +import gift.dto.option.OptionRequest; +import gift.dto.option.OptionResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; + +import java.util.List; + +@Tag(name = "상품 옵션 API") +public interface OptionApi { + + @Operation(summary = "상품에 옵션을 추가한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "옵션 추가 성공", content = @Content(schema = @Schema(implementation = OptionResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "409", description = "옵션 추가 실패(사유 : 이미 존재하는 이름입니다. )", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity addOption(Long productId, OptionRequest optionRequest); + + @Operation(summary = "기존 옵션을 수정한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "옵션 수정 성공"), + @ApiResponse(responseCode = "400", description = "옵션 수정 실패(사유 : 옵션과 연결된 상품 ID 가 아닙니다.)"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "409", description = "옵션 수정 실패(사유 : 이미 존재하는 이름입니다. )"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity updateOption(Long productId, Long id, OptionRequest optionRequest); + + @Operation(summary = "특정 옵션을 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "특정 옵션 조회 성공", content = @Content(schema = @Schema(implementation = OptionResponse.class))), + @ApiResponse(responseCode = "400", description = "특정 옵션 조회 실패(사유 : 옵션과 연결된 상품 ID 가 아닙니다.)", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getOption(Long productId, Long id); + + @Operation(summary = "모든 옵션을 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "모든 옵션 조회 성공", content = @Content(array = @ArraySchema(schema = @Schema(implementation = OptionResponse.class)))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity> getOptions(Long productId); + + @Operation(summary = "특정 옵션을 삭제한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "옵션 삭제 성공"), + @ApiResponse(responseCode = "400", description = "옵션 삭제 실패(사유 : 옵션과 연결된 상품 ID 가 아닙니다.)"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "404", description = "옵션 삭제 실패(사유 : 존재하지 않는 ID 입니다.)"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity deleteOption(Long productId, Long id); +} diff --git a/src/main/java/gift/controller/api/ProductApi.java b/src/main/java/gift/controller/api/ProductApi.java new file mode 100644 index 000000000..54ab79c1d --- /dev/null +++ b/src/main/java/gift/controller/api/ProductApi.java @@ -0,0 +1,61 @@ +package gift.controller.api; + +import gift.dto.product.ProductAddRequest; +import gift.dto.product.ProductPageResponse; +import gift.dto.product.ProductResponse; +import gift.dto.product.ProductUpdateRequest; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; + +@Tag(name = "상품 API") +public interface ProductApi { + + @Operation(summary = "새 상품을 등록한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "상품 등록 성공", content = @Content(schema = @Schema(implementation = ProductResponse.class))), + @ApiResponse(responseCode = "400", description = "상품 등록 실패(사유 : 카카오가 포함된 이름입니다.)", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity addProduct(ProductAddRequest productAddRequest); + + @Operation(summary = "기존 상품을 수정한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "상품 수정 성공"), + @ApiResponse(responseCode = "400", description = "상품 수정 실패(사유 : 카카오가 포함된 이름입니다.)"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity updateProduct(Long id, ProductUpdateRequest productUpdateRequest); + + @Operation(summary = "특정 상품을 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "특정 상품 조회 성공", content = @Content(schema = @Schema(implementation = ProductResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getProduct(Long id); + + @Operation(summary = "모든 상품을 페이지 단위로 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "모든 상품 조회 성공", content = @Content(schema = @Schema(implementation = ProductPageResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getProducts(Long categoryId, Pageable pageable); + + @Operation(summary = "특정 상품을 삭제한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "상품 삭제 성공"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "404", description = "상품 삭제 실패(사유 : 존재하지 않는 ID 입니다.)"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity deleteProduct(Long id); +} diff --git a/src/main/java/gift/controller/api/WishProductApi.java b/src/main/java/gift/controller/api/WishProductApi.java new file mode 100644 index 000000000..47b6efda0 --- /dev/null +++ b/src/main/java/gift/controller/api/WishProductApi.java @@ -0,0 +1,51 @@ +package gift.controller.api; + +import gift.dto.wishproduct.WishProductPageResponse; +import gift.dto.wishproduct.WishProductRequest; +import gift.dto.wishproduct.WishProductResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; + +@Tag(name = "위시리스트 API") +public interface WishProductApi { + + @Operation(summary = "회원의 위시 리스트에 상품을 추가한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "위시 리스트 추가 성공", content = @Content(schema = @Schema(implementation = WishProductResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity addWishProduct(WishProductRequest wishProductRequest, Long memberId); + + @Operation(summary = "회원의 특정 위시 리스트를 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "특정 위시 리스트 조회 성공", content = @Content(schema = @Schema(implementation = WishProductResponse.class))), + @ApiResponse(responseCode = "400", description = "특정 위시 리스트 조회 실패(사유 : 다른 사람의 위시 리스트는 접근할 수 없습니다.)", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getWishProduct(Long memberId, Long id); + + @Operation(summary = "회원의 위시 리스트에 있는 상품을 페이지 단위로 조회한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "모든 위시 리스트 조회 성공", content = @Content(schema = @Schema(implementation = WishProductPageResponse.class))), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류", content = @Content(schema = @Schema(hidden = true))) + }) + ResponseEntity getWishProducts(Long memberId, Pageable pageable); + + @Operation(summary = "회원의 위시 리스트에서 상품을 삭제한다.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "위시 리스트 삭제 성공"), + @ApiResponse(responseCode = "401", description = "허용되지 않는 요청"), + @ApiResponse(responseCode = "404", description = "위시 리스트 삭제 실패(사유 : 존재하지 않는 ID 입니다.)"), + @ApiResponse(responseCode = "500", description = "내부 서버의 오류") + }) + ResponseEntity deleteWishProduct(Long id); +} diff --git a/src/main/java/gift/controller/auth/AuthController.java b/src/main/java/gift/controller/auth/AuthController.java index dfbea3515..5c9f9e5b7 100644 --- a/src/main/java/gift/controller/auth/AuthController.java +++ b/src/main/java/gift/controller/auth/AuthController.java @@ -1,12 +1,14 @@ package gift.controller.auth; +import gift.config.properties.KakaoProperties; +import gift.controller.api.AuthApi; import gift.dto.auth.AuthResponse; import gift.dto.auth.LoginRequest; import gift.dto.auth.RegisterRequest; import gift.service.auth.AuthService; -import io.swagger.v3.oas.annotations.Hidden; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -15,15 +17,18 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.net.URI; + @RestController @RequestMapping("/api/members") -@Tag(name = "MEMBER") -public class AuthController { +public class AuthController implements AuthApi { private final AuthService authService; + private final KakaoProperties kakaoProperties; - public AuthController(AuthService authService) { + public AuthController(AuthService authService, KakaoProperties kakaoProperties) { this.authService = authService; + this.kakaoProperties = kakaoProperties; } @PostMapping("/register") @@ -38,10 +43,22 @@ public ResponseEntity login(@Valid @RequestBody LoginRequest login return ResponseEntity.ok(auth); } - @Hidden - @GetMapping("/oauth/kakao") + @GetMapping("/login/kakao") + public ResponseEntity redirectKakaoLogin() { + var headers = getRedirectHeader(kakaoProperties.redirectUri()); + return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY); + } + + @GetMapping("/login/kakao/callback") public ResponseEntity loginWithKakaoAuth(@RequestParam String code) { var auth = authService.loginWithKakaoAuth(code); return ResponseEntity.ok(auth); } + + private HttpHeaders getRedirectHeader(String redirectUri) { + var headers = new HttpHeaders(); + String redirectLocation = kakaoProperties.oauthBaseUri() + "&client_id=" + kakaoProperties.restApiKey() + "&redirect_uri=" + redirectUri; + headers.setLocation(URI.create(redirectLocation)); + return headers; + } } diff --git a/src/main/java/gift/controller/auth/AuthInterceptor.java b/src/main/java/gift/controller/auth/AuthInterceptor.java index afda6021c..7d124776e 100644 --- a/src/main/java/gift/controller/auth/AuthInterceptor.java +++ b/src/main/java/gift/controller/auth/AuthInterceptor.java @@ -2,7 +2,6 @@ import gift.config.properties.JwtProperties; import gift.exception.UnauthorizedAccessException; -import gift.model.MemberRole; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; @@ -41,9 +40,7 @@ private Claims getClaimsWithToken(String token) { private void setMemberInformationWithClaims(HttpServletRequest request, Claims claims) { var memberId = Long.parseLong(claims.getSubject()); - var memberRole = MemberRole.valueOf(claims.get("role").toString()); request.setAttribute("memberId", memberId); - request.setAttribute("memberRole", memberRole); } private String getTokenWithAuthorizationHeader(String authorizationHeader) { @@ -54,7 +51,7 @@ private String getTokenWithAuthorizationHeader(String authorizationHeader) { private String getHeader(HttpServletRequest request) { var header = request.getHeader("Authorization"); - if (header == null) throw new UnauthorizedAccessException("인가되지 않은 요청입니다."); + if (header == null) throw new UnauthorizedAccessException("허용되지 않는 요청입니다."); return header; } diff --git a/src/main/java/gift/dto/auth/AuthResponse.java b/src/main/java/gift/dto/auth/AuthResponse.java index 10dac2a0e..d06dfbaa7 100644 --- a/src/main/java/gift/dto/auth/AuthResponse.java +++ b/src/main/java/gift/dto/auth/AuthResponse.java @@ -1,6 +1,8 @@ package gift.dto.auth; -public record AuthResponse(String token) { +public record AuthResponse( + String token +) { public static AuthResponse of(String token) { return new AuthResponse(token); } diff --git a/src/main/java/gift/dto/auth/RegisterRequest.java b/src/main/java/gift/dto/auth/RegisterRequest.java index 75b0451ad..519ee25cd 100644 --- a/src/main/java/gift/dto/auth/RegisterRequest.java +++ b/src/main/java/gift/dto/auth/RegisterRequest.java @@ -1,18 +1,11 @@ package gift.dto.auth; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; -import org.hibernate.validator.constraints.Length; public record RegisterRequest( - @Length(max = 8, message = "이름의 길이는 8자를 초과할 수 없습니다.") - @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") - String name, @Pattern(regexp = "^[0-9a-z\\-\\_\\+\\w]*@([0-9a-z]+\\.)+[a-z]{2,9}", message = "허용되지 않은 형식의 이메일입니다.") String email, @Pattern(regexp = "^[0-9a-zA-Z\\-\\_\\+\\!\\*\\@\\#\\$\\%\\^\\&\\(\\)\\.]{8,}$", message = "허용되지 않은 형식의 패스워드입니다.") - String password, - @Pattern(regexp = "^(MEMBER|ADMIN)$", message = "존재하지 않는 회원 타입입니다.") - String role + String password ) { } diff --git a/src/main/java/gift/dto/category/CategoryInformation.java b/src/main/java/gift/dto/category/CategoryInformation.java deleted file mode 100644 index 11c6e1c74..000000000 --- a/src/main/java/gift/dto/category/CategoryInformation.java +++ /dev/null @@ -1,7 +0,0 @@ -package gift.dto.category; - -public record CategoryInformation(Long id, String name) { - public static CategoryInformation of(Long id, String name) { - return new CategoryInformation(id, name); - } -} diff --git a/src/main/java/gift/dto/category/CategoryResponse.java b/src/main/java/gift/dto/category/CategoryResponse.java index 370a9a4ae..2ab654bf9 100644 --- a/src/main/java/gift/dto/category/CategoryResponse.java +++ b/src/main/java/gift/dto/category/CategoryResponse.java @@ -1,6 +1,12 @@ package gift.dto.category; -public record CategoryResponse(Long id, String name, String description, String color, String imageUrl) { +public record CategoryResponse( + Long id, + String name, + String description, + String color, + String imageUrl +) { public static CategoryResponse of(Long id, String name, String description, String color, String imageUrl) { return new CategoryResponse(id, name, description, color, imageUrl); } diff --git a/src/main/java/gift/dto/giftorder/GiftOrderPageResponse.java b/src/main/java/gift/dto/giftorder/GiftOrderPageResponse.java new file mode 100644 index 000000000..8de266f82 --- /dev/null +++ b/src/main/java/gift/dto/giftorder/GiftOrderPageResponse.java @@ -0,0 +1,6 @@ +package gift.dto.giftorder; + +import java.util.List; + +public record GiftOrderPageResponse(Integer page, Integer size, Long totalElements, Integer totalPages, List content) { +} diff --git a/src/main/java/gift/dto/giftorder/GiftOrderResponse.java b/src/main/java/gift/dto/giftorder/GiftOrderResponse.java index 5bda70e3c..7e21cc0cf 100644 --- a/src/main/java/gift/dto/giftorder/GiftOrderResponse.java +++ b/src/main/java/gift/dto/giftorder/GiftOrderResponse.java @@ -1,11 +1,22 @@ package gift.dto.giftorder; -import gift.dto.option.OptionInformation; +import com.fasterxml.jackson.annotation.JsonProperty; +import gift.dto.option.OptionResponse; +import gift.dto.product.ProductBasicInformation; import java.time.LocalDateTime; -public record GiftOrderResponse(Long id, OptionInformation optionInformation, Integer quantity, LocalDateTime orderDateTime, String message) { - public static GiftOrderResponse of(Long id, OptionInformation optionInformation, Integer quantity, LocalDateTime orderDateTime, String message) { - return new GiftOrderResponse(id, optionInformation, quantity, orderDateTime, message); +public record GiftOrderResponse( + Long id, + @JsonProperty("product") + ProductBasicInformation productBasicInformation, + @JsonProperty("option") + OptionResponse optionResponse, + Integer quantity, + LocalDateTime orderDateTime, + String message +) { + public static GiftOrderResponse of(Long id, ProductBasicInformation productBasicInformation, OptionResponse optionResponse, Integer quantity, LocalDateTime orderDateTime, String message) { + return new GiftOrderResponse(id, productBasicInformation, optionResponse, quantity, orderDateTime, message); } } diff --git a/src/main/java/gift/dto/kakao/KakaoAuthInformation.java b/src/main/java/gift/dto/kakao/KakaoAuthInformation.java index 4c408e5e7..5bde93a88 100644 --- a/src/main/java/gift/dto/kakao/KakaoAuthInformation.java +++ b/src/main/java/gift/dto/kakao/KakaoAuthInformation.java @@ -1,6 +1,9 @@ package gift.dto.kakao; -public record KakaoAuthInformation(String name, String email) { +public record KakaoAuthInformation( + String name, + String email +) { public static KakaoAuthInformation of(String name, String email) { return new KakaoAuthInformation(name, email); } diff --git a/src/main/java/gift/dto/option/OptionAddRequest.java b/src/main/java/gift/dto/option/OptionAddRequest.java deleted file mode 100644 index bb0e3ea62..000000000 --- a/src/main/java/gift/dto/option/OptionAddRequest.java +++ /dev/null @@ -1,21 +0,0 @@ -package gift.dto.option; - -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import org.hibernate.validator.constraints.Length; - -public record OptionAddRequest( - @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") - @Length(max = 50, message = "이름의 길이는 50자를 초과할 수 없습니다.") - @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") - String name, - @Min(value = 1, message = "수량은 최소 1개 이상, 1억개 미만입니다.") - @Max(value = 100_000_000, message = "수량은 최소 1개 이상, 1억개 미만입니다.") - Integer quantity, - @NotNull(message = "상품은 반드시 선택되어야 합니다.") - Long productId -) { -} diff --git a/src/main/java/gift/dto/option/OptionInformation.java b/src/main/java/gift/dto/option/OptionInformation.java deleted file mode 100644 index 6d039dfcb..000000000 --- a/src/main/java/gift/dto/option/OptionInformation.java +++ /dev/null @@ -1,7 +0,0 @@ -package gift.dto.option; - -public record OptionInformation(Long id, String productName, Integer price, String name) { - public static OptionInformation of(Long id, String productName, Integer price, String name) { - return new OptionInformation(id, productName, price, name); - } -} diff --git a/src/main/java/gift/dto/option/OptionUpdateRequest.java b/src/main/java/gift/dto/option/OptionRequest.java similarity index 95% rename from src/main/java/gift/dto/option/OptionUpdateRequest.java rename to src/main/java/gift/dto/option/OptionRequest.java index d309e1130..9c7f0f982 100644 --- a/src/main/java/gift/dto/option/OptionUpdateRequest.java +++ b/src/main/java/gift/dto/option/OptionRequest.java @@ -6,7 +6,7 @@ import jakarta.validation.constraints.Pattern; import org.hibernate.validator.constraints.Length; -public record OptionUpdateRequest( +public record OptionRequest( @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") @Length(max = 50, message = "이름의 길이는 50자를 초과할 수 없습니다.") @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") diff --git a/src/main/java/gift/dto/option/OptionResponse.java b/src/main/java/gift/dto/option/OptionResponse.java index 91b41ced6..6d76c4fbb 100644 --- a/src/main/java/gift/dto/option/OptionResponse.java +++ b/src/main/java/gift/dto/option/OptionResponse.java @@ -1,6 +1,10 @@ package gift.dto.option; -public record OptionResponse(Long id, String name, Integer quantity) { +public record OptionResponse( + Long id, + String name, + Integer quantity +) { public static OptionResponse of(Long id, String name, Integer quantity) { return new OptionResponse(id, name, quantity); } diff --git a/src/main/java/gift/dto/product/ProductAddRequest.java b/src/main/java/gift/dto/product/ProductAddRequest.java new file mode 100644 index 000000000..317eab665 --- /dev/null +++ b/src/main/java/gift/dto/product/ProductAddRequest.java @@ -0,0 +1,27 @@ +package gift.dto.product; + +import gift.dto.option.OptionRequest; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.PositiveOrZero; +import org.hibernate.validator.constraints.Length; + +import java.util.List; + +public record ProductAddRequest( + @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") + @Length(max = 15, message = "이름의 길이는 15자를 초과할 수 없습니다.") + @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") + String name, + @PositiveOrZero(message = "금액은 0보다 크거나 같아야 합니다.") + Integer price, + @NotBlank(message = "상품 이미지는 필수로 입력해야 합니다.") + String imageUrl, + @NotNull(message = "상품 카테고리는 반드시 선택되어야 합니다.") + Long categoryId, + @NotEmpty(message = "상품의 옵션은 반드시 1개 이상 존재해야 합니다.") + List options +) { +} diff --git a/src/main/java/gift/dto/product/ProductBasicInformation.java b/src/main/java/gift/dto/product/ProductBasicInformation.java index 3d8a0ae57..71990461b 100644 --- a/src/main/java/gift/dto/product/ProductBasicInformation.java +++ b/src/main/java/gift/dto/product/ProductBasicInformation.java @@ -1,6 +1,10 @@ package gift.dto.product; -public record ProductBasicInformation(Long id, String name, Integer price) { +public record ProductBasicInformation( + Long id, + String name, + Integer price +) { public static ProductBasicInformation of(Long id, String name, Integer price) { return new ProductBasicInformation(id, name, price); } diff --git a/src/main/java/gift/dto/product/ProductPageResponse.java b/src/main/java/gift/dto/product/ProductPageResponse.java new file mode 100644 index 000000000..1b99a3e98 --- /dev/null +++ b/src/main/java/gift/dto/product/ProductPageResponse.java @@ -0,0 +1,6 @@ +package gift.dto.product; + +import java.util.List; + +public record ProductPageResponse(Integer page, Integer size, Long totalElements, Integer totalPages, List content) { +} diff --git a/src/main/java/gift/dto/product/ProductResponse.java b/src/main/java/gift/dto/product/ProductResponse.java index 806988f31..a48cea91f 100644 --- a/src/main/java/gift/dto/product/ProductResponse.java +++ b/src/main/java/gift/dto/product/ProductResponse.java @@ -1,9 +1,17 @@ package gift.dto.product; -import gift.dto.category.CategoryInformation; +import com.fasterxml.jackson.annotation.JsonProperty; +import gift.dto.category.CategoryResponse; -public record ProductResponse(Long id, String name, Integer price, String imageUrl, CategoryInformation categoryInformation) { - public static ProductResponse of(Long id, String name, Integer price, String imageUrl, CategoryInformation categoryInformation) { - return new ProductResponse(id, name, price, imageUrl, categoryInformation); +public record ProductResponse( + Long id, + String name, + Integer price, + String imageUrl, + @JsonProperty("category") + CategoryResponse categoryResponse +) { + public static ProductResponse of(Long id, String name, Integer price, String imageUrl, CategoryResponse categoryResponse) { + return new ProductResponse(id, name, price, imageUrl, categoryResponse); } } diff --git a/src/main/java/gift/dto/product/ProductRequest.java b/src/main/java/gift/dto/product/ProductUpdateRequest.java similarity index 96% rename from src/main/java/gift/dto/product/ProductRequest.java rename to src/main/java/gift/dto/product/ProductUpdateRequest.java index 70182125c..19bafe1a5 100644 --- a/src/main/java/gift/dto/product/ProductRequest.java +++ b/src/main/java/gift/dto/product/ProductUpdateRequest.java @@ -6,7 +6,7 @@ import jakarta.validation.constraints.PositiveOrZero; import org.hibernate.validator.constraints.Length; -public record ProductRequest( +public record ProductUpdateRequest( @Pattern(regexp = "^[\s\\-\\&\\(\\)\\[\\]\\+\\/\\_a-zA-z0-9ㄱ-ㅎ가-힣]*$", message = "허용되지 않은 형식의 이름입니다.") @Length(max = 15, message = "이름의 길이는 15자를 초과할 수 없습니다.") @NotBlank(message = "이름의 길이는 최소 1자 이상이어야 합니다.") diff --git a/src/main/java/gift/dto/wishproduct/WishProductAddRequest.java b/src/main/java/gift/dto/wishproduct/WishProductAddRequest.java deleted file mode 100644 index d994bf9af..000000000 --- a/src/main/java/gift/dto/wishproduct/WishProductAddRequest.java +++ /dev/null @@ -1,12 +0,0 @@ -package gift.dto.wishproduct; - -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Positive; - -public record WishProductAddRequest( - @NotNull(message = "상품은 반드시 선택되어야 합니다.") - Long productId, - @Positive(message = "상품의 수량은 반드시 1개 이상이어야 합니다.") - Integer quantity -) { -} diff --git a/src/main/java/gift/dto/wishproduct/WishProductPageResponse.java b/src/main/java/gift/dto/wishproduct/WishProductPageResponse.java new file mode 100644 index 000000000..545c3fa99 --- /dev/null +++ b/src/main/java/gift/dto/wishproduct/WishProductPageResponse.java @@ -0,0 +1,6 @@ +package gift.dto.wishproduct; + +import java.util.List; + +public record WishProductPageResponse(Integer page, Integer size, Long totalElements, Integer totalPages, List content) { +} diff --git a/src/main/java/gift/dto/wishproduct/WishProductRequest.java b/src/main/java/gift/dto/wishproduct/WishProductRequest.java new file mode 100644 index 000000000..079d79757 --- /dev/null +++ b/src/main/java/gift/dto/wishproduct/WishProductRequest.java @@ -0,0 +1,9 @@ +package gift.dto.wishproduct; + +import jakarta.validation.constraints.NotNull; + +public record WishProductRequest( + @NotNull(message = "상품은 반드시 선택되어야 합니다.") + Long productId +) { +} diff --git a/src/main/java/gift/dto/wishproduct/WishProductResponse.java b/src/main/java/gift/dto/wishproduct/WishProductResponse.java index 2eb609312..90b7a97de 100644 --- a/src/main/java/gift/dto/wishproduct/WishProductResponse.java +++ b/src/main/java/gift/dto/wishproduct/WishProductResponse.java @@ -1,9 +1,14 @@ package gift.dto.wishproduct; +import com.fasterxml.jackson.annotation.JsonProperty; import gift.dto.product.ProductBasicInformation; -public record WishProductResponse(Long id, ProductBasicInformation productBasicInformation, Integer quantity) { - public static WishProductResponse of(Long id, ProductBasicInformation productBasicInformation, Integer quantity) { - return new WishProductResponse(id, productBasicInformation, quantity); +public record WishProductResponse( + Long id, + @JsonProperty("product") + ProductBasicInformation productBasicInformation +) { + public static WishProductResponse of(Long id, ProductBasicInformation productBasicInformation) { + return new WishProductResponse(id, productBasicInformation); } } diff --git a/src/main/java/gift/dto/wishproduct/WishProductUpdateRequest.java b/src/main/java/gift/dto/wishproduct/WishProductUpdateRequest.java deleted file mode 100644 index 845b40628..000000000 --- a/src/main/java/gift/dto/wishproduct/WishProductUpdateRequest.java +++ /dev/null @@ -1,9 +0,0 @@ -package gift.dto.wishproduct; - -import jakarta.validation.constraints.PositiveOrZero; - -public record WishProductUpdateRequest( - @PositiveOrZero(message = "상품의 수량은 반드시 0개 이상이어야 합니다.") - Integer quantity -) { -} diff --git a/src/main/java/gift/exception/AlreadyExistsException.java b/src/main/java/gift/exception/AlreadyExistsException.java new file mode 100644 index 000000000..985c3b517 --- /dev/null +++ b/src/main/java/gift/exception/AlreadyExistsException.java @@ -0,0 +1,7 @@ +package gift.exception; + +public class AlreadyExistsException extends RuntimeException { + public AlreadyExistsException(String message) { + super(message); + } +} diff --git a/src/main/java/gift/exception/ExceptionResponse.java b/src/main/java/gift/exception/ExceptionResponse.java new file mode 100644 index 000000000..731dbeffe --- /dev/null +++ b/src/main/java/gift/exception/ExceptionResponse.java @@ -0,0 +1,4 @@ +package gift.exception; + +public record ExceptionResponse(Integer status, String message) { +} diff --git a/src/main/java/gift/exception/GlobalExceptionHandler.java b/src/main/java/gift/exception/GlobalExceptionHandler.java index aee7e7143..d6465e0c9 100644 --- a/src/main/java/gift/exception/GlobalExceptionHandler.java +++ b/src/main/java/gift/exception/GlobalExceptionHandler.java @@ -1,9 +1,7 @@ package gift.exception; import io.jsonwebtoken.ExpiredJwtException; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.mapping.PropertyReferenceException; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindingResult; @@ -12,8 +10,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; -import java.net.URI; - @RestControllerAdvice public class GlobalExceptionHandler { @@ -23,61 +19,55 @@ public class GlobalExceptionHandler { private static final String DUPLICATED_NAME_MESSAGE = "이미 존재하는 이름입니다."; private static final String INVALID_LOGIN_INFO_MESSAGE = "로그인 정보가 유효하지 않습니다."; private static final String INVALID_PAGE_REQUEST_MESSAGE = "요청에 담긴 페이지 정보가 유효하지 않습니다."; - private static final String UNAUTHORIZED_ACCESS_MESSAGE = "인가되지 않은 요청입니다."; private static final String EXPIRED_JWT_MESSAGE = "인증 정보가 만료되었습니다."; - @Value("${kakao.redirect-token-uri}") - private String redirectTokenUri; @ExceptionHandler(value = NotFoundElementException.class) - public ResponseEntity notFoundElementExceptionHandling() { - return new ResponseEntity<>(NOT_FOUND_MESSAGE, HttpStatus.NOT_FOUND); + public ResponseEntity notFoundElementExceptionHandling() { + return getExceptionResponse(NOT_FOUND_MESSAGE, HttpStatus.NOT_FOUND); } @ExceptionHandler(value = InvalidProductNameWithKAKAOException.class) - public ResponseEntity invalidProductNameWithKAKAOExceptionHandling() { - return new ResponseEntity<>(INVALID_PRODUCT_NAME_WITH_KAKAO_MESSAGE, HttpStatus.BAD_REQUEST); + public ResponseEntity invalidProductNameWithKAKAOExceptionHandling() { + return getExceptionResponse(INVALID_PRODUCT_NAME_WITH_KAKAO_MESSAGE, HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = DuplicatedEmailException.class) - public ResponseEntity duplicatedEmailExceptionHandling() { - return new ResponseEntity<>(DUPLICATED_EMAIL_MESSAGE, HttpStatus.CONFLICT); + public ResponseEntity duplicatedEmailExceptionHandling() { + return getExceptionResponse(DUPLICATED_EMAIL_MESSAGE, HttpStatus.CONFLICT); } @ExceptionHandler(value = DuplicatedNameException.class) - public ResponseEntity duplicatedNameExceptionHandling() { - return new ResponseEntity<>(DUPLICATED_NAME_MESSAGE, HttpStatus.CONFLICT); + public ResponseEntity duplicatedNameExceptionHandling() { + return getExceptionResponse(DUPLICATED_NAME_MESSAGE, HttpStatus.CONFLICT); } @ExceptionHandler(value = InvalidLoginInfoException.class) - public ResponseEntity invalidLoginInfoExceptionHandling() { - return new ResponseEntity<>(INVALID_LOGIN_INFO_MESSAGE, HttpStatus.UNAUTHORIZED); + public ResponseEntity invalidLoginInfoExceptionHandling() { + return getExceptionResponse(INVALID_LOGIN_INFO_MESSAGE, HttpStatus.UNAUTHORIZED); } @ExceptionHandler(value = UnauthorizedAccessException.class) - public ResponseEntity unauthorizedAccessExceptionHandling() { - return new ResponseEntity<>(UNAUTHORIZED_ACCESS_MESSAGE, HttpStatus.UNAUTHORIZED); + public ResponseEntity unauthorizedAccessExceptionHandling(UnauthorizedAccessException exception) { + return getExceptionResponse(exception.getMessage(), HttpStatus.UNAUTHORIZED); } - @ExceptionHandler(value = ExpiredJwtException.class) - public ResponseEntity expiredJwtExceptionHandling() { - return new ResponseEntity<>(EXPIRED_JWT_MESSAGE, HttpStatus.UNAUTHORIZED); + @ExceptionHandler(value = AlreadyExistsException.class) + public ResponseEntity alreadyExistsExceptionHandling(AlreadyExistsException exception) { + return getExceptionResponse(exception.getMessage(), HttpStatus.CONFLICT); } - @ExceptionHandler(value = InvalidKakaoTokenException.class) - public ResponseEntity invalidKakaoTokenExceptionHandling() { - var headers = new HttpHeaders(); - String redirectLocation = redirectTokenUri; - headers.setLocation(URI.create(redirectLocation)); - return new ResponseEntity<>(headers, HttpStatus.MOVED_PERMANENTLY); + @ExceptionHandler(value = ExpiredJwtException.class) + public ResponseEntity expiredJwtExceptionHandling() { + return getExceptionResponse(EXPIRED_JWT_MESSAGE, HttpStatus.UNAUTHORIZED); } @ExceptionHandler(value = BadRequestException.class) - public ResponseEntity badRequestExceptionHandling(BadRequestException exception) { - return new ResponseEntity<>(exception.getMessage(), HttpStatus.BAD_REQUEST); + public ResponseEntity badRequestExceptionHandling(BadRequestException exception) { + return getExceptionResponse(exception.getMessage(), HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = MethodArgumentNotValidException.class) - public ResponseEntity methodArgumentNotValidExceptionHandling(MethodArgumentNotValidException exception) { + public ResponseEntity methodArgumentNotValidExceptionHandling(MethodArgumentNotValidException exception) { BindingResult bindingResult = exception.getBindingResult(); StringBuilder builder = new StringBuilder(); @@ -85,16 +75,21 @@ public ResponseEntity methodArgumentNotValidExceptionHandling(MethodArgu builder.append(fieldError.getDefaultMessage()); } - return ResponseEntity.badRequest().body(builder.toString()); + return getExceptionResponse(builder.toString(), HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = PropertyReferenceException.class) - public ResponseEntity propertyReferenceExceptionHandling() { - return new ResponseEntity<>(INVALID_PAGE_REQUEST_MESSAGE, HttpStatus.BAD_REQUEST); + public ResponseEntity propertyReferenceExceptionHandling() { + return getExceptionResponse(INVALID_PAGE_REQUEST_MESSAGE, HttpStatus.BAD_REQUEST); } @ExceptionHandler(value = Exception.class) - public ResponseEntity internalServerExceptionHandling(Exception exception) { - return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + public ResponseEntity internalServerExceptionHandling(Exception exception) { + return getExceptionResponse(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + + private ResponseEntity getExceptionResponse(String message, HttpStatus status) { + var response = new ExceptionResponse(status.value(), message); + return ResponseEntity.status(status).body(response); } } diff --git a/src/main/java/gift/exception/InvalidKakaoTokenException.java b/src/main/java/gift/exception/InvalidKakaoTokenException.java deleted file mode 100644 index 67511aba9..000000000 --- a/src/main/java/gift/exception/InvalidKakaoTokenException.java +++ /dev/null @@ -1,7 +0,0 @@ -package gift.exception; - -public class InvalidKakaoTokenException extends RuntimeException { - public InvalidKakaoTokenException(String message) { - super(message); - } -} diff --git a/src/main/java/gift/model/Member.java b/src/main/java/gift/model/Member.java index a3d0dbf7d..3a168ea81 100644 --- a/src/main/java/gift/model/Member.java +++ b/src/main/java/gift/model/Member.java @@ -3,8 +3,6 @@ import gift.exception.InvalidLoginInfoException; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import org.hibernate.annotations.SQLDelete; @@ -15,9 +13,6 @@ @SQLDelete(sql = "update member set deleted = true where id = ?") @SQLRestriction("deleted is false") public class Member extends BaseEntity { - @NotNull - @Column(name = "name") - private String name; @NotNull @Column(name = "email", unique = true) private String email; @@ -25,42 +20,26 @@ public class Member extends BaseEntity { @Column(name = "password") private String password; @NotNull - @Enumerated(value = EnumType.STRING) - @Column(name = "role") - private MemberRole role; - @NotNull @Column(name = "deleted") private Boolean deleted = Boolean.FALSE; protected Member() { } - public Member(String name, String email, MemberRole role, OauthType oauthType) { - this.name = name; + public Member(String email, OauthType oauthType) { this.email = email; this.password = oauthType.name(); - this.role = role; } - public Member(String name, String email, String password, MemberRole role) { - this.name = name; + public Member(String email, String password) { this.email = email; this.password = password; - this.role = role; - } - - public String getName() { - return name; } public String getPassword() { return password; } - public MemberRole getRole() { - return role; - } - public void passwordCheck(String inputPassword) { if (!password.equals(inputPassword)) { throw new InvalidLoginInfoException("로그인 정보가 유효하지 않습니다."); diff --git a/src/main/java/gift/model/MemberRole.java b/src/main/java/gift/model/MemberRole.java deleted file mode 100644 index 5616538c5..000000000 --- a/src/main/java/gift/model/MemberRole.java +++ /dev/null @@ -1,6 +0,0 @@ -package gift.model; - -public enum MemberRole { - MEMBER, - ADMIN -} diff --git a/src/main/java/gift/model/WishProduct.java b/src/main/java/gift/model/WishProduct.java index 89a7205d3..208f57b26 100644 --- a/src/main/java/gift/model/WishProduct.java +++ b/src/main/java/gift/model/WishProduct.java @@ -24,19 +24,15 @@ public class WishProduct extends BaseEntity { @JoinColumn(name = "member_id", referencedColumnName = "id") private Member member; @NotNull - @Column(name = "quantity") - private Integer quantity; - @NotNull @Column(name = "deleted") private Boolean deleted = Boolean.FALSE; protected WishProduct() { } - public WishProduct(Product product, Member member, Integer quantity) { + public WishProduct(Product product, Member member) { this.product = product; this.member = member; - this.quantity = quantity; } public Product getProduct() { @@ -46,12 +42,4 @@ public Product getProduct() { public Member getMember() { return member; } - - public Integer getQuantity() { - return quantity; - } - - public void updateQuantity(Integer updateQuantity) { - this.quantity = updateQuantity; - } } diff --git a/src/main/java/gift/repository/GiftOrderRepository.java b/src/main/java/gift/repository/GiftOrderRepository.java index b08789e2f..28ac64d50 100644 --- a/src/main/java/gift/repository/GiftOrderRepository.java +++ b/src/main/java/gift/repository/GiftOrderRepository.java @@ -1,18 +1,17 @@ package gift.repository; import gift.model.GiftOrder; +import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import java.util.List; - @Repository public interface GiftOrderRepository extends JpaRepository { + Page findAllByMemberId(Long memberId, Pageable pageable); + void deleteAllByOptionId(Long optionId); void deleteAllByMemberId(Long memberId); - - List findAllByMemberId(Long memberId, Pageable pageable); } diff --git a/src/main/java/gift/repository/OauthTokenRepository.java b/src/main/java/gift/repository/OauthTokenRepository.java index 361059be4..ac43e5c3c 100644 --- a/src/main/java/gift/repository/OauthTokenRepository.java +++ b/src/main/java/gift/repository/OauthTokenRepository.java @@ -1,6 +1,7 @@ package gift.repository; import gift.model.OauthToken; +import gift.model.OauthType; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -9,9 +10,9 @@ @Repository public interface OauthTokenRepository extends JpaRepository { - boolean existsByMemberId(Long memberId); + boolean existsByMemberIdAndOauthType(Long memberId, OauthType oauthType); - Optional findByMemberId(Long memberId); + Optional findByMemberIdAndOauthType(Long memberId, OauthType oauthType); - void deleteByMemberId(Long memberId); + void deleteAllByMemberId(Long memberId); } diff --git a/src/main/java/gift/repository/OptionRepository.java b/src/main/java/gift/repository/OptionRepository.java index aa8ee4394..089597def 100644 --- a/src/main/java/gift/repository/OptionRepository.java +++ b/src/main/java/gift/repository/OptionRepository.java @@ -2,7 +2,6 @@ import gift.model.Option; import jakarta.persistence.LockModeType; -import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Query; @@ -13,7 +12,7 @@ @Repository public interface OptionRepository extends JpaRepository { - List